第4章:协同过滤入门:User-based CF、Item-based CF、矩阵分解基础
好,咱们进入推荐系统最经典的一块——协同过滤。说实话,这玩意儿我用了快十年了,从最早的电商网站到后来的短视频平台,核心思想一直没变:物以类聚,人以群分。
今天咱们聊三个方向:User-based CF、Item-based CF,还有矩阵分解。前两个是传统方法,第三个是深度学习时代的基础。我建议你把这章吃透,后面很多高级玩法都从这里长出来的。
4.1 User-based CF:找和你口味相似的人
User-based Collaborative Filtering,说白了就是「邻居推荐」。你想想看,你身边总有几个朋友,推荐电影、音乐特别对你胃口。算法做的就是这件事——找到和你行为最像的那群人,把他们喜欢的东西推荐给你。
具体怎么算?核心两步:
- 计算用户相似度:用余弦相似度或者皮尔逊相关系数
- 加权聚合:把相似用户的评分加权平均,预测你没看过的物品
举个简单的例子。假设有三个用户对两部电影的评分:
| 用户 | 《流浪地球》 | 《哪吒》 |
|---|---|---|
| 小明 | 5 | 3 |
| 小红 | 4 | 4 |
| 小刚 | 2 | 5 |
我想给小明推荐《战狼2》,但小明没看过。怎么办?先算相似度。小明和小红的评分向量是(5,3)和(4,4),余弦相似度算出来是0.98。小明和小刚是(5,3)和(2,5),相似度只有0.78。所以小红是小明的「近邻」。
如果小红给《战狼2》打了5分,那小明很可能也会喜欢。
核心公式(余弦相似度):
similarity(A, B) = (A·B) / (||A|| * ||B||)
我的经验:实际项目中,用户数往往远大于物品数。User-based CF在用户量百万级时,实时计算相似度会非常慢。我一般会离线预计算用户相似度矩阵,或者用LSH(局部敏感哈希)做近似查找。
4.2 Item-based CF:找和你喜欢的物品相似的物品
Item-based CF的思路刚好反过来——不找相似的人,找相似的物品。你给《流浪地球》打了5分,那系统就去找和《流浪地球》最像的电影推荐给你。
为什么会有这个思路?我记得2010年左右,亚马逊发了一篇论文,说Item-based CF在电商场景下效果比User-based好很多。原因很简单:物品的数量相对稳定,用户的数量却一直在涨。物品相似度矩阵可以离线算好,线上查询就是O(1)的事。
计算物品相似度时,我们看的是「哪些用户同时喜欢这两个物品」。比如:
| 物品 | 喜欢它的用户集合 |
|---|---|
| 《流浪地球》 | {小明, 小红, 小丽} |
| 《星际穿越》 | {小明, 小红, 小刚} |
| 《爱情片A》 | {小丽, 小刚} |
《流浪地球》和《星际穿越》的共同用户有小明和小红,相似度就高。而《流浪地球》和《爱情片A》只有小丽一个共同用户,相似度就低。
避坑指南:我曾经犯过一个错误——直接用所有用户计算物品相似度。结果热门物品和谁都像,推荐结果全是爆款。后来加了IUF(Inverse User Frequency)惩罚,给活跃用户降权,效果才正常。
4.3 矩阵分解:把用户和物品塞进同一个空间
前面两种方法都有个共同问题——稀疏性。用户-物品矩阵里,99%以上的格子都是空的。你想想看,一个用户看过几百部电影,但平台有几十万部,这比例太低了。
矩阵分解就是来解决这个问题的。它的核心思想:把用户和物品映射到同一个低维空间,用隐向量来表示。
比如,我们设定隐向量维度k=10。每个用户用一个10维向量表示,每个物品也用10维向量表示。用户对物品的评分,就是这两个向量的点积:
r_ui ≈ p_u · q_i
其中p_u是用户u的隐向量,q_i是物品i的隐向量。
怎么学出这些向量?最经典的方法是SVD(奇异值分解),但实际工业界更常用ALS(交替最小二乘法)或者SGD(随机梯度下降)。
SGD更新公式(简化版):
e_ui = r_ui - p_u · q_i
p_u = p_u + lr * (e_ui * q_i - reg * p_u)
q_i = q_i + lr * (e_ui * p_u - reg * q_i)
其中lr是学习率,reg是正则化系数。
我个人的习惯是先用SGD快速迭代出一个baseline,再用ALS做精细调优。ALS的好处是可以并行化,适合Spark这种分布式框架。
4.4 三种方法的对比与选型
咱们用一张表总结一下:
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| User-based CF | 直观、容易解释 | 用户量大时计算慢、冷启动问题 | 用户数少、社交属性强的场景 |
| Item-based CF | 稳定、可离线预计算 | 无法捕捉用户兴趣变化 | 电商、视频等物品相对稳定的场景 |
| 矩阵分解 | 精度高、能处理稀疏数据 | 可解释性差、训练耗时 | 大规模推荐系统、需要高精度的场景 |
我的建议:如果你刚开始做推荐系统,先从Item-based CF入手。它实现简单,效果稳定,线上出问题也好排查。等业务量上来了,再逐步引入矩阵分解。别一上来就搞深度学习,容易翻车。
4.5 实战中的几个坑
最后分享几个我踩过的坑:
- 相似度归一化:不同用户的评分习惯不一样,有人喜欢打高分,有人喜欢打低分。不做归一化,相似度计算会偏。我一般先做均值中心化,再算相似度。
- 冷启动问题:新用户没有行为数据,协同过滤直接失效。这时候要用热门推荐或者内容特征兜底。我习惯在召回层同时跑协同过滤和内容召回,做加权融合。
- 实时性:用户刚点了一个物品,下一秒就应该更新推荐。Item-based CF可以做到秒级更新,矩阵分解就比较吃力。我一般用增量更新,只更新受影响的那部分向量。
嗯,这一章的内容就到这。下一章咱们聊协同过滤的工程实现,包括怎么用Spark做大规模矩阵分解,怎么在线上做实时召回。到时候我会把代码贴出来,咱们一行一行过。