Northwind 推荐引擎
最近,我被要求提出一种提供推荐的方法。幸运的是,凭借在最近的 Neo4j 伦敦聚会上从 Max De Marzi 和 Mark Needham 的演讲中获得的知识,我知道这可以用 Neo4j 轻松实现。
推荐引擎的关键问题来自数据。幸运的是,Neo4j 附带了 Northwind 图示例。Northwind 数据库是一个臭名昭著的数据集,包含多年来用于教授关系数据库的购买历史记录,并且是一个很好的起点。
您可以按照 Neo4j 上的 “将数据导入 Neo4j” 文章将 Northwind 数据库导入图中,或者在 Neo4j 的浏览器中输入以下内容,例如 Neo4j 桌面版中的空数据库或 空白沙箱。
:play northwind graph
以下是如何手动加载数据
现在我们有一些数据了,让我们开始探索数据集。
热门产品
要查找数据集中最热门的产品,我们可以从 :Customer
到 :Product
沿着路径查找。
match (c:Customer)-[:PURCHASED]->(o:Order)-[:PRODUCT]->(p:Product)
return c.companyName, p.productName, count(o) as orders
order by orders desc
limit 5
基于内容的推荐
我们可以为客户提供的最简单的推荐是基于内容的推荐。根据他们之前的购买记录,我们能否推荐一些他们还没有购买过的商品?对于我们的客户购买的每种产品,让我们看看其他客户也购买了哪些产品。每个 :Product
都与一个 :Category
相关联,因此我们可以使用它进一步缩小要推荐的产品列表。
match (c:Customer)-[:PURCHASED]->(o:Order)-[:PRODUCT]->(p:Product)
<-[:PRODUCT]-(o2:Order)-[:PRODUCT]->(p2:Product)-[:PART_OF]->(:Category)<-[:PART_OF]-(p)
WHERE c.customerID = 'ANTON' and NOT( (c)-[:PURCHASED]->(:Order)-[:PRODUCT]->(p2) )
return c.companyName, p.productName as has_purchased, p2.productName as has_also_purchased, count(DISTINCT o2) as occurrences
order by occurrences desc
limit 5
到目前为止,这很标准。
协同过滤
协同过滤是一种由推荐引擎使用的方法,它根据其他客户的反馈来推荐内容。为此,我们可以使用 k-NN (k-最近邻) 算法。k-N 通过根据项目彼此之间的相似性将项目分组到分类中来工作。在我们的例子中,这可能是两个客户对产品的评分。举个现实世界的例子,Netflix 就是根据你已经观看的节目的评分来推荐节目的。
使此模型工作的第一件事是创建一些“评分关系”。现在,让我们根据客户购买产品的次数为每种产品创建一个介于 0 和 1 之间的分数。
MATCH (c:Customer)-[:PURCHASED]->(o:Order)-[:PRODUCT]->(p:Product)
WITH c, count(p) as total
MATCH (c)-[:PURCHASED]->(o:Order)-[:PRODUCT]->(p:Product)
WITH c, total,p, count(o)*1.0 as orders
MERGE (c)-[rated:RATED]->(p)
ON CREATE SET rated.rating = orders/total
ON MATCH SET rated.rating = orders/total
WITH c.companyName as company, p.productName as product, orders, total, rated.rating as rating
ORDER BY rating DESC
RETURN company, product, orders, total, rating LIMIT 10
现在我们的模型应该如下所示
MATCH (me:Customer)-[r:RATED]->(p:Product)
WHERE me.customerID = 'ANTON'
RETURN p.productName, r.rating limit 10
现在我们可以使用这些评分来比较两个客户的偏好。
// See Customer's Similar Ratings to Others
MATCH (c1:Customer {customerID:'ANTON'})-[r1:RATED]->(p:Product)<-[r2:RATED]-(c2:Customer)
RETURN c1.customerID, c2.customerID, p.productName, r1.rating, r2.rating,
CASE WHEN r1.rating-r2.rating < 0 THEN -(r1.rating-r2.rating) ELSE r1.rating-r2.rating END as difference
ORDER BY difference ASC
LIMIT 15
现在,我们可以使用余弦相似度计算两个客户之间的相似度分数(感谢 Nicole White 提供了最初的 Cypher 查询……)
MATCH (c1:Customer)-[r1:RATED]->(p:Product)<-[r2:RATED]-(c2:Customer)
WITH
SUM(r1.rating*r2.rating) as dot_product,
SQRT( REDUCE(x=0.0, a IN COLLECT(r1.rating) | x + a^2) ) as r1_length,
SQRT( REDUCE(y=0.0, b IN COLLECT(r2.rating) | y + b^2) ) as r2_length,
c1,c2
MERGE (c1)-[s:SIMILARITY]-(c2)
SET s.similarity = dot_product / (r1_length * r2_length)
MATCH (me:Customer)-[r:SIMILARITY]->(them)
WHERE me.customerID='ANTON'
RETURN me.companyName, them.companyName, r.similarity
ORDER BY r.similarity DESC limit 10
很好,现在让我们根据这些相似度分数做出推荐。
WITH 1 as neighbours
MATCH (me:Customer)-[:SIMILARITY]->(c:Customer)-[r:RATED]->(p:Product)
WHERE me.customerID = 'ANTON' and NOT ( (me)-[:RATED|PRODUCT|ORDER*1..2]->(p:Product) )
WITH p, COLLECT(r.rating)[0..neighbours] as ratings, collect(c.companyName)[0..neighbours] as customers
WITH p, customers, REDUCE(s=0,i in ratings | s+i) / LENGTH(ratings) as recommendation
ORDER BY recommendation DESC
RETURN p.productName, customers, recommendation LIMIT 10
就是这样!使用 Neo4j 快速简单地进行推荐。
此页面是否有帮助?