推荐系统协同过滤:用户-物品矩阵分解
推荐系统协同过滤:用户-物品矩阵分解完全指南
什么是协同过滤与矩阵分解
协同过滤是推荐系统中最经典的技术之一,它的核心思想是利用群体智慧——如果两个用户在过去对某些物品的喜好相似,那么他们在未来对其他物品的偏好也可能相似。而矩阵分解则是实现协同过滤的一种强大方法,它通过将稀疏的用户-物品交互矩阵分解为低维的用户隐向量矩阵和物品隐向量矩阵,挖掘出用户与物品之间潜在的关联模式。
本教程将从零开始,带你深入理解基于矩阵分解的协同过滤模型,包括算法原理、数学推导、主流实现方式以及实战中的关键技巧。
用户-物品交互矩阵
推荐系统的一切始于数据。在协同过滤中,我们通常将用户与物品的交互记录整理成一个用户-物品矩阵。
假设有 m 个用户和 n 个物品,这个矩阵记为 R,它的维度是 m × n。矩阵中的每个元素 R_{ui} 表示用户 u 对物品 i 的反馈,可以是显式反馈(如评分1-5)或隐式反馈(如点击、购买、播放次数)。
在真实场景中,用户只会与极少部分物品产生交互,导致矩阵 R 极端稀疏(通常99%以上的元素为空)。直接在这个稀疏矩阵上计算用户或物品相似度会遇到严重的数据稀疏问题,导致推荐结果不可靠。
矩阵分解就是为了解决这一核心挑战而生的。
矩阵分解的核心思想
矩阵分解的目标是将原始的稀疏矩阵 R 近似分解为两个低秩矩阵的乘积:
R ≈ P × Q^T
其中:
- P 是
m × k的用户隐向量矩阵,每一行p_u代表用户u在k个潜在因子上的偏好。 - Q 是
n × k的物品隐向量矩阵,每一行q_i代表物品i在同样k个潜在因子上的属性。 k是隐向量的维度,远小于m和n。
每个用户对物品的预测评分可以通过对应隐向量的内积得到:
ŕ_{ui} = p_u · q_i^T = ∑_{f=1}^{k} p_{uf} × q_{if}
这些潜在因子(隐因子)没有直接的业务含义,但经过训练后,它们能够自动编码出用户的偏好模式与物品的属性特征。例如,在电影推荐中,某个隐因子可能代表“动作片程度”,另一个代表“喜剧程度”,用户向量在该因子上的值体现了用户对该类型的喜好,物品向量中的值则体现了电影在该类型上的强度。
目标函数与优化
为了学习出合理的P和Q矩阵,我们需要定义一个优化目标。最常用的目标函数是最小化重构误差加上正则化项,以防止过拟合:
min_{P,Q} ∑_{(u,i)∈K} (R_{ui} - p_u · q_i^T)^2 + λ (||p_u||^2 + ||q_i||^2)
其中:
K是所有已知评分的用户-物品对集合。λ是正则化系数,控制隐向量的大小。
这个目标函数要求学习出的隐向量能够尽量准确地还原已有的评分,同时对向量长度施加惩罚,使得模型更加稳定。
主要分解算法:ALS与SGD
解决上述目标函数常用的方法有两种:交替最小二乘法(ALS)和随机梯度下降(SGD)。
交替最小二乘法(ALS)
ALS的核心思路是固定一个矩阵,优化另一个矩阵。由于目标函数对于P和Q分别是二次的,因此每一步求解都是简单的线性回归问题,可以求出解析解。
ALS流程:
- 随机初始化用户矩阵P和物品矩阵Q。
- 固定Q,逐用户优化P。对于每个用户u,收集该用户评过分的所有物品的当前q向量,求解一个
k × k的线性方程组,得到新的p_u。 - 固定P,逐物品优化Q。对于每个物品i,收集评过该物品的所有用户的p向量,求解线性方程组更新
q_i。 - 重复步骤2和3,直到收敛。
ALS天然支持并行计算,尤其适合在大规模数据上运行,Spark MLlib中的推荐模块就默认采用ALS。另外,ALS对隐式反馈也非常友好,可以通过置信度加权等方式扩展。
随机梯度下降(SGD)
SGD是另一种常用优化方法,每次使用一个训练样本(一个已知评分)来更新参数。
对于每个评分 R_{ui},计算预测误差:
e_{ui} = R_{ui} - p_u · q_i^T
然后沿负梯度方向更新:
p_u ← p_u + α (e_{ui} · q_i - λ p_u)
q_i ← q_i + α (e_{ui} · p_u - λ q_i)
其中α是学习率。SGD实现简单、收敛快,但需要仔细调整学习率和正则化参数,且迭代顺序对结果有一定影响。
从矩阵分解到协同过滤推荐
模型训练完成后,我们获得了所有用户和物品的隐向量。此时,推荐可以灵活进行:
- 为某个用户生成Top-N推荐:计算该用户向量与所有物品向量的内积,过滤掉已交互物品,取分数最高的N个物品。
- 物品相似度计算:直接计算物品隐向量之间的余弦相似度,用于 “看了又看” 等关联推荐。
- 用户相似度计算:同样可以根据隐向量计算用户之间的相似度。
隐向量包含的潜在语义使得这种相似度计算远比基于原始稀疏向量的方法更稳定、更准确。
处理隐式反馈的变体
在很多场景中,我们只有隐式反馈(如是否购买、观看时长),没有显式评分。此时可以使用加权交替最小二乘法(Weighted ALS) 或置信度加权矩阵分解。
核心思想是,对于有交互的行为(R_{ui} = 1)赋予高置信度,对于没有交互的行为(R_{ui} = 0)赋予低置信度。目标函数变为:
min_{P,Q} ∑_{u,i} C_{ui} (R_{ui} - p_u · q_i^T)^2 + λ (∑||p_u||^2 + ∑||q_i||^2)
其中 C_{ui} 往往设为 1 + α × 交互强度,或者简单的二值权重 C_{ui} = 1 如果交互,C_{ui} = c_0(一个较小的常数)否则。
这种方式让模型能从海量未交互数据中学习到用户的负面偏好,是工业界处理隐式反馈的主流方案。
融入用户和物品偏置项
基础矩阵分解仅用隐向量内积预测评分,未考虑用户和物品本身的偏差。例如,有些用户习惯打高分,有些物品天生受欢迎。加入偏置项可以显著提高预测准确度:
ŕ_{ui} = μ + b_u + b_i + p_u · q_i^T
其中:
- μ 是全局平均评分。
- b_u 是用户偏置(用户评分习惯与平均水平的偏差)。
- b_i 是物品偏置(物品被评分的高低与平均水平的偏差)。
偏置项同样可以通过SGD或ALS学习得到。这是Netflix Prize竞赛中经典模型FunkSVD的重要组成部分。
评估与调优实践
离线评估指标
对于评分预测任务,常用均方根误差(RMSE) 和平均绝对误差(MAE);对于Top-N推荐,常用精确率(Precision)、召回率(Recall)和平均倒数排名(MRR)。
采用时间切分或随机切分数据集,确保训练集和测试集无时间穿越,评估结果才具有参考价值。
关键超参数调优
- 隐因子维度k:太小会欠拟合,太大会过拟合且计算量增大。一般在20~200之间网格搜索。
- 正则化系数λ:防止过拟合的关键,可以从0.01、0.1、1等值开始尝试。
- 迭代次数:监控验证集误差,当误差不再下降或开始上升时停止。
- 学习率(仅SGD):过大不收敛,过小收敛慢,常用0.001~0.1。
冷启动问题
矩阵分解对新用户和新物品存在天然缺陷,因为它们的隐向量无法从交互数据中学得。应对策略:
- 利用用户注册信息和物品内容属性构建混合模型。
- 新物品推荐时,基于内容的相似度先做初步推荐,积累一定交互后再纳入分解。
- 使用人口统计特征或群体平均向量作为初始化。
基于Python的实现要点
以下是一个使用Surprise库实现SVD分解的简化示例,帮助你快速上手:
from surprise import SVD, Dataset, Reader, accuracy
from surprise.model_selection import train_test_split
# 数据格式:user item rating
reader = Reader(line_format='user item rating', sep=',')
data = Dataset.load_from_file('ratings.csv', reader=reader)
trainset, testset = train_test_split(data, test_size=0.2)
algo = SVD(n_factors=100, n_epochs=20, lr_all=0.005, reg_all=0.02)
algo.fit(trainset)
predictions = algo.test(testset)
print("RMSE:", accuracy.rmse(predictions))
# 预测某用户对物品的评分
uid, iid = '196', '302'
pred = algo.predict(uid, iid)
print(pred.est)
对于更大规模的数据,可使用Spark MLlib中的ALS,或使用Implicit库处理隐式反馈矩阵分解。
矩阵分解的优缺点总结
优点:
- 能够处理稀疏矩阵,泛化能力强。
- 模型空间占用小,在线推理速度快(仅需向量内积)。
- 易于添加偏置、时间动态等扩展。
- 隐向量可解释性在某些场合尚可接受,且能直接用于物品相似度计算。
缺点:
- 冷启动问题严重,依赖交互历史。
- 难以建模复杂的非线性交互关系(后来被神经网络推荐模型所超越)。
- 训练过程相对较慢,尤其在大规模数据上需要分布式支持。
- 隐因子含义不明确,可解释性不如基于内容的推荐。
尽管深度学习推荐模型不断发展,矩阵分解凭借其简洁、高效、可解释和易于部署的特性,仍然是推荐系统工程落地中不可或缺的基础方法。掌握其原理与变体,是深入学习推荐系统的重要基石。
最后更新:2025年 | 免费在线教程 - 用最清晰的方式带你掌握核心技术