推荐系统不只是给所有物品打分:候选太多时,通常先筛出一批可能相关的内容,再做更精细的排序。本文从协同过滤与矩阵分解讲起,区分召回、排序和评估的职责。离线指标能帮助筛选方案,但不能替代对曝光偏差和用户长期体验的检查。
前置阅读:机器学习基础、Embedding 概念(推荐系统的向量与语义检索同源)。
协同过滤:推荐的第一个伟大思想
协同过滤(CF)的洞察:相似的用户喜欢相似的物品。不需要理解物品内容,只看行为矩阵(用户 × 物品的评分/点击):
- UserCF:找和你口味像的人,把他们喜欢而你看过的推给你。
- ItemCF:找和你喜欢过的物品相似的物品(“看了又看”)。物品相似度由”共同喜欢它们的用户”定义。
UserCF 适合用户少的社区,ItemCF 更稳定(物品相似性变化慢),亚马逊的”购买此商品的顾客也购买了”就是 ItemCF 的经典落地。CF 的痛点:行为矩阵极度稀疏(99%+ 是空的),新用户新物品没有行为——冷启动问题。
矩阵分解:把协同过滤向量化
矩阵分解(MF)把巨大的稀疏评分矩阵 分解成两个小矩阵:
的每行是用户的 k 维兴趣向量, 的每列是物品的 k 维特征向量,预测就是点积。k 取几十到几百——这和 Embedding 是完全相同的思想:把离散的 ID 变成连续向量,相似性变成距离。
用 SGD 训练一个最小实现:
import numpy as np
def train_mf(R, k=32, lr=0.01, reg=0.02, epochs=50):
m, n = R.shape
P = np.random.normal(0, 0.1, (m, k))
Q = np.random.normal(0, 0.1, (n, k))
users, items = np.nonzero(R) # 只在有行为的位置学
for epoch in range(epochs):
for u, i in zip(users, items):
err = R[u, i] - P[u] @ Q[i]
P[u] += lr * (err * Q[i] - reg * P[u])
Q[i] += lr * (err * P[u] - reg * Q[i])
return P, Q
# 给用户 u 推荐:P[u] @ Q.T 排序,排除已交互物品
现代架构:召回 + 排序两段式
工业推荐系统的标准架构,两个阶段各司其职:
召回(Retrieval):从百万物品里快速捞出几百个候选,要求快、求全。主力是双塔模型:用户塔和物品塔各自把特征编码成向量,训练让正样本对向量接近——线上检索就变成向量最近邻(Faiss/Milvus 的用武之地)。多路召回(行为向量 + 热门 + 内容相似)并行取并集是常态。
排序(Ranking):对几百个候选精排,要求准。特征是用户画像 + 物品属性 + 交叉特征,模型从 LR/GBDT 演进到 DeepFM/DIN。预测点击率(CTR)或时长,按分排序产出最终列表。
后面还有一层重排:打散同作者、保证多样性、插入探索流量——纯算法之外的业务规则层。
评估:离线看 AUC/Recall,在线看 AB
- 召回侧:Recall@K(用户真实点击有多少被召回了)——漏掉的好物品排序救不回来。
- 排序侧:AUC(点对排序能力)、NDCG@K(位置加权的命中率,排越前权重越高)。
- 终极裁判是线上 A/B 测试:离线指标涨线上不一定涨(离线数据是旧策略的产物,有选择偏差)。评估方法的细节见 A/B 测试篇。
冷启动与偏差:推荐的两座大山
冷启动:新用户→用注册画像/热门榜兜底;新物品→用内容特征(标题/类别的向量)进入召回,给小流量试探。这是双塔里加内容特征而非纯 ID 向量的动机。
偏差治理:位置偏差(排前面的天然点击高,训练数据被污染)、流行度偏差(热门越推越热)。常见手段:点击模型去偏、IPS 加权、探索流量(ε 比例随机曝光)——和强化学习的探索-利用遥相呼应。
踩坑排查
| 症状 | 原因 | 处理 |
|---|---|---|
| 推荐全是热门物品 | 流行度偏差 | 降权热门、加探索流量、负采样 |
| 新用户推荐质量差 | 冷启动无行为 | 画像特征 + 热门兜底 |
| 离线涨在线跌 | 选择偏差 | 用倾向性加权;相信 A/B |
| 双塔检索结果趋同 | 负采样太弱 | 加 batch 内负样本/难负样本 |
| 排序模型过拟合 ID | ID 特征占比过大 | ID embedding 降维 + 正则 |
| 推荐结果重复度高 | 缺多样性约束 | 重排层 MMR 打散 |
练习
- 在 MovieLens-100k 上实现矩阵分解,计算 Recall@20。
- 实现 ItemCF 的简化版(共现计数 + 余弦相似),与 MF 对比。
- 构造双塔模型的训练样本(用户历史 → 下一个点击),用 PyTorch 训练并做向量检索。
- 统计推荐结果的流行度分布,实现一个流行度降权并观察变化。
面试常问
Q:UserCF 和 ItemCF 怎么选? 用户少且稳定用 UserCF(新闻社区);物品稳定用 ItemCF(电商)——物品相似性更新慢、可离线预计算、推荐解释自然(“因为你买过 X”)。实践中 ItemCF 用得更广。
Q:双塔模型为什么适合做召回? 用户和物品向量解耦:物品向量可离线全量预计算建索引,线上只需算一次用户向量 + 一次 ANN 检索,毫秒级从百万库取候选。缺点是双塔交互太浅,精度不如排序模型——这正是两段式架构的分工。
Q:推荐系统怎么处理冷启动? 新用户:注册画像 + 热门/编辑精选 + 快速试探(bandit);新物品:内容特征向量进召回池 + 小流量扶持 + 观察期。核心思想是用内容信号补行为信号的缺失。
Q:为什么离线指标和线上效果经常不一致? 训练数据由旧策略产生(选择偏差),离线评估测的是”旧策略会怎么排这些数据”而非”新策略上线后用户会怎么点”。只有 A/B 测试能拿到新策略的真实因果效应。
相关阅读:A/B 测试与统计推断、K-Means 用户分群。
💬 评论
这套方案在哪一步卡住了?也欢迎补充你的实现方式或踩坑经验。