推荐算法Chapter2.2 矩阵分解模型


1.矩阵分解

1.模型结构

1.1 模型核心原理

矩阵分解的核心思想是:每一个用户和每一个物品,都可以映射到一个低维的隐空间(Latent Space)中。

假设我们有 $m$ 个用户和 $n$ 个物品,交互矩阵为 $R \in \mathbb{R}^{m \times n}$。
由于用户不可能消费所有物品,$R$ 是极度稀疏的。我们将其分解为两个稠密的低维矩阵:

  • 用户矩阵 $P \in \mathbb{R}^{m \times k}$:每一行 $p_u$ 是用户 $u$ 的隐向量。
  • 物品矩阵 $Q \in \mathbb{R}^{n \times k}$:每一行 $q_i$ 是物品 $i$ 的隐向量。

预测公式:

其中 $k$ 是隐向量的维度(通常比 $m, n$ 小得多),代表了潜在的特征(如:电影的导演、风格、演员等,虽然这些特征是模型自动学习出的,并不一定具有直观语义)。

训练流程:如何学习 $P$ 和 $Q$

矩阵分解的训练本质上是一个优化问题,目标是让预测评分 $\hat{r}{ui}$ 尽可能接近真实评分 $r{ui}$。

第一步:初始化

随机初始化 $P$ 和 $Q$ 矩阵中的元素(通常服从均值为 0、方差较小的正态分布)。

第二步:定义损失函数

使用带有 L2 正则化的平方损失函数:

第三步:选择优化算法进行迭代

常用的优化方法有两种:

1. 随机梯度下降 (SGD)
  • 流程: 遍历每一个已知的评分 $r{ui}$,计算误差 $e{ui} = r{ui} - \hat{r}{ui}$,然后根据梯度更新参数:
  • 优点:实现简单,速度快,适合处理超大规模数据。
2. 交替最小二乘法 (ALS)
  • 流程:由于 $P$ 和 $Q$ 同时变化时目标函数不是凸的,但如果固定 $Q$ 更新 $P$,或者固定 $P$ 更新 $Q$,问题就变成了线性最小二乘问题。
    • 固定 $Q$,利用最小二乘法更新 $P$。
    • 固定 $P$,利用最小二乘法更新 $Q$。
  • 优点:适合并行化计算(Spark 默认实现),处理隐式反馈效果好。

1.2 损失函数表达式

在矩阵分解(Matrix Factorization)中,最常用的带 L2 正则化的平方损失函数为:

参数含义:

  • $r_{ui}$:用户 $u$ 对物品 $i$ 的真实评分(Label)。
  • $q_i^T p_u$:模型预估评分(Prediction)。
  • $\lambda$:正则化系数(Hyperparameter),控制惩罚力度。
  • $| \cdot |^2$:L2 范数(每个元素的平方和)。

2. 正则化项的作用

  1. 抑制过拟合 (Overfitting)
    • 推荐数据极度稀疏,模型极易学习到噪声。
    • L2 正则化通过惩罚大的参数值,降低模型复杂度,使模型不至于为了拟合个别样本而让向量波动剧烈。
  2. 提升数值稳定性
    • 在梯度下降过程中,防止权重更新过大导致梯度爆炸。
    • 使目标函数变成严格凸函数(Strictly Convex),更容易收敛到稳定的局部最优解。
  3. 解决多重共线性
    • 当特征之间存在高度相关性时,正则化能有效约束参数空间。

3. L1 与 L2 正则化的对比

特性 L1 正则化 (Lasso) L2 正则化 (Ridge)
惩罚项 $\lambda \sum w_i $ $\lambda \sum w_i^2$
效果 倾向于产生稀疏解(部分权重为0) 倾向于产生稠密小值解
用途 特征选择(自动剔除无效特征) 防止过拟合,保证模型稳定性
贝叶斯先验 拉普拉斯分布 (Laplace) 高斯分布 (Gaussian)
为什么是这样

从贝叶斯统计学的视角来看,正则化项本质上是我们在观测数据之前,对模型参数 $\theta$ 所抱有的先验假设 (Prior Assumption)

1. 概率论背景:最大后验概率 (MAP)

模型学习的目标是最大化后验概率 $P(\theta|D)$。根据对数变换:

2. L2 正则化 <=> 高斯先验 (Gaussian Prior)

  • 假设:每个参数 $w_i$ 独立同分布地服从 $N(0, \sigma^2)$。
  • 推导: 高斯概率密度 取负对数得到
  • 物理意义:高斯分布的曲线在 0 附近是平滑的。它告诉模型:参数值不应该太大,但它们可以均匀地分布在 0 附近,这导致了参数的整体缩小 (Weight Decay)

3. L1 正则化 <=> 拉普拉斯先验 (Laplacian Prior)

  • 假设:每个参数 $w_i$ 独立同分布地服从 $Laplace(0, b)$。
  • 推导: 拉普拉斯概率密度 取负对数得到
  • 物理意义:拉普拉斯分布在 $w=0$ 处有一个非常尖的峰(导数不连续)。这意味着模型有极高的概率预设参数就是 0,从而促使模型产生稀疏性 (Sparsity)

4. 拓展:如何选择 $\lambda$?

  • 交叉验证 (Cross-Validation):在验证集上观察不同 $\lambda$ 下的 RMSE/MAE。
  • 经验法则
    • $\lambda$ 太小 $\rightarrow$ 模型依然过拟合(训练集效果好,测试集差)。
    • $\lambda$ 太大 $\rightarrow$ 模型欠拟合(训练集和测试集效果都差)。

5. 工业实践演进

  • Dropout:深度学习时代最常用的正则化手段,通过随机关闭神经元实现。
  • Early Stopping:在验证集性能不再提升时提前停止训练。
  • Embedding Normalization:在双塔模型中,对输出的向量进行 $L_2$ 归一化,使其映射到单位球面上,这可以看作是一种隐式的硬正则化。

2.FunkSVD 与传统 SVD

1. 传统 SVD 的“数学困境”

在数学定义中,奇异值分解(Singular Value Decomposition)将矩阵 $M$ 分解为 $U\Sigma V^T$。

  • 硬性要求:矩阵 $M$ 必须是稠密(Dense)的。
  • 推荐系统的现实:用户-物品矩阵 $R$ 极其稀疏(99% 为空)。
  • 后果:为了使用传统 SVD,你必须对缺失值进行填充(Imputation),如填充 0 或均值。这会导致矩阵体积爆炸,且引入的伪造数据会严重干扰预测精度。

2. FunkSVD 的“工程智慧”

由 Simon Funk 提出,它跳出了“分解”的数学框架,转而使用“拟合”的机器学习框架。

  • 核心逻辑:不再追求完美的数学分解,而是定义一个损失函数,只看已有的评分。
  • 预测公式:$\hat{r}_{ui} = p_u \cdot q_i^T$
  • 损失函数:
    注意:求和只针对 $\text{Observed}$(已观测到的数据),这完美解决了稀疏性问题。
训练过程

FunkSVD 的训练是一个基于 SGD(随机梯度下降) 的迭代过程,主要分为以下四个步骤:

第一步:参数初始化 (Initialization)

  • 随机初始化矩阵 $P$ 和 $Q$。
  • 通常使用均值为 0、方差较小的正态分布(如 $N(0, 0.1)$)。初始值不宜过大,否则内积结果容易爆炸,导致模型不收敛。

第二步:定义损失函数 (Loss Function)

  • 目标:让预测评分尽可能接近真实评分,同时防止过拟合。
  • 公式

    核心注意点:求和符号 $\sum$ 只针对已有的(非空)评分数据进行计算。

第三步:执行随机梯度下降 (Optimization via SGD)

  • 流程:遍历训练集中的每一个三元组 $(u, i, r_{ui})$。
  • 计算误差:$e{ui} = r{ui} - \hat{r}_{ui}$。
  • 梯度更新:根据损失函数对参数求偏导,得到更新公式: 其中 $\eta$ 是学习率(Learning Rate)。

第四步:收敛与存储

  • 重复迭代(Epochs),直到损失函数不再下降或达到预设次数。
  • 训练完成后,丢弃原始稀疏矩阵,只保留稠密的 $P$ 和 $Q$ 矩阵。

3.知识点讲解:随机梯度下降(SGD)优化矩阵分解

矩阵分解(MF)的训练目标是找到最优的用户隐向量 $p_u$ 和物品隐向量 $q_i$。由于目标函数通常不是简单的凸函数,我们采用随机梯度下降(SGD)这一迭代算法来寻找局部最优解。


1. 损失函数定义 (Loss Function)

为了保证模型既能拟合训练数据,又具备泛化能力,我们定义单个样本 $(u, i)$ 的损失函数如下:

其中:

  • $r{ui}$ 是真实评分,$\hat{r}{ui} = p_u \cdot q_i^T$ 是模型预测评分。
  • $\lambda (|p_u|^2 + |q_i|^2)$ 是 L2 正则化项,防止隐向量数值过大导致过拟合。

2. 参数更新公式推导 (Derivation)

我们需要通过求导找到损失函数下降最快的方向(负梯度方向)。

第一步:求偏导数

利用链式法则,分别对变量 $p_u$ 和 $q_i$ 求偏导:

  1. 对 $p_u$ 求偏导:

  2. 对 $q_i$ 求偏导:

第二步:参数更新

根据梯度下降公式 $\theta \leftarrow \theta - \eta \frac{\partial L}{\partial \theta}$(其中 $\eta$ 为学习率),我们将常数项 $2$ 合并到学习率中,得到最终更新公式:

  1. 用户向量更新:
  2. 物品向量更新:

3. 算法执行流程

  1. 初始化:随机初始化矩阵 $P$ 和 $Q$。
  2. 洗牌 (Shuffle):打乱训练集中三元组 $(u, i, r_{ui})$ 的顺序,防止模型产生周期性震荡。
  3. 迭代更新:遍历每个样本,计算误差并根据上述公式立即更新对应的 $p_u$ 和 $q_i$。
  4. 收敛检查:检查全量损失是否稳定或验证集指标是否下降。

4. 完整笔记与面试避坑

核心问题 关键答案
为什么叫“随机”? 因为它不像批量梯度下降 (BGD) 那样计算所有样本的梯度,而是每看到一个样本就更新一次参数。
学习率 $\eta$ 的作用? 步长。太大容易越过最优解(不收敛),太小收敛速度极慢。
正则化 $\lambda$ 的意义? 相当于给隐向量戴上“枷锁”,强制它们不要为了拟合单一噪声而变得过大。
SGD vs ALS? SGD 在非凸问题上更灵活;ALS 在分布式场景(并行计算)下效率更高。

5. 拓展:工业界常见的优化变体

  • Momentum (动量法):引入前一次更新的方向,帮助模型冲出“鞍点”或局部极小值。
  • Adam (自适应矩估计):为每个参数动态调整学习率,是目前最常用的优化器。
  • Bias-SVD:在更新公式中增加对 $b_u, b_i$(偏置项)的导数更新。

To be continued…


Author: YANG
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source YANG !
  TOC