1. SVD++ 是什么?
一句话概括 :SVD++ 是 Yehuda Koren 在 Netflix Prize 竞赛中提出的矩阵分解推荐算法。它在基础 SVD(FunkSVD)的基础上,逐步引入了偏置信息 、隐式反馈 、用户属性 和时间因素,最终形成一个能够充分利用各种信号的强大推荐模型。
重要澄清 :推荐系统中所说的"SVD"并不是线性代数中的正统奇异值分解 ,而是一种基于梯度下降的矩阵分解方法(本质是 FunkSVD / Regularized SVD)。正统 SVD 要求矩阵是稠密的,而推荐场景下的评分矩阵极度稀疏,无法直接做 SVD。因此,推荐领域的"SVD"是伪 SVD,通过优化损失函数来学习隐向量。
2. SVD++ 在推荐系统中的作用
SVD++ 的核心作用是将用户和物品映射到一个 k 维隐空间中,通过用户隐向量和物品隐向量的点积来预测评分或推荐分数。
它的演进过程如下:
| 版本 | 公式 | 核心改进 |
|---|---|---|
| 基础 SVD | r^ui=pu⋅qiT\hat{r}_{ui} = p_u \cdot q_i^Tr^ui=pu⋅qiT | 学习用户和物品隐向量 |
| SVD + 偏置 | r^ui=μ+bi+bu+pu⋅qiT\hat{r}_{ui} = \mu + b_i + b_u + p_u \cdot q_i^Tr^ui=μ+bi+bu+pu⋅qiT | 引入全局均值、物品偏置、用户偏置 |
| SVD++ | r^ui=μ+bi+bu+(pu+∣N(u)∣−0.5∑j∈N(u)xj+∑a∈Auya)⋅qiT\hat{r}{ui} = \mu + b_i + b_u + \left(p_u + |N(u)|^{-0.5} \sum{j \in N(u)} x_j + \sum_{a \in A_u} y_a\right) \cdot q_i^Tr^ui=μ+bi+bu+(pu+∣N(u)∣−0.5∑j∈N(u)xj+∑a∈Auya)⋅qiT | 引入隐式反馈和用户属性 |
| SVD++ + 时间 | 在各项中加入时间衰减或时间区间参数 | 引入时间因素,捕捉用户兴趣漂移 |
3. 增加偏置信息有什么作用?
核心问题:在真实场景中,评分的绝对值受到三类系统性偏差的影响:
- 全局平均分 μ\muμ:整个平台的平均评分水平。
- 物品偏置 bib_ibi:某些物品天生评分高(如经典电影《肖申克的救赎》),某些物品天生评分低。
- 用户偏置 bub_ubu:某些用户打分宽松(总是给高分),某些用户打分严格(总是给低分)。
举例说明:
- 全局平均分 μ=3.0\mu = 3.0μ=3.0
- 《肖申克的救赎》平均比全局高 1 分 → bi=+1.0b_i = +1.0bi=+1.0
- 用户小明比平均严格 0.5 分 → bu=−0.5b_u = -0.5bu=−0.5
- 那么,仅靠偏置预测小明对《肖申克》的评分:3.0+1.0+(−0.5)=3.53.0 + 1.0 + (-0.5) = 3.53.0+1.0+(−0.5)=3.5
作用 :将评分分解为"系统性偏差 + 个性化偏好",让隐向量 pu⋅qiTp_u \cdot q_i^Tpu⋅qiT 只需专注于捕捉用户与物品之间真正的个性化交互,而不是被评分尺度差异干扰。
4. 增加历史行为(隐式反馈)有什么作用?
核心问题 :显式反馈(评分)非常稀疏,大多数用户只看不评。但用户的浏览、点击、收藏等隐式行为却大量存在。
SVD++ 的解决方案:
- 用户有过行为的物品集合 N(u)N(u)N(u),每个物品 jjj 都有一个隐因子向量 xjx_jxj。
- 将这些隐因子向量求和并归一化 ,作为用户兴趣的补充表示:
隐式反馈贡献=∣N(u)∣−0.5∑j∈N(u)xj \text{隐式反馈贡献} = |N(u)|^{-0.5} \sum_{j \in N(u)} x_j 隐式反馈贡献=∣N(u)∣−0.5j∈N(u)∑xj - 同样,用户属性(性别、年龄等)也各有一个隐因子向量 yay_aya,求和后加入用户表示。
作用:
- 缓解冷启动:即使新用户没有评分,也可以通过他的浏览行为预测偏好。
- 增强表达能力:隐式反馈提供了比评分更丰富的用户兴趣信号。
- 提高预测精度:Netflix Prize 中,SVD++ 比基础 SVD 的 RMSE 降低了约 5%。
5. SVD++ 时间因素有什么作用?
核心问题:用户的兴趣和物品的受欢迎程度都随时间变化。
三种时间建模方式:
| 方式 | 做法 | 作用 |
|---|---|---|
| 时间加权 | 对久远的评分赋予更低权重,使其趋近平均值 | 让模型更关注近期行为 |
| 时间区间 | 将时间划分为多个区间,每个区间学习独立的隐因子向量 | 捕捉用户兴趣的阶段变化 |
| 特殊时段 | 对节日、周末等特殊时段单独训练隐因子 | 捕捉周期性模式 |
作用:解决"人是善变的"问题,让推荐更贴近用户当前的真实兴趣。
6. SVD++ 损失函数的作用
完整损失函数 :
L=∑(u,i)∈K(rui−r^ui)2+λ(bu2+bi2+∥pu∥2+∥qi∥2+∥xj∥2+∥ya∥2) L = \sum_{(u,i) \in \mathcal{K}} \left( r_{ui} - \hat{r}_{ui} \right)^2 + \lambda \left( b_u^2 + b_i^2 + \|p_u\|^2 + \|q_i\|^2 + \|x_j\|^2 + \|y_a\|^2 \right) L=(u,i)∈K∑(rui−r^ui)2+λ(bu2+bi2+∥pu∥2+∥qi∥2+∥xj∥2+∥ya∥2)
两部分的作用:
- 预测误差项:控制模型偏差,让预测值尽可能接近真实评分。
- 正则化项:控制模型方差,防止隐向量过大导致过拟合。
正则化系数 λ\lambdaλ 的作用 :λ\lambdaλ 越大,模型越保守(隐向量被压小),泛化能力越强,但可能欠拟合;λ\lambdaλ 越小,模型越激进,拟合能力越强,但可能过拟合。
7. SVD++ 的梯度下降流程
-
准备评分矩阵,每条评分作为训练样本
-
随机初始化所有参数:pu, qi, bu, bi, xj, yap_u,\ q_i,\ b_u,\ b_i,\ x_j,\ y_apu, qi, bu, bi, xj, ya
-
对每条已知评分 (u,i)(u, i)(u,i):
a. 计算预测值:
r^ui=μ+bu+bi+(pu+∣N(u)∣−0.5∑xj+∑ya)⋅qiT \hat{r}_{ui} = \mu + b_u + b_i + \left(p_u + |N(u)|^{-0.5}\sum x_j + \sum y_a\right)\cdot q_i^T r^ui=μ+bu+bi+(pu+∣N(u)∣−0.5∑xj+∑ya)⋅qiT
b. 计算误差:
eui=rui−r^ui e_{ui} = r_{ui} - \hat{r}_{ui} eui=rui−r^ui
c. 计算梯度:
∂L∂bu=−2eui+2λbu∂L∂bi=−2eui+2λbi∂L∂pu=−2euiqi+2λpu∂L∂qi=−2eui(pu+∣N(u)∣−0.5∑xj+∑ya)+2λqi∂L∂xj=−2eui ∣N(u)∣−0.5 qi+2λxj∂L∂ya=−2eui qi+2λya \begin{aligned} \frac{\partial L}{\partial b_u} &= -2e_{ui} + 2\lambda b_u \\ \frac{\partial L}{\partial b_i} &= -2e_{ui} + 2\lambda b_i \\ \frac{\partial L}{\partial p_u} &= -2e_{ui} q_i + 2\lambda p_u \\ \frac{\partial L}{\partial q_i} &= -2e_{ui}\left(p_u + |N(u)|^{-0.5}\sum x_j + \sum y_a\right) + 2\lambda q_i \\ \frac{\partial L}{\partial x_j} &= -2e_{ui}\ |N(u)|^{-0.5}\ q_i + 2\lambda x_j \\ \frac{\partial L}{\partial y_a} &= -2e_{ui}\ q_i + 2\lambda y_a \end{aligned} ∂bu∂L∂bi∂L∂pu∂L∂qi∂L∂xj∂L∂ya∂L=−2eui+2λbu=−2eui+2λbi=−2euiqi+2λpu=−2eui(pu+∣N(u)∣−0.5∑xj+∑ya)+2λqi=−2eui ∣N(u)∣−0.5 qi+2λxj=−2eui qi+2λya
d. 沿负梯度方向更新参数
8. 完整数值推导案例
8.1 数据准备
| 用户 \ 物品 | I1 | I2 | I3 |
|---|---|---|---|
| U1 | 5 | 3 | ? |
| U2 | 4 | ? | 2 |
- 已知评分 :r11=5r_{11}=5r11=5,r12=3r_{12}=3r12=3,r21=4r_{21}=4r21=4,r23=2r_{23}=2r23=2
- 隐向量维度 :k=1k=1k=1(标量,方便手算)
- 学习率 :η=0.01\eta=0.01η=0.01
- 正则化系数 :λ=0.1\lambda=0.1λ=0.1
- 全局平均分 :μ=(5+3+4+2)/4=3.5\mu = (5+3+4+2)/4 = 3.5μ=(5+3+4+2)/4=3.5
隐式反馈设定:
- 用户 U1 有过行为的物品集合 N(U1)={I1,I2}N(U1) = \{I1, I2\}N(U1)={I1,I2}(已评分的物品)
- 用户 U2 有过行为的物品集合 N(U2)={I1,I3}N(U2) = \{I1, I3\}N(U2)={I1,I3}
- ∣N(U1)∣=2|N(U1)| = 2∣N(U1)∣=2,∣N(U2)∣=2|N(U2)| = 2∣N(U2)∣=2,归一化因子 ∣N(u)∣−0.5=0.707|N(u)|^{-0.5} = 0.707∣N(u)∣−0.5=0.707
8.2 随机初始化
| 参数 | 初始值 |
|---|---|
| bU1b_{U1}bU1 | 0.0 |
| bU2b_{U2}bU2 | 0.0 |
| bI1b_{I1}bI1 | 0.0 |
| bI2b_{I2}bI2 | 0.0 |
| bI3b_{I3}bI3 | 0.0 |
| pU1p_{U1}pU1 | 0.1 |
| pU2p_{U2}pU2 | 0.2 |
| qI1q_{I1}qI1 | 0.3 |
| qI2q_{I2}qI2 | 0.4 |
| qI3q_{I3}qI3 | 0.5 |
| xI1x_{I1}xI1 | 0.1 |
| xI2x_{I2}xI2 | 0.2 |
| xI3x_{I3}xI3 | 0.1 |
注意 :为了简化,这里假设没有用户属性 yay_aya(即 Au=∅A_u = \emptysetAu=∅)。
8.3 第 1 步:计算用户 U1 对物品 I1 的预测值
用户 U1 的隐式反馈贡献 :
Imp(U1)=∣N(U1)∣−0.5∑j∈N(U1)xj=0.707×(xI1+xI2)=0.707×(0.1+0.2)=0.2121 \text{Imp}(U1) = |N(U1)|^{-0.5} \sum_{j \in N(U1)} x_j = 0.707 \times (x_{I1} + x_{I2}) = 0.707 \times (0.1 + 0.2) = 0.2121 Imp(U1)=∣N(U1)∣−0.5j∈N(U1)∑xj=0.707×(xI1+xI2)=0.707×(0.1+0.2)=0.2121
用户 U1 的完整隐向量 :
p~U1=pU1+Imp(U1)=0.1+0.2121=0.3121 \tilde{p}{U1} = p{U1} + \text{Imp}(U1) = 0.1 + 0.2121 = 0.3121 p~U1=pU1+Imp(U1)=0.1+0.2121=0.3121
预测值 :
r^11=μ+bU1+bI1+p~U1⋅qI1=3.5+0+0+0.3121×0.3=3.5+0.0936=3.5936 \hat{r}{11} = \mu + b{U1} + b_{I1} + \tilde{p}{U1} \cdot q{I1} = 3.5 + 0 + 0 + 0.3121 \times 0.3 = 3.5 + 0.0936 = 3.5936 r^11=μ+bU1+bI1+p~U1⋅qI1=3.5+0+0+0.3121×0.3=3.5+0.0936=3.5936
误差 :
e11=r11−r^11=5−3.5936=1.4064 e_{11} = r_{11} - \hat{r}_{11} = 5 - 3.5936 = 1.4064 e11=r11−r^11=5−3.5936=1.4064
8.4 第 2 步:计算梯度并更新参数
更新偏置 bU1b_{U1}bU1 :
bU1←bU1+η(e11−λbU1)=0+0.01×(1.4064−0)=0.014064 b_{U1} \leftarrow b_{U1} + \eta (e_{11} - \lambda b_{U1}) = 0 + 0.01 \times (1.4064 - 0) = 0.014064 bU1←bU1+η(e11−λbU1)=0+0.01×(1.4064−0)=0.014064
更新偏置 bI1b_{I1}bI1 :
bI1←0+0.01×(1.4064−0)=0.014064 b_{I1} \leftarrow 0 + 0.01 \times (1.4064 - 0) = 0.014064 bI1←0+0.01×(1.4064−0)=0.014064
更新用户隐向量 pU1p_{U1}pU1 :
pU1←pU1+η(e11qI1−λpU1)=0.1+0.01×(1.4064×0.3−0.1×0.1) p_{U1} \leftarrow p_{U1} + \eta (e_{11} q_{I1} - \lambda p_{U1}) = 0.1 + 0.01 \times (1.4064 \times 0.3 - 0.1 \times 0.1) pU1←pU1+η(e11qI1−λpU1)=0.1+0.01×(1.4064×0.3−0.1×0.1)
=0.1+0.01×(0.42192−0.01)=0.1+0.004119=0.104119 = 0.1 + 0.01 \times (0.42192 - 0.01) = 0.1 + 0.004119 = 0.104119 =0.1+0.01×(0.42192−0.01)=0.1+0.004119=0.104119
更新物品隐向量 qI1q_{I1}qI1 :
qI1←qI1+η(e11p~U1−λqI1)=0.3+0.01×(1.4064×0.3121−0.1×0.3) q_{I1} \leftarrow q_{I1} + \eta (e_{11} \tilde{p}{U1} - \lambda q{I1}) = 0.3 + 0.01 \times (1.4064 \times 0.3121 - 0.1 \times 0.3) qI1←qI1+η(e11p~U1−λqI1)=0.3+0.01×(1.4064×0.3121−0.1×0.3)
=0.3+0.01×(0.4389−0.03)=0.3+0.004089=0.304089 = 0.3 + 0.01 \times (0.4389 - 0.03) = 0.3 + 0.004089 = 0.304089 =0.3+0.01×(0.4389−0.03)=0.3+0.004089=0.304089
更新隐式反馈向量 xI1x_{I1}xI1 (因为 I1 在 N(U1)N(U1)N(U1) 中):
xI1←xI1+η(e11⋅∣N(U1)∣−0.5⋅qI1−λxI1) x_{I1} \leftarrow x_{I1} + \eta \left( e_{11} \cdot |N(U1)|^{-0.5} \cdot q_{I1} - \lambda x_{I1} \right) xI1←xI1+η(e11⋅∣N(U1)∣−0.5⋅qI1−λxI1)
=0.1+0.01×(1.4064×0.707×0.3−0.1×0.1) = 0.1 + 0.01 \times (1.4064 \times 0.707 \times 0.3 - 0.1 \times 0.1) =0.1+0.01×(1.4064×0.707×0.3−0.1×0.1)
=0.1+0.01×(0.2983−0.01)=0.1+0.002883=0.102883 = 0.1 + 0.01 \times (0.2983 - 0.01) = 0.1 + 0.002883 = 0.102883 =0.1+0.01×(0.2983−0.01)=0.1+0.002883=0.102883
更新隐式反馈向量 xI2x_{I2}xI2 (同样在 N(U1)N(U1)N(U1) 中,梯度按相同方式传播):
xI2←0.2+0.01×(1.4064×0.707×0.3−0.1×0.2) x_{I2} \leftarrow 0.2 + 0.01 \times (1.4064 \times 0.707 \times 0.3 - 0.1 \times 0.2) xI2←0.2+0.01×(1.4064×0.707×0.3−0.1×0.2)
=0.2+0.01×(0.2983−0.02)=0.2+0.002783=0.202783 = 0.2 + 0.01 \times (0.2983 - 0.02) = 0.2 + 0.002783 = 0.202783 =0.2+0.01×(0.2983−0.02)=0.2+0.002783=0.202783
注意 :xI3x_{I3}xI3 不在 N(U1)N(U1)N(U1) 中,因此本次更新不涉及 xI3x_{I3}xI3。
8.5 第 3 步:对下一条评分重复更新
对 r12=3r_{12}=3r12=3,r21=4r_{21}=4r21=4,r23=2r_{23}=2r23=2 依次执行相同的预测、误差计算、梯度更新过程。每一轮(epoch)遍历所有已知评分后,参数会逐渐收敛。
8.6 预测问号:U1 对 I3 的评分
假设经过 500 轮迭代后,参数收敛为:
| 参数 | 收敛值 |
|---|---|
| bU1b_{U1}bU1 | 0.8 |
| bI3b_{I3}bI3 | -0.3 |
| pU1p_{U1}pU1 | 0.5 |
| qI3q_{I3}qI3 | 0.4 |
| xI1x_{I1}xI1 | 0.3 |
| xI2x_{I2}xI2 | 0.4 |
用户 U1 的隐式反馈贡献 :
Imp(U1)=0.707×(xI1+xI2)=0.707×(0.3+0.4)=0.495 \text{Imp}(U1) = 0.707 \times (x_{I1} + x_{I2}) = 0.707 \times (0.3 + 0.4) = 0.495 Imp(U1)=0.707×(xI1+xI2)=0.707×(0.3+0.4)=0.495
完整用户向量 :
p~U1=0.5+0.495=0.995 \tilde{p}_{U1} = 0.5 + 0.495 = 0.995 p~U1=0.5+0.495=0.995
预测 U1 对 I3 的评分 :
r^13=μ+bU1+bI3+p~U1⋅qI3=3.5+0.8+(−0.3)+0.995×0.4 \hat{r}{13} = \mu + b{U1} + b_{I3} + \tilde{p}{U1} \cdot q{I3} = 3.5 + 0.8 + (-0.3) + 0.995 \times 0.4 r^13=μ+bU1+bI3+p~U1⋅qI3=3.5+0.8+(−0.3)+0.995×0.4
=4.0+0.398=4.398 = 4.0 + 0.398 = 4.398 =4.0+0.398=4.398
所以,U1 对 I3 的预测评分为 4.4,可以推荐给 U1。
9. 总结:SVD++ 各组件的作用
| 组件 | 作用 | 在案例中的体现 |
|---|---|---|
| 基础 SVD | 学习用户和物品隐向量 | pU1⋅qI1p_{U1} \cdot q_{I1}pU1⋅qI1 |
| 偏置信息 | 捕捉评分尺度差异 | μ+bU1+bI1\mu + b_{U1} + b_{I1}μ+bU1+bI1 |
| 隐式反馈 | 利用浏览/点击行为补充用户兴趣 | 0.707×(xI1+xI2)0.707 \times (x_{I1} + x_{I2})0.707×(xI1+xI2) |
| 用户属性 | 缓解冷启动 | yay_aya(本例省略) |
| 时间因素 | 捕捉兴趣漂移 | 本例未涉及,但可通过时间区间实现 |
| 损失函数 | 平衡拟合能力与泛化能力 | 预测误差 + L2 正则化 |
| 梯度下降 | 迭代优化所有参数 | 逐样本更新 b,p,q,x,yb, p, q, x, yb,p,q,x,y |
一句话记住 SVD++:
SVD++ = 基础矩阵分解 + 偏置信息 + 隐式反馈 + 用户属性 + 时间因素。它把能利用的信号全部融入一个统一的梯度下降框架中,是 Netflix Prize 时代最强大的推荐算法之一。