Neo4j实时推荐性能



我正在努力了解neo4j在实时推荐系统中的性能。

下面是一个密码查询(取自他们的沙箱(,它计算与查询用户"最相似的前100个用户(余弦距离(;Cynthia Freeman":

MATCH 
(p1:User {name: "Cynthia Freeman"})-[x:RATED]->(m:Movie)<-[y:RATED]-(p2:User)
WITH 
COUNT(m) AS numberMovies, 
SUM(x.rating * y.rating) AS xyDotProduct,
SQRT(REDUCE(xDot = 0.0, a IN COLLECT(x.rating) | xDot + a^2)) AS xLength,
SQRT(REDUCE(yDot = 0.0, b IN COLLECT(y.rating) | yDot + b^2)) AS yLength,
p1, p2 
WHERE
numberMovies > 10
RETURN 
p1.name, p2.name, xyDotProduct / (xLength * yLength) AS sim
ORDER BY sim DESC
LIMIT 100;

如果我的理解是正确的,那么LIMIT子句后面没有魔术,因为距离计算仍然需要与所有其他用户进行,所以实时解决这个查询似乎有点困难,除非neo4j在幕后做一些事情。

在另一个例子中,他们预先计算用户节点之间的[:SIMILARITY]关系,并将其存储在图中,因此查询前N个最相似的用户就变成了节点的排序。这将直观地使图密集,因此与简单地使用相似性矩阵相比,没有存储优势。

我是否遗漏了图形数据库(尤其是neo4j(工作方式的一些基本内容?这如何扩展到实时应用程序,在那里可以有数以万计的用户,甚至可以与他们交互的更多产品?

如果您想在数万个或更多的节点上使用某种余弦距离度量进行实时推荐,最好将预先计算的值存储为关系。

至于使图密集,你可以将SIMILAR关系限制在前K个相似节点,还可以定义相似性截止阈值,这可以使你的图尽可能稀疏。你只能存储相关结果。因此,例如,在一个有10000个节点的图中,如果每个项目都与前10个其他节点有连接,那么这不是一个真正密集的图。如果还删除从一个节点指向另一个节点并返回的重复关系,则可以删除更多。因此,如果有10k*10k(如果你将这些关系视为无向关系,则除以2(的关系是可能的,那么你就不会有十亿种可能的关系,但最多只有10万种。

图形数据科学库支持两种计算余弦距离的算法:

第一个初始版本计算所有对之间的距离,并且可以用topKsimilarityCutoff参数进行调谐。

就在最近,kNN算法的优化实现被添加到GDS1.4预发布中。它使用了本文中描述的实现:https://dl.acm.org/doi/abs/10.1145/1963405.1963487

然而,对于10k多个节点之间的相似度的实时计算,可能仍需要超过100ms的时间才能使实时响应达到最大值,因此使用预先计算的相似度关系是有意义的。

除了@TomažBratanič的精彩建议外,您现有的查询还可以提高效率。它正在为每个p1/p2对执行数学计算,即使是后来因为共享电影的数量不超过10而被过滤掉的对。相反,在进行计算之前,您应该尝试过滤掉不需要的p1/p2

例如:

MATCH
(p1:User {name: "Cynthia Freeman"})-[x:RATED]->(m:Movie)<-[y:RATED]-(p2:User)
WITH
COLLECT({xr: x.rating, yr: y.rating}) AS data
p1, p2
WHERE
SIZE(data) > 10
WITH
REDUCE(s = 0, d IN data | s + d.xr * d.yr) AS xyDotProduct,
SQRT(REDUCE(xDot = 0.0, a IN data | xDot + a.xr^2)) AS xLength,
SQRT(REDUCE(yDot = 0.0, b IN data | yDot + b.yr^2)) AS yLength,
p1, p2
RETURN 
p1.name, p2.name, xyDotProduct / (xLength * yLength) AS sim
ORDER BY sim DESC
LIMIT 100;

最新更新