老王在小区门口开了一家小卖部,想让每位邻居都买到合心意的东西。他不挨个问,而是准备了一个“邻里评价本”:谁买过什么、打几分,都清清楚楚记下来。推荐逻辑听上去很美——口味相似的邻居喜欢什么,就推荐什么,这就是协同过滤。但翻开评价本,老王立刻遇到两个头疼的问题:新邻居刚搬来,本子上一条记录都没有,这叫冷启动;老邻居们大多只写过三五条,几百种商品绝大多数是空白,这叫稀疏性。空白越多,老王越难判断谁跟谁口味像。为了解这两道难题,老王学会了计算“口味相似度”,又从零散记录里提炼“酸甜苦辣”等基本味型(隐因子),最后用一套工程办法:热门榜单先兜底、小流量试水、随时盯指标。本文就用这个故事,把协同过滤从理论到落地讲明白。
冷启动与稀疏性是推荐系统从离线模型走向在线业务时最先撞上的两堵墙。掌握相似度计算、矩阵分解与混合兜底策略,是推荐算法工程师搭建可靠系统的核心能力。
可视化演示
模拟用户-物品交互矩阵稀疏性、冷启动困境与矩阵分解降维过程,展示三种模式下稀疏度与推荐质量的变化。
本节配有交互动效(进入视口后自动加载,约 3KB)。
模式1:交互矩阵稀疏性 紫色点表示已有交互 灰色空格表示未交互 稀疏度 = 1 - 交互数/(用户数×物品数) 真实系统稀疏度通常超过99%。
协同过滤基础
老王翻着邻里评价本,发现推荐门道不是按照价格摆货,而是“找对人”:张三口味像李四,那李四说好的酱豆腐,张三大概率也爱。这就是协同过滤的第一个基石——不靠商品说明书,只靠人与人、物品与物品之间的行为记录互相推荐。
在技术里,评价本就是一个用户-物品矩阵:行是用户,列是商品,格子里是评分。绝大多数格子空白,少数格子里有 1 到 5 分。协同过滤要做的是:找到目标用户的“口味相似邻居”,再用这些邻居的打分预测空白格子。实际计算时,不能直接拿邻居的原始分相加,因为有的人天生手松、爱给高分,有的人手紧、只给三分。于是先减去每个人的平均分,再按相似度加权,最后加回自己的平均分。
老王明白了:预测不是简单抄作业,而是先看邻居们“比平时更喜欢还是更讨厌”,再搬回自己的基准线上。这套思路无论找相似用户,还是找相似商品,都通用。
- 邻居集合先找出和目标用户口味最相近的一群邻居,相似度低于阈值的不要。
- 去均值化每个邻居的打分都先减去自己的平均分,抵消“手松手紧”造成的尺度差异。
- 加权求和相似度越高,话语权越大,把邻居们的相对偏好汇总起来。
- 还原预测把偏移量归一化后加回目标用户的平均分,得到最终预测。
相似度度量
要判断两个邻居口味像不像,老王不能只看他们买过同一包辣条,还要看整体偏好是否一致。他给每个邻居画了一张“口味指纹”:把所有商品打分排成一个长长的向量,两个向量方向越一致,口味就越像。
余弦相似度就是干这件事的。它只比较方向,不比较长度。为什么?因为张三可能每样东西都比李四多打一分,但两个人对商品的“相对喜好”完全一致。余弦只看方向夹角,自动忽略这种“全局手松手紧”。不过它也有缺陷:如果两个人都只对同一件商品打了分,哪怕一个是五分一个是三分,余弦也可能算出很高相似度,因为共同评分太少,偶然性太大。
老王发现,余弦不够用时还可以用皮尔逊相关系数——它先给每个人减去平均分再做余弦,等于把“手松手紧”也去掉了。对于点击、收藏这类没有具体分数的行为,杰卡德相似度看“共同买过的品类占比”,更合适。相似度是协同过滤的秤,秤不准,后面全白搭。
- 共同评分集合只看两个人同时打过分的商品,没有共同记录就算不出相似度。
- 分子累加同一商品上两人打分相乘再求和,方向一致时贡献为正。
- 分母归一分别计算两个用户打分向量的长度并相乘,让结果只与方向有关。
- 比值输出分子除以分母得到 −1 到 1 之间的相似度,越大越像。
冷启动问题
这天小区搬来一位新邻居,评价本上他的名字后面一片空白。老王想给他推荐辣条,却找不到任何口味相似的旧邻居。这就是冷启动:没有历史行为,协同过滤这个“口碑推荐引擎”直接失灵。冷启动不止新用户一种,还有新商品——货架上刚到的坚果没人买过,自然也不会被推荐;以及新系统——刚上线时整个评价本都是空的,连老邻居都找不到。
对付新用户,最常见的做法是注册时引导选兴趣标签,先用热门榜和内容特征顶上去;对付新商品,则靠商品本身的品类、图文特征找相似旧商品;对付新系统,干脆先做热度榜或人工精选,等评价有了一定积累再逐步切换协同过滤。工程上更稳妥的是混合策略:协同过滤给一个分,热门榜给一个底分,两者按权重融合。
老王最后总结:冷启动的本质是“还没建立起口碑网络”,所以不能只靠网络内部互相推荐,要借助外部信息——标签、内容、热度——先把网络织起来。
- 协同过滤分先用已有邻居和相似度算出一个偏好预测。
- 热度兜底分用商品被所有用户评分的平均值代表大众接受度。
- 权重融合α 越大越依赖个性化协同过滤,α 越小越依赖大众热度。
- 动态调整用户行为越丰富,α 越高;新用户则让 α 接近 0。
稀疏性挑战
即使老邻居都在评价本上留过言,问题依然很大:三百个邻居、五百种商品,本应有十五万个格子,真正填了评分的可能只有几千个,空白率高达百分之九十九。老王想算两位邻居的余弦相似度,却发现他们共同打分过的商品只有一两种,算出来的相似度像抽签一样不可靠。
稀疏性用稀疏度量化:已观测评分除以所有可能格子的比例,离 1 越近越稀疏。稀疏会带来两个连锁反应:一是相似度估计失真,偶尔的共同评分被当成强信号;二是覆盖率极低,大量长尾商品从来没进入过推荐列表,形成马太效应——热门的越来越热,冷门的永远冷门。
老王开始明白,空白太多时,直接在两两相似度上做文章会越来越不准,必须换思路:不再死磕“共同记录”,而是把整个矩阵压缩成少数几个潜在口味维度,让所有邻居共享一套“基本味型”。
- 全部格子用户数乘以物品数,理论上的最大评分记录数。
- 已观测记录评价本上真实存在的评分数。
- 占比相减1 减去“已填比例”就是“空白比例”,越接近 1 越稀疏。
- 现实量级假设只有 1% 格子有分,稀疏度就是 0.99。
矩阵分解
老王观察了很久,发现邻居们口味其实没那么复杂:有人偏爱酸甜,有人无辣不欢,有人就喜欢香脆。如果把口味拆成“辣度”“甜度”“脆度”等少数几个基调,每个用户只是不同基调的混合比例,每件商品也是不同口味的混合比例,那么空白评分就能用两个比例向量点乘出来。这就是矩阵分解。
矩阵分解把高维稀疏的评分矩阵 R 拆成两个矮胖矩阵:P 是用户对隐因子的偏好,Q 是商品在隐因子上的含量。训练时只对已观测评分计算误差,同时加正则项防止把稀疏数据里的噪声背下来。
求解时,常见方法有交替最小二乘:先固定用户向量,把它当常数去优化商品向量;再固定商品向量反过来优化用户向量,反复交替直到收敛。老王把三百个邻居的复杂口味压缩成三种基本味型,终于能对空白格子给出合理猜测。
- 隐因子向量每个用户有一个偏好向量,每个商品有一个特征向量,维度 k 远小于用户数和商品数。
- 预测评分用户向量和商品向量做点积,得到该用户对商品的预测分。
- 观测残差只对实际有评分的格子计算平方误差,空白格不参与训练。
- 正则约束λ 惩罚隐向量长度,防止稀疏矩阵下模型把个别记录记得太死。
工程优化
理想算法还要能落地。老王不可能每次有人进店都现场翻本子、现算相似度再推荐,那样顾客早走了。工程上要先离线计算:每天打烊后用全量数据跑一遍矩阵分解,生成每位邻居的“推荐清单”存在货架旁。但口味会变,所以还要增量更新:每隔几分钟把新产生的评价喂给模型,小步快跑地调整隐向量。
同时,任何单一算法都有失灵的时候——新商品没有任何评分、今天系统刚崩过。于是老王准备了混合策略:协同过滤分数、内容相似分数、热门榜分数按权重合并;万一主模型超时,就直接降级成畅销榜,保证顾客永远有推荐可看。
老王的小卖部由此形成闭环:晚上批量算好推荐清单,白天实时追加新反馈,模型再自信也不敢撤掉热门榜这个安全网。
- 协同过滤分基于邻居行为的个性化信号,推荐精度高但依赖行为数据。
- 内容语义分基于商品图文标签的相似度,能覆盖没有行为记录的新品。
- 流行度分全局热度的兜底信号,冷启动时尤其重要。
- 权重加和α、β、γ 之和为 1,根据业务阶段动态调整,并配有降级开关。
评估上线
推荐系统改版不能拍脑袋。老王决定在门口摆试吃摊做实验:把邻居分成 A、B 两组,A 组用新推荐算法,B 组用旧算法,比较谁买得更多。离线阶段,他先用历史数据算准召率,看看推荐列表里有多少是用户真买过的东西;在线阶段,再做小流量 A/B 实验,最后灰度放量。
离线指标最常见的是 Precision@K:在给用户推荐的 K 个商品里,有多少个确实被用户点击或购买。这个指标直观但不能反映全貌,还要配合召回率和覆盖率一起看。
老王上线后还盯着监控面板:新用户点击率有没有异常,新商品的曝光占比是否过低,模型延迟有没有飙高。一旦指标恶化,立刻回滚到旧版本。至此,他从一个只会口口相传的小卖部老板,变成了一个会用数据持续优化推荐的“系统负责人”。
- 推荐列表系统按得分从高到低给用户生成 K 个推荐位。
- 真实相关用户后来真正产生过购买、点击等行为的物品集合。
- 交集命中推荐列表和真实相关物品重合的数量。
- 取比值除以 K 得到精确率,越高说明推荐位越不浪费。
AI 应用场景
电商新品冷启动
阶段:召回与排序阶段的冷启动兜底
解决的问题:新品没有用户评分,协同过滤无法直接预测“谁喜欢它”。传统做法只能靠运营手动配置位置,效率低且覆盖不全。AI 利用新品图文标签和同类目历史商品的行为数据,先算出内容相似分,再叠加热门相似品带来的间接协同信号,让新品也能进入推荐流。
具体案例:某电商平台新上架一款耳机,零评价。算法先用类目、价格带、商品标题向量找到若干历史爆款耳机,借用它们的用户偏好生成候选集,再用小流量给“偏好相似的老用户”试推。积累几百个点击后,逐步切换成正式协同过滤模型。
视频平台高稀疏矩阵优化
阶段:离线训练与在线增量更新
解决的问题:长尾视频成千上万,用户观看记录不足 1%,稀疏度高达 99.9%,传统最近邻相似度不可用。矩阵分解把亿级用户和千万级视频压缩到几十维隐因子空间,用隐因子点积填补空白,同时用正则化防止只记住少数热门观看记录。
具体案例:某短视频平台每天凌晨用全量观看时长构建评分矩阵,训练隐因子模型;白天每隔十分钟用新增观看行为做增量更新,让用户兴趣变化能快速反映到推荐结果中。遇到模型超时,直接降级为热门视频榜。
音乐产品混合降级策略
阶段:线上预估与流量策略
解决的问题:新注册用户没有任何听歌记录,无法用协同过滤生成歌单。纯热门榜虽然稳定但个性化不足。混合推荐把协同过滤分、艺人内容相似分、全局热榜分按权重相加,新用户权重偏重内容相似和热榜,老用户权重偏重协同过滤。
具体案例:某音乐 App 在注册页让用户挑三个喜欢的歌手,用这组歌手的内容特征初始化用户向量;听歌行为超过 20 条后开始叠加协同过滤评分。灰度发布时选取 10% 用户做 A/B 实验,确认次日留存和人均播放时长正向提升后再全量上线。
Python 演示
import numpy as np # 评分矩阵:0 表示未打分R = np.array([ [5, 3, 0, 1, 0], [4, 0, 0, 1, 2], [1, 1, 0, 5, 0], [0, 0, 4, 0, 4], [0, 1, 5, 0, 0],]) def train_mf(R, k=3, steps=500, lr=0.02, reg=0.1): users, items = R.shape rng = np.random.default_rng(42) P = rng.normal(0, 0.1, (users, k)) Q = rng.normal(0, 0.1, (items, k)) obs = R > 0 for _ in range(steps): pred = P @ Q.T err = np.where(obs, R - pred, 0.0) P += lr * (err @ Q - reg * P) Q += lr * (err.T @ P - reg * Q) return P, Q P, Q = train_mf(R)full = P @ Q.T def score_sparsity(matrix): total = matrix.size viewed = int((matrix > 0).sum()) return 1 - viewed / total print("稀疏度:", round(score_sparsity(R), 3))print("补全后的预测矩阵:")print(np.round(full, 2))
obs = R > 0标记已观测评分的位置,只在这些格子上计算误差。pred = P @ Q.T用用户向量和物品向量点积生成完整预测矩阵。err = np.where(obs, R - pred, 0.0)空白位置的误差清零,避免“没打分”被当成“低分”。P += lr * (err @ Q - reg * P)沿着梯度更新用户隐向量,同时用正则项抑制过拟合。return 1 - viewed / total计算稀疏度,直观看到数据空白问题的严重程度。
常见误区
误区“冷启动不就是新用户没历史数据吗?给新用户推荐热门榜就行了,新商品反正不急着推。”
正解冷启动有三类:新用户、新商品、新系统。新商品没有曝光记录,协同过滤同样无法为它找到相似物;新系统则整个矩阵都是空的。只盯着新用户,会导致新品长尾永远冒不出来。正确的做法是分类处理:用户冷启动用兴趣标签和热门榜,商品冷启动用内容特征和相似旧品,系统冷启动先用人工精选冷启。
为何常见:因为日常产品里最容易感知到的是“我刚注册,却刷不到我喜欢的内容”,新商品的困境藏在后台,只有算法同学看数据才能发现。
误区“矩阵里那么多空格,直接当成 0 分,或者全部填平均值,不就能算了?”
正解把空白当成 0 分,等于把“没吃过这道菜”误解成“讨厌这道菜”,会严重扭曲相似度。平均值填充则会制造大量虚假共现,让所有用户看起来都差不多。更稳的做法是矩阵分解:只让模型拟合已观测评分,用隐因子点积预测空白,空白本身不参与损失计算。
为何常见:人对“缺失值”的第一反应是“用一个值补上”,这来自传统数据处理习惯,但推荐系统的缺失不是随机噪声,而是带有强烈偏性的真实信息缺失。
误区“相似度算出来之后,把所有正相似度的邻居都拿来做预测,信息越全越准。”
正解稀疏矩阵下,很多邻居只有一两条共同评分,相似度本身是噪声。全部纳入会稀释真正高价值邻居的贡献,像让一百个只尝过一种零食的人投票决定你爱吃什么。工程上应只取 Top-N 高相似邻居,或是设置相似度阈值,宁缺毋滥。
为何常见:直觉上“样本越多越好”,但忽略了样本质量。协同过滤里每个邻居的可信度天差地别,数量不等于质量。
误区“模型离线训练好,每天跑一遍历史数据,推荐结果就一直准。”
正解用户兴趣会漂移,商品库每天在更新,离线模型会越跑越旧。需要增量更新让新行为在几分钟内进入模型,同时保留离线全量训练的稳定性。还要加混合推荐和降级兜底,主模型挂了就切热门榜,保证线上永远有推荐可用。
为何常见:受传统批处理思维影响,认为模型是一次性交付物。推荐系统是持续运营的服务,不是写一次就完事的函数。
误区“离线评估 Precision 涨了 3%,直接全量上线,效果一定会变好。”
正解离线指标评估的是历史数据,无法反映用户对新鲜度、多样性的真实反应,甚至可能过拟合。必须先做小流量 A/B 实验,对比点击率、转化率、留存等业务指标,再灰度放量。上线后还要监控新用户和新商品的细分指标,发现异常立即回滚。
为何常见:离线实验跑得快、数字直观,容易让人忽略在线环境中的各种偏差。推荐系统最终以线上真实反馈为准,离线只是第一道筛选。
面试题
Q1. 协同过滤的核心思想最接近下列哪项?
- A.按商品成本自动定价
- B.利用相似偏好的人做推荐← 正确答案
- C.只看全网销量最高品
- D.按用户年龄划分货架
答案:B。协同过滤不依赖成本和人口属性,而是利用用户行为寻找相似偏好,再互相推荐。热门榜单虽然能兜底,但缺少个性化,不属于协同过滤核心思想。
Q2. 用户物品矩阵里“稀疏”指的是什么?
- A.用户和物品数量都很少
- B.有评分的格子占比很低← 正确答案
- C.评分大多是同一个数
- D.物品名称重复出现
答案:B。稀疏指的是绝大多数格子上没有评分,而不是矩阵本身很小。即使用户和物品都很多,只要评分记录稀疏,矩阵依然是稀疏矩阵。
Q3. 新用户冷启动面临的主要难题是?
- A.没有历史行为可参考← 正确答案
- B.历史打分噪声特别大
- C.相似度公式算不出来
- D.系统无法生成推荐页面
答案:A。新用户的问题是完全没有行为记录,因此找不到相似邻居。噪声大、公式算不出是稀疏数据下的问题,不是冷启动特有的主因;页面生成是前端工程问题。
Q4. 余弦相似度未考虑用户打分尺度的原因是?
- A.它只求向量方向夹角← 正确答案
- B.它对缺失评分取均值
- C.它必须使用归一化值
- D.它看用户性别属性
答案:A。余弦相似度只关心两个向量方向是否一致,不关心向量长度,因此不会自动减去用户平均分。皮尔逊相关系数先做去中心化,是余弦的改进版本。
Q5. 皮尔逊相关系数和余弦相似度的核心差异?
- A.皮尔逊先做去中心化处理← 正确答案
- B.皮尔逊不需要共同评分
- C.余弦更适合离散数据
- D.余弦必须做减均值处理
答案:A。皮尔逊相关系数在计算前先减去每个用户的平均评分,把“手松手紧”的尺度差异去掉;余弦相似度没有这一步骤。两者都需要共同评分项。
Q6. 关于矩阵分解隐因子的说法,正确的有?
- A.它是数据中潜在口味维度← 正确答案
- B.它通常远小于用户和物品数← 正确答案
- C.它等于用户评分的平均值
- D.它是商品在数据库的编号
答案:AB。隐因子是模型从评分中学习到的潜在偏好维度,维度 k 远小于用户数和物品数,用来压缩稀疏矩阵。它不是评分均值,也不是数据库主键。
Q7. 交替最小二乘(ALS)的更新方式描述正确的是?
- A.固定用户向量再优化物品向量← 正确答案
- B.用户物品向量同时随机调整
- C.只更新物品向量不更新用户
- D.完全随机搜索所有隐向量
答案:A。ALS 的核心是坐标下降思想:先固定用户矩阵,把问题变成关于物品矩阵的最小二乘问题;再固定物品矩阵更新用户矩阵,交替迭代直到收敛。
Q8. 稀疏矩阵下正则化最重要的作用是?
- A.抑制隐向量过度拟合观测← 正确答案
- B.提高矩阵乘法运行速度
- C.保证评分一定是正整数
- D.让用户数量自动变少
答案:A。稀疏矩阵对每个用户只有几条观测,模型很容易把这几条记录背下来。正则项惩罚隐向量长度,强迫模型学到更泛化的口味模式,而不是记住个别分数。
Q9. 推荐系统上线评估合理做法有哪些?
- A.先离线评估再做小流量实验← 正确答案
- B.只看整体点击率不分群体
- C.同时监控新用户和新物品指标← 正确答案
- D.发现异常立刻一键回滚← 正确答案
答案:ACD。离线评估是第一步,随后必须用小流量 A/B 实验验证真实效果;只看整体指标会掩盖新用户和新物品的局部恶化;监控发现异常后回滚是标准兜底动作。
Q10. 针对冷启动问题的混合推荐可行做法是?
- A.热门榜给新用户做初步兜底← 正确答案
- B.用物品内容特征填补空白← 正确答案
- C.新用户积累行为后更新模型← 正确答案
- D.把所有评分一律视为缺失
答案:ABC。冷启动阶段用热榜和内容特征顶上,等用户产生行为后逐步切换协同过滤,是工程上最稳妥的路径。把所有评分都视为缺失会彻底丢掉已有信息,显然不可行。