矩阵分解(Matrix Factorization)是将高维稀疏的评分矩阵R∈ℝ^(m×n)分解为两个低维稠密矩阵的乘积。这是Netflix Prize竞赛中获胜的核心方法,至今仍是推荐系统的重要基石。
R ≈ P × QT
P∈ℝ^(m×k)为用户隐因子矩阵,Q∈ℝ^(n×k)为物品隐因子矩阵,k<<min(m,n)
| 方法 | 优化方式 | 优点 | 缺点 |
|---|---|---|---|
| SVD | 奇异值分解(解析解) | 数学最优、计算快速 | 需填充缺失值、不适用稀疏数据 |
| SGD-MF | 随机梯度下降 | 天然处理缺失值、灵活加特征 | 串行计算、需调学习率 |
| ALS | 交替最小二乘 | 可并行化、适合隐式反馈 | 每轮需解线性方程组 |
加入偏置项可以捕捉用户和物品的全局偏差:有些用户总是打高分(宽松),有些物品天然更受欢迎。偏置项将这部分信息从隐因子中分离,让隐因子专注于刻画个性化偏好。
min Σ(r_ui - p_u·q_i)² + λ(||p_u||² + ||q_i||²)
r̂_ui = μ + b_u + b_i + p_u·q_i
μ: 全局均值, b_u: 用户偏差, b_i: 物品偏差
R = UΣVT, 取前k个奇异值
P_u = (Q_uTQ_u + λI)-1Q_uTr_u
import numpy as np
np.random.seed(42)
n_u,n_i=20,15;K=5
true_P=np.random.randn(n_u,K);true_Q=np.random.randn(n_i,K)
true_R=true_P@true_Q.T+0.5*np.random.randn(n_u,n_i)
mask=np.random.random((n_u,n_i))>0.5;R=np.where(mask,true_R,0)
print("="*60+"\n矩阵分解推荐系统\n"+"="*60)
print(f"矩阵:{n_u}x{n_i} 观测率:{mask.sum()/(n_u*n_i):.2%}")
# SVD
Rm=R[mask].mean();Rf=np.where(mask,R,Rm)
U,S,Vt=np.linalg.svd(Rf,full_matrices=False)
Rsvd=U[:,:K]@np.diag(S[:K])@Vt[:K,:]
rs=np.sqrt(np.mean((Rsvd[mask]-R[mask])**2));print(f"\nSVD(K={K}) RMSE:{rs:.4f}")
# SGD-MF with bias
P=np.random.randn(n_u,K)*.01;Q=np.random.randn(n_i,K)*.01
bu=np.zeros(n_u);bi=np.zeros(n_i);mu=R[mask].mean()
lr=.005;reg=.02;obs=list(zip(*np.where(mask)))
for ep in range(200):
np.random.shuffle(obs)
for u,i in obs:
pred=mu+bu[u]+bi[i]+P[u]@Q[i];err=R[u,i]-pred
bu[u]+=lr*(err-reg*bu[u]);bi[i]+=lr*(err-reg*bi[i])
P[u]+=lr*(err*Q[i]-reg*P[u]);Q[i]+=lr*(err*P[u]-reg*Q[i])
if(ep+1)%50==0:
rmse=np.sqrt(np.mean([(mu+bu[u]+bi[i]+P[u]@Q[i]-R[u,i])**2 for u,i in obs]))
print(f" SGD Epoch{ep+1}:RMSE={rmse:.4f}")
Rsgd=mu+bu[:,None]+bi[None,:]+P@Q.T;rs2=np.sqrt(np.mean((Rsgd[mask]-R[mask])**2))
print(f"SGD最终RMSE:{rs2:.4f}")
# ALS
Pa=np.random.randn(n_u,K)*.01;Qa=np.random.randn(n_i,K)*.01;ra=.1
for it in range(20):
for u in range(n_u):
idx=np.where(mask[u])[0]
if len(idx)==0:continue
Qu=Qa[idx];A=Qu.T@Qu+ra*np.eye(K);b=Qu.T@R[u,idx];Pa[u]=np.linalg.solve(A,b)
for i in range(n_i):
idx=np.where(mask[:,i])[0]
if len(idx)==0:continue
Pi=Pa[idx];A=Pi.T@Pi+ra*np.eye(K);b=Pi.T@R[idx,i];Qa[i]=np.linalg.solve(A,b)
if(it+1)%5==0:
rmse=np.sqrt(np.mean((Pa@Qa.T[mask]-R[mask])**2));print(f" ALS Iter{it+1}:RMSE={rmse:.4f}")
Rals=Pa@Qa.T;ra2=np.sqrt(np.mean((Rals[mask]-R[mask])**2));print(f"ALS最终RMSE:{ra2:.4f}")
# 推荐对比
t=0;unrated=np.where(~mask[t])[0]
st=sorted([(i,Rsgd[t,i])for i in unrated],key=lambda x:-x[1])[:5]
at=sorted([(i,Rals[t,i])for i in unrated],key=lambda x:-x[1])[:5]
print(f"\nU1 SGD推荐:{[(f'I{i}',f'{s:.2f}')for i,s in st]}")
print(f"U1 ALS推荐:{[(f'I{i}',f'{s:.2f}')for i,s in at]}")
print(f"\n{'方法':<10}{'RMSE':<10}\n{'-'*20}")
print(f"{'SVD':<10}{rs:.4f}\n{'SGD-MF':<10}{rs2:.4f}\n{'ALS-MF':<10}{ra2:.4f}")
print("\n隐因子(前2维):")
for u in range(5):print(f" U{u+1}:({P[u,0]:.2f},{P[u,1]:.2f})")
for i in range(5):print(f" I{i+1}:({Q[i,0]:.2f},{Q[i,1]:.2f})")
# K值影响
print("\nK值对RMSE影响:")
for k_test in [2,5,8,10]:
P2=np.random.randn(n_u,k_test)*.01;Q2=np.random.randn(n_i,k_test)*.01
bu2=np.zeros(n_u);bi2=np.zeros(n_i)
for ep in range(100):
np.random.shuffle(obs)
for u,i in obs:
pred=mu+bu2[u]+bi2[i]+P2[u]@Q2[i];err=R[u,i]-pred
bu2[u]+=lr*(err-reg*bu2[u]);bi2[i]+=lr*(err-reg*bi2[i])
P2[u]+=lr*(err*Q2[i]-reg*P2[u]);Q2[i]+=lr*(err*P2[u]-reg*Q2[i])
rmse=np.sqrt(np.mean([(mu+bu2[u]+bi2[i]+P2[u]@Q2[i]-R[u,i])**2 for u,i in obs]))
print(f" K={k_test}:RMSE={rmse:.4f}")
✅ 验证通过
本课代码涵盖了从数据生成、模型训练到效果评估的完整流程。以下是关键步骤的详细解读:
代码设计原则:简洁可读、关键步骤有注释、结果可复现(固定随机种子)。
| 组件 | 版本 | 说明 |
|---|---|---|
| Python | 3.8+ | 推荐3.10版本 |
| NumPy | 1.21+ | 核心数值计算库 |
| SciPy | 1.7+ | 统计检验(部分课程) |
隐因子向量虽无明确语义,但往往对应可解释维度:
K通常取20-200,太小欠拟合,太大过拟合。可通过交叉验证选择最优K。
本课是第3课:矩阵分解(SVD / ALS),属于基础方法阶段。我们系统学习了本课的核心概念、数学原理和代码实现,并通过实机运行验证了算法的正确性。
练习1:调整K∈{2,5,8,10},画RMSE随K变化的曲线
练习2:去掉偏置项(bu,bi)对比有无偏置的RMSE差异
练习3:实现加权ALS用于隐式反馈场景
练习4:将SGD学习率从0.005改为0.01/0.001,观察收敛速度
| 公司 | 场景 | 核心技术 |
|---|---|---|
| 字节跳动 | 抖音/TikTok | 多目标排序+实时特征+双塔召回 |
| 阿里巴巴 | 淘宝推荐 | DIN/DIEN序列模型+MIND多兴趣 |
| 腾讯 | 微信看一看 | DeepFM+图神经网络召回 |
| 美团 | 本地生活推荐 | 多场景多目标+时空特征 |
| Netflix | 视频推荐 | 矩阵分解+深度学习混合 |
✅ 掌握SVD/SGD-MF/ALS三种矩阵分解方法
✅ 理解偏置项和正则化的作用
✅ 完成三种方法的RMSE评估对比
✅ 理解隐因子的物理含义与K值选择