【问题标题】:Collaborative filtering cypher with attributes in neo4jNeo4j中具有属性的协同过滤密码
【发布时间】:2017-03-14 14:52:32
【问题描述】:

我正在使用 neo4j 设置推荐系统。我有以下设置:

节点:

  • 用户
  • 电影
  • 电影属性(例如流派)

关系

  • (m:Movie)-[w:WEIGHT {weight: 10}]->(a:Attribute)
  • (u:User)-[r:RATED {rating: 5}]->(m:Movie)

这是它的外观图:

我现在正试图弄清楚如何应用协同过滤方案,其工作原理如下:

  1. 检查user 喜欢的属性(隐含喜欢电影)
  2. 找到喜欢这些相似属性的相似other users
  3. 推荐顶级电影给user,用户没看过,但类似other users看过。

条件显然是每个属性对每部电影都有一定的权重。例如。流派adventure 的权重可以是指环王的10,但泰坦尼克号的权重是5

此外,系统需要考虑每部电影的收视率。例如。如果other user 已将指环王评为5,则他/她的范围之王属性将按5 而不是10 进行缩放。对隐含属性的评分也接近5user 应该会推荐这部电影,而不是另一个对相似属性评分更高的用户。

我只是简单地推荐其他用户评价过的其他电影,但我不知道如何考虑 RATING 和 WEIGHT 之间的关系。它也没有工作:

MATCH (user:User)-[:RATED]->(movie1)<-[:RATED]-(ouser:User),
         (ouser)-[:RATED]->(movie2)<-[:RATED]-(oouser:User)
WHERE user.uid = "user4"
AND   NOT    (user)-[:RATED]->(movie2)
RETURN oouser

【问题讨论】:

  • 您能否发布一些用例来阐明如何回答您的 3 个问题/业务逻辑?
  • 电影分级?你到底是什么意思?
  • 找到喜欢这些相似属性的其他相似用户:你的相似性标准是什么?如果用户喜欢具有“冒险”属性的电影,而其他用户喜欢具有相同属性的另一部电影,这些用户是否相似?这些相似性取决于属性的权重?
  • 没错!所以我们确实需要一个相似性标准,基于用户喜欢的电影的属性权重和用户对该属性的评分权重。例如。评级 * attr.权重 = 用户属性的得分
  • 但是根据这个标准,如果一个“用户 1”被评为 2 一个“电影 a”,属性冒险 = 10 (2x10=20),另一个“用户 2”被评为 10 个“电影 b" 属性冒险 = 2 (10x2=20) 这些用户被认为非常相似。有意义吗?

标签: neo4j cypher graph-databases collaborative-filtering


【解决方案1】:

从数学上讲,您正在寻找的是两个用户之间的简化 Jaccard index。也就是说,它们的相似程度取决于它们有多少共同点。我说简化是因为我们没有考虑他们不同意的电影。从本质上讲,按照您的命令,它将是:

1) 获取每个用户每个属性的总权重。例如:

MATCH (user:User{name:'user1'})
OPTIONAL MATCH (user)-[r:RATED]->(m:Movie)->[w:WEIGHT]->(a:Attribute)
WITH user, r.rating * w.weight AS totalWeight, a
WITH user, a, sum(totalWeight) AS totalWeight

我们需要最后一行,因为每个电影属性组合都有一行

2) 然后,我们得到具有相似品味的用户。这是一个性能危险区域,可能需要进行一些过滤。但是强制它,我们让用户在 10% 的误差范围内喜欢每个属性(例如)

WITH user, a, totalWeight*0.9 AS minimum, totalWeight*1.10 AS maximum
MATCH (a)<-[w:WEIGHT]-(m:Movie)<-[r:RATES]-(otherUser:User)
WITH user, a, otherUser
WHERE w.weight * r.rating > minimum AND w.weight * r.rating < maximum
WITH user, otherUser

所以现在我们有一行(由于最后一行是唯一的)与匹配的任何 otherUser。在这里,老实说,我需要尝试确定是否会包含只有 1 个类型匹配的其他用户。如果是,则需要额外的过滤器。但我认为这应该在我们开始之后进行。

3) 现在很容易:

MATCH (otherUser)-[r:RATES]->(m:Movie)
WHERE NOT (user)-[:RATES]->(m)
RETURN m, sum(r.rating) AS totalRating ORDER BY totalRating DESC

如前所述,棘手的部分是 2),但是在我们知道如何进行数学运算之后,它应该会更容易。哦,关于数学,为了让它正常工作,一部电影的总权重应该是 1 (normalizing)。在任何其他情况下,电影总权重之间的差异会导致不公平的比较。

我在没有经过适当研究(纸、铅笔、方程式、统计数据)并在示例数据集中尝试代码的情况下编写了此代码。无论如何,我希望它可以帮助你!

如果您在不考虑用户评分或属性权重的情况下想要此推荐,则将 1) 和 2) 中的数学分别替换为 r.rating 或 w.weight 就足够了。 RATES 和 WEIGHTS 关系仍将被使用,例如冒险电影的狂热消费者会被冒险电影的消费者推荐电影,但不会像我们选择的那样根据评级或属性权重进行修改。

编辑:编辑代码以修复 cmets 中讨论的语法错误。

【讨论】:

  • 非常感谢;到目前为止,这太棒了。我现在正在为 2) 中的最后一行而苦苦挣扎。 distinct 似乎不起作用,实际上它说语法不正确。未知函数'distinct'
  • 这是我的错误。显然 (github.com/neo4j/neo4j/issues/6545 ) 在某些情况下,如果应用它没有区别,那么 distinct 不起作用。我会尝试“WITH user, otherUser”,因为在 a 上进行聚合应该可以解决问题,而且无论如何我们不想在该行通过 Attribute 进行区分。如果存在重复的其他用户,请尝试“与用户,不同(其他用户)作为其他用户”,它应该可以工作。对于不确定性,我很抱歉,有时我仍然会在 Cypher 上遇到这种用例!
  • 关于最后一个错误,把最后一行改成 RETURN m, sum(r.rating) AS totalRating ORDER BY totalRating DESC 。当我们修复这些最后的语法错误时,我将编辑答案。
  • 我不确定我是否理解。我认为您在这里提出第二个问题并为其分配赏金不会有任何问题。如果您问我是否同意将我的答案用作其他人回答的起点,通过将其链接到那里,我绝对没有问题:-)
  • 我修改了答案(倒数第二段)以添加使用纯用户评分或属性权重的选项。在任何情况下,我们都会计算用户相似度以便能够推荐更多电影。我认为增加过滤选项的深度(例如,仅具有这些属性的电影,以任何顺序,或仅通过相似电影计算相似度)是一项应该在此处之外讨论的工作,因为范围可以增长很多并且业务逻辑应该澄清。有许多可能的预期结果。由于这是一个很小的补充,我认为不应该给予任何赏金:-)
【解决方案2】:

回答您的第一个问题:

检查用户喜欢的属性(隐含地喜欢电影)

MATCH (user:User)
OPTIONAL MATCH (user)-[r:RATED]->(m:movie)
OPTIONAL MATCH (m)-[r:RATED]->(a:Attribute)
WHERE user.uid = "user4"
RETURN user, collect ({ a:a.title })

这是一个子查询结构,您可以在其中找到用户评分的电影,然后找到电影的属性,最后返回喜欢的属性列表

如果需要整个节点,可以将return语句修改为collect (a) as attributes

【讨论】:

  • 嗯,你在哪里包括权重和评分?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多