2. 协同过滤基础:基于用户的协同过滤、基于物品的协同过滤、相似度计算方法
好,咱们今天聊聊协同过滤。说实话,这是推荐系统里最经典、最接地气的一类算法。我入行那会儿,第一个上线的推荐模块用的就是协同过滤。你想想看,它的核心思想其实特别朴素——物以类聚,人以群分。
说白了,就是利用用户和物品之间的交互行为(比如点击、购买、评分),来发现相似的用户或相似的物品,然后做推荐。嗯,这里要注意,它不需要知道物品的内容是什么,也不需要知道用户的画像,全靠行为数据说话。
2.1 基于用户的协同过滤(UserCF)
这个思路很直接:找到和你口味相似的人,把他们喜欢的东西推荐给你。
我在项目中遇到过这样一个场景:一个新用户注册后,只看了两本技术书。系统通过UserCF发现,有几个老用户也看过这两本书,而且他们还买了《深度学习入门》和《算法导论》。好,那这两本书就可以推给新用户了。
具体步骤分三步:
- 计算用户相似度:根据用户的历史行为,算出用户之间的相似度矩阵。
- 找到最近邻:对目标用户,找出相似度最高的K个用户(邻居)。
- 生成推荐:把邻居们喜欢、但目标用户还没碰过的物品,按得分排序推荐。
这里有个关键点——用户相似度怎么算? 我习惯用余弦相似度或者皮尔逊相关系数。咱们后面会细讲。
UserCF的优缺点
- 优点:推荐结果有社交解释性(“和你相似的人也买了...”),用户容易接受。
- 缺点:用户数量大时,相似度矩阵计算量爆炸。而且新用户冷启动问题严重——没有行为数据,就找不到邻居。
我的经验:UserCF适合用户数相对稳定、物品更新快的场景,比如新闻推荐。我曾经在一个社交平台上用过,效果不错,但每天凌晨算相似度矩阵时,服务器CPU直接拉满...后来我加了增量更新才缓解。
2.2 基于物品的协同过滤(ItemCF)
这个更常用,也是电商平台的主流做法。核心逻辑是:推荐和你喜欢过的物品相似的物品。
举个例子:你买了一本《Python编程》,系统发现买这本书的人大多也买了《数据科学入门》。那系统就会把后者推荐给你。注意,这里不是看物品内容是否相似,而是看行为模式上的共现关系。
步骤也很清晰:
- 计算物品相似度:根据用户的行为记录,计算物品之间的相似度矩阵。
- 生成推荐:对目标用户喜欢的物品,找出最相似的N个物品,去重后推荐。
你可能会问:为什么ItemCF比UserCF更流行?
原因很简单:物品数量通常比用户数量少,而且物品的相似度相对稳定。你想想看,用户的口味可能天天变,但《三体》和《流浪地球》的相似关系,一年半载都不会变。所以物品相似度矩阵可以离线算好,线上直接查,性能好很多。
避坑指南:我曾经犯过一个错——直接用所有用户的行为算物品相似度,结果热门物品被过度推荐。后来我加了惩罚热门物品的机制(比如用log函数降权),推荐多样性才提上来。
2.3 相似度计算方法
相似度计算是协同过滤的基石。选错了方法,推荐效果直接崩。我整理了几种最常用的:
| 方法 | 公式 | 适用场景 | 我的建议 |
|---|---|---|---|
| 余弦相似度 | cos(A,B) = (A·B) / (||A|| ||B||) | 评分数据、隐式反馈(点击/购买) | 最常用,简单稳定 |
| 皮尔逊相关系数 | r = Σ((x-μx)(y-μy)) / (n·σx·σy) | 评分数据(用户评分尺度不同时) | 能消除评分偏差,我偏好这个 |
| Jaccard相似度 | J(A,B) = |A∩B| / |A∪B| | 只有购买/收藏等布尔行为 | 简单粗暴,适合冷启动 |
| 调整余弦相似度 | 减去用户平均评分后再算余弦 | 评分数据,用户评分习惯差异大 | 比普通余弦更准,但计算量大 |
咱们重点说说余弦相似度和皮尔逊相关系数的区别。
余弦相似度看的是两个向量的方向是否一致。比如用户A和B都买了5本书,但A给每本打5分,B给每本打3分。余弦相似度会认为他们很相似(方向一致),但皮尔逊相关系数会扣掉各自的平均分,发现其实评分模式不同。
我个人习惯:如果数据是评分,优先用皮尔逊;如果是点击/购买这种0/1数据,用余弦或Jaccard就够了。
代码示例:Python实现余弦相似度
import numpy as np
def cosine_similarity(vec_a, vec_b):
dot_product = np.dot(vec_a, vec_b)
norm_a = np.linalg.norm(vec_a)
norm_b = np.linalg.norm(vec_b)
if norm_a == 0 or norm_b == 0:
return 0.0
return dot_product / (norm_a * norm_b)
# 示例:两个用户的评分向量
user1 = [5, 3, 0, 1]
user2 = [4, 0, 0, 1]
print(cosine_similarity(user1, user2)) # 输出:0.975
小技巧:实际工程中,我一般会把相似度矩阵存成稀疏矩阵格式(比如用scipy.sparse),不然内存分分钟爆掉。尤其是UserCF,用户数一多,矩阵就是几百万乘几百万,普通机器根本扛不住。
2.4 UserCF vs ItemCF:怎么选?
这个问题我经常被问到。直接给结论:
- 用户数少、物品数多(比如新闻网站):用UserCF。因为用户少,相似度矩阵小,而且新闻更新快,物品相似度不稳定。
- 用户数多、物品数少(比如电商、视频网站):用ItemCF。物品相似度稳定,可以离线算好,线上响应快。
- 冷启动场景:两个都不太灵。这时候我一般先用热门推荐或者内容推荐顶一阵,等行为数据积累够了再切协同过滤。
嗯,这里要提醒一句:协同过滤不是万能的。它依赖用户行为数据,如果数据稀疏(比如大部分用户只交互过1-2个物品),相似度算出来就不准。我踩过这个坑——当时一个App刚上线,用户行为很少,UserCF推荐出来的东西跟随机推荐差不多。后来我加了矩阵分解(咱们后面章节会讲),才把效果拉起来。
好了,这一章就到这里。下一章咱们聊聊矩阵分解,那是协同过滤的进阶版,也是很多工业级推荐系统的基石。