引言
推荐系统传统上以「用户-物品评分矩阵」为中心,用协同过滤、矩阵分解求解。但当交互数据变成关系图谱——用户、物品、标签、品牌、类目、内容互相连接——图数据库提供了更自然的建模与更丰富的推荐信号:「看了这本书的人也看了」本质上就是一条二跳路径。
本文系统讲图驱动推荐:先对比协同过滤与图推荐的差异,再建立用户-物品二部图模型;接着从相似度推荐(Jaccard/余弦)讲到路径推荐(元路径、多跳传播),用 Neo4j 落地实时推荐查询;然后上图嵌入推荐(node2vec/GraphSAGE),最后给出混合推荐架构与冷启动治理方案。
前置:/graphdb-algorithms-practice/(相似度与链路预测)、/graphdb-modeling-patterns/(建模)、/graphdb-gnn-embedding/(图嵌入)、/graphdb-graphrag-vector/(向量检索)。
目录
- 1. 协同过滤 vs 图推荐
- 2. 用户-物品二部图建模
- 3. 相似度推荐:Jaccard、余弦与共同邻居
- 4. 路径推荐:元路径与多跳传播
- 5. Neo4j 实时推荐查询
- 6. 图嵌入推荐:node2vec 与 GraphSAGE
- 7. 混合推荐架构
- 8. 冷启动治理
- 9. 推荐质量评估
- 10. 速查表
- 延伸阅读
1. 协同过滤 vs 图推荐
1.1 两者的本质差异
| 维度 | 协同过滤 | 图推荐 |
|---|---|---|
| 输入 | 用户-物品评分矩阵 | 关系图谱(多类型节点/关系) |
| 相似性 | 评分向量 | 邻居/路径/结构相似度 |
| 可解释性 | 难(矩阵分解潜向量) | 易(路径即解释) |
| 冷启动 | 很弱 | 借助关联实体(品牌/类目)缓解 |
| 丰富信号 | 只利用「评分」 | 可融合标签/浏览/购买/关注 |
1.2 图推荐的信号来源
评分信号 :用户评分/星级 → 加权关系
行为信号 :浏览/点击/购买 → 带类型的交互关系
属性信号 :品类/品牌/标签 → 物品之间的关系
社交信号 :好友/关注 → 用户之间的信任传播
一句话总结:协同过滤只看「评分矩阵」,图推荐把「一切交互与属性」建成关系——信号更丰富、可解释性更强、冷启动更可控。
2. 用户-物品二部图建模
2.1 核心图结构
// 二部图:用户与物品是两类节点,交互是关系
(:User {id, gender, age})-[:RATED {score, ts}]->(:Movie {id, title, genres})
(:User)-[:VIEWED]->(:Movie)
(:User)-[:PURCHASED]->(:Product)
(:User)-[:FOLLOWS]->(:User)
(:Movie)-[:IN_GENRE]->(:Genre)
(:Movie)-[:DIRECTED_BY]->(:Director)
2.2 建模要点
1. 交互关系带时间戳 → 支持「近期行为」加权
2. 用户-物品之外的「属性节点」(品类/导演/品牌)是冷启动的解药
3. 用户之间的关注/信任关系 → 社交传播推荐
4. 用唯一约束防重复节点
2.3 示例建模落地
CREATE CONSTRAINT IF NOT EXISTS FOR (u:User) REQUIRE u.id IS UNIQUE;
CREATE CONSTRAINT IF NOT EXISTS FOR (m:Movie) REQUIRE m.id IS UNIQUE;
CREATE (:User {id:'U1'})-[:RATED {score:5}]->(:Movie {id:'M1', title:'盗梦空间'})
CREATE (:User {id:'U1'})-[:RATED {score:4}]->(:Movie {id:'M2', title:'星际穿越'})
一句话总结:二部图把「用户-物品-属性」三类节点与「评分-行为-类型」关系建好,是图推荐的地基——属性节点是后续冷启动与路径推荐的关键。
3. 相似度推荐:Jaccard、余弦与共同邻居
3.1 Jaccard 相似度推荐「喜欢同一类物品的用户」
// 找到与 U1 行为最像的用户(Jaccard 相似度)
MATCH (u1:User {id:'U1'})-[:RATED]->(shared)<-[:RATED]-(u2:User)
WHERE u1 <> u2
RETURN u2.id,
count(DISTINCT shared) AS common,
// Jaccard = |共同| / |并集|
toFloat(count(DISTINCT shared)) / (size((u1)-[:RATED]->()) + size((u2)-[:RATED]->()) - count(DISTINCT shared)) AS jaccard
ORDER BY jaccard DESC LIMIT 10;
3.2 余弦相似度(加权评分)
// 余弦:考虑评分高低而非只看「是否评过」
MATCH (u1:User {id:'U1'})-[r1:RATED]->(m)<-[r2:RATED]-(u2:User)
WHERE u1 <> u2
RETURN u2.id,
sum(r1.score * r2.score) / (sqrt(sum(r1.score^2)) * sqrt(sum(r2.score^2))) AS cosine
ORDER BY cosine DESC LIMIT 10;
3.3 相似度→推荐
// 「相似用户评过高分但 U1 没看过」的电影
MATCH (u1:User {id:'U1'})
MATCH (u2:User)-[r:RATED]->(m:Movie)
WHERE u1 <> u2 AND r.score >= 4
AND NOT (u1)-[:RATED]->(m)
AND (u1)-[:RATED]->(:Movie)<-[:RATED]-(u2) // u2 与 u1 有共同评分
RETURN m.title, avg(r.score) AS score, count(u2) AS fromUsers
ORDER BY fromUsers DESC, score DESC LIMIT 20;
一句话总结:相似度推荐走「找相似用户 → 推荐他们喜欢而你没看过的」——Jaccard 看共同度、余弦加权评分,二跳查询即可落地。
4. 路径推荐:元路径与多跳传播
4.1 元路径(Meta-path)
不同元路径捕捉不同的推荐语义:
元路径 U-R-M-U-R-M :用户-评电影-用户-评电影 → 「口味相同的人看什么」
元路径 U-R-M-I-M :用户-评-电影-同类型-电影 → 「同类型的其他电影」
元路径 U-F-U-R-M :用户-关注-用户-评-电影 → 「朋友在看什么」
元路径 U-R-M-D-M :用户-评-电影-导演-其他电影 → 「同一导演的作品」
4.2 同类型推荐(U-R-M-I-M)
// 用户喜欢哪些类型 → 推荐同类型中评价最高的电影
MATCH (u:User {id:'U1'})-[:RATED {score:>=4}]->(m1:Movie)-[:IN_GENRE]->(g:Genre)
WITH u, g, avg(m1.score) AS pref // 用户对每个类型的偏好
MATCH (g)<-[:IN_GENRE]-(m2:Movie)
WHERE NOT (u)-[:RATED]->(m2)
RETURN m2.title, g.name, sum(pref) AS affinity
ORDER BY affinity DESC, m2.rating DESC LIMIT 20;
4.3 导演/演员同源推荐(U-R-M-P-M)
// 看过的电影的导演 → 该导演的其他电影
MATCH (u:User {id:'U1'})-[:RATED]->(m1:Movie)-[:DIRECTED_BY]->(d:Director)
MATCH (d)-[:DIRECTED]->(m2:Movie)
WHERE NOT (u)-[:RATED]->(m2)
RETURN m2.title, count(d) AS affinity
ORDER BY affinity DESC LIMIT 10;
4.4 多跳传播(社交推荐)
// 二度好友看过的高分内容(2 跳,信任衰减)
MATCH (u:User {id:'U1'})-[:FOLLOWS*1..2]->(f:User)-[r:RATED]->(m:Movie)
WHERE r.score >= 4 AND NOT (u)-[:RATED]->(m)
RETURN m.title, count(DISTINCT f) AS socialSupport
ORDER BY socialSupport DESC LIMIT 20;
一句话总结:元路径让推荐「可解释」——同类型、同导演、好友在看、二度传播,每条路径都对应一种可对用户解释的推荐理由。
5. Neo4j 实时推荐查询
5.1 为实时查询建索引
CREATE INDEX IF NOT EXISTS FOR (u:User) ON (u.id);
CREATE INDEX IF NOT EXISTS FOR (m:Movie) ON (m.id);
CREATE INDEX IF NOT EXISTS FOR (g:Genre) ON (g.name);
5.2 组装一条实时推荐查询
// 综合推荐:相似用户 + 同类型 + 同导演 多路召回,按信号强度融合
MATCH (u:User {id:$userId})
// 路 1:相似用户高分
MATCH (u)-[:RATED]->(m1:Movie)<-[:RATED]-(u2:User)-[r2:RATED]->(rec1:Movie)
WHERE r2.score >= 4 AND NOT (u)-[:RATED]->(rec1)
WITH rec1, sum(r2.score) * 1.0 AS s1, count(u2) AS c1
// 路 2:同类型
MATCH (u:User {id:$userId})-[:RATED]->(m:Movie)-[:IN_GENRE]->(g:Genre)
WITH rec1, s1, c1, collect(DISTINCT g) AS genres
MATCH (rec2:Movie) WHERE (rec2)-[:IN_GENRE]->(genres) AND NOT (u)-[:RATED]->(rec2)
WITH rec1, s1, c1, rec2, size(genres) AS affinity
// 汇总(简化示意)
RETURN coalesce(rec1.id, rec2.id) AS movie_id,
s1 + affinity * 0.5 AS score
ORDER BY score DESC LIMIT 20;
5.3 在线 vs 离线推荐
| 方式 | 场景 | 实现 |
|---|---|---|
| 在线 Cypher | 个性化实时召回 | 带索引、限制深度、LIMIT 截断 |
| 离线预计算 | 全量用户冷启动/首页推荐 | GDS 跑相似度/嵌入,结果写回 |
| 混合 | 在线召回 + 离线缓存 | 先在 Neo4j 查,缓存命中加速 |
一句话总结:实时推荐 = 多路元路径召回 + 加权融合 + 索引与 LIMIT 保性能;高频用户可用离线预计算结果缓存加速。
6. 图嵌入推荐:node2vec 与 GraphSAGE
6.1 为什么需要嵌入
当图谱规模巨大、路径复杂时,显式路径查询会变慢;图嵌入把每个节点压缩成低维向量,相似度计算变成向量内积,还能直接喂进已有 ML 栈。
6.2 node2vec 流程
# 1. 导出边列表
edges = [] # [(user1, item1, weight), ...]
# 2. 训练 node2vec(保留局部结构)
from node2vec import Node2Vec
model = Node2Vec(edges, dimensions=128,
walk_length=30, num_walks=100, workers=4).fit()
# 3. 推荐:候选物品向量与用户向量做余弦
import numpy as np
user_vec = model.wv['U1']
recs = []
for item in candidate_items:
vec = model.wv[item]
recs.append((item, np.dot(user_vec, vec) / (np.linalg.norm(user_vec)*np.linalg.norm(vec)+1e-9)))
recs.sort(key=lambda x: -x[1])
6.3 GraphSAGE 端到端(归纳学习)
# PyTorch Geometric GraphSAGE:预测用户-物品边
from torch_geometric.nn import SAGEConv
import torch.nn.functional as F
class SAGERec(torch.nn.Module):
def __init__(self, in_dim, hid_dim):
super().__init__()
self.conv1 = SAGEConv(in_dim, hid_dim)
self.conv2 = SAGEConv(hid_dim, hid_dim)
def forward(self, x, edge_index):
x = F.relu(self.conv1(x, edge_index))
return self.conv2(x, edge_index)
# 训练后:user_node_emb · item_node_emb → 点积打分
# 归纳性:新用户/新物品无需重训,用邻居特征聚合即可(缓解冷启动)
6.4 嵌入推荐的取舍
| 方法 | 优点 | 缺点 |
|---|---|---|
| node2vec | 简单、结构感知 | 转导式(新节点需重训) |
| GraphSAGE | 归纳式、可处理新节点 | 训练成本高、需特征 |
| 元路径嵌入 | 可解释性强 | 路径设计依赖专家 |
一句话总结:图嵌入把推荐从「显式路径查询」升级为「向量相似度」——node2vec 简单、GraphSAGE 归纳可抗冷启动;嵌入可与路径召回融合成多路推荐。
7. 混合推荐架构
7.1 多路召回 + 融合排序
召回(GraphDB):
① 相似用户高分 ② 同类型 ③ 同导演 ④ 社交二跳 ⑤ 向量嵌入TopK
排序(Model):
特征(召回分数、新颖度、热度、多样性)+ 模型(LR/GBDT)→ 精排
重排(业务):
去重、多样性(MMR)、品类平衡、实时反馈(不感兴趣)
7.2 Neo4j 在混合架构中的位置
离线:GDS 跑相似度/社区/嵌入 → 写回属性
在线:Neo4j 做多路召回(毫秒级)+ 外部排序模型精排
存储:Neo4j 管关系召回,向量库(如 FAISS)管嵌入 TopK
7.3 架构图
业务事件 → Kafka → 图更新(Neo4j) ─→ 离线GDS(相似度/嵌入) ─→ 写回
↑ ↓
用户请求 → 实时多路召回(Neo4j) → 融合排序(GBDT) → 多样性重排 → 响应
一句话总结:混合推荐 = 图多路召回 + 模型精排 + 业务重排——Neo4j 负责「关系召回」,向量库负责「嵌入召回」,排序模型融合后做业务重排。
8. 冷启动治理
8.1 新用户冷启动
// 新用户无评分 → 用「属性路径」推荐
// 注册时填了偏好类型 → 直接推荐该类型高分项
MATCH (g:Genre {name: $prefGenre})<-[:IN_GENRE]-(m:Movie)
WHERE NOT (m)<-[:RATED]-(:User {id:$userId})
RETURN m.title, m.rating ORDER BY m.rating DESC LIMIT 10;
// 无偏好 → 用热门榜(社区热度 = 被评分人数)
MATCH (m:Movie)<-[r:RATED]-()
RETURN m.title, count(r) AS popularity, avg(r.score) AS score
ORDER BY popularity DESC LIMIT 20;
8.2 新物品冷启动
新物品无人评 → 靠「内容属性路径」召回:
同品牌/同类目/同导演/同作者 → 先有曝光
再通过用户-属性-新物品路径推给对相关属性感兴趣的用户
8.3 冷启动总结
| 对象 | 无数据时的推荐信号 |
|---|---|
| 新用户 | 热门榜、注册偏好、设备地域 |
| 新物品 | 属性相似(品牌/类目/作者)、同创作者 |
| 新关系 | 用户社交二跳、嵌入归纳(GraphSAGE) |
一句话总结:冷启动靠「属性与热度」兜底——新用户推热门与偏好,新物品靠属性路径曝光,嵌入的归纳性让新节点也能被推荐。
9. 推荐质量评估
9.1 离线指标
Accuracy:HR@K、NDCG@K、MRR(排序质量)
多样性 :类别覆盖率、ILAD(物品间平均距离)
新颖度 :推荐中「非热门物品」占比
覆盖率 :被推荐物品占全物品比例
# 示例:计算 HR@10
def hit_rate(recs, ground_truth, k=10):
hits = sum(1 for rec in recs[:k] if rec in ground_truth)
return hits / len(ground_truth)
9.2 在线指标
CTR(点击率)、下单转化、人均时长、退坑率、投诉率
A/B 测试:图推荐 vs 矩阵分解 对照
9.3 线上闭环
推荐展示 → 用户反馈(点击/跳过/不感兴趣)→ 写回图谱
→ 图信号更新 → 下一轮召回更准
一句话总结:评估用 HR/NDCG 看离线精度、CTR/转化看在线价值,反馈写回图谱形成闭环——图推荐的优势正是「反馈即新关系,立即可用」。
10. 速查表
| 需求 | 图谱方案 |
|---|---|
| 找相似用户 | Jaccard / 余弦(共同邻居) |
| 推荐新物品 | 相似用户的高分未看物品 |
| 可解释推荐 | 元路径(同类型/同导演/社交) |
| 实时召回 | 多路 Cypher + 索引 + LIMIT |
| 大规模 | node2vec / GraphSAGE 嵌入 |
| 新用户 | 热门榜 + 注册偏好 |
| 新物品 | 属性路径曝光 |
| 评估 | HR@K / NDCG / CTR 闭环 |
一句话记忆:图推荐把「用户-物品-属性」建成二部图,用相似度(Jaccard/余弦)、元路径(同类型/同导演/社交二跳)、图嵌入(node2vec/GraphSAGE)多路召回;混合架构 = 图召回 + 模型精排 + 多样性重排;冷启动靠属性与热度兜底,评估用离线精度 + 在线 CTR,反馈写回图谱形成闭环——关系就是推荐信号。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。