矩阵分解|Matrix Factorization
上一部分我们介绍了用户与物品之间的关系可以通过矩阵来表示,而矩阵分解是一种简洁高效的嵌入模型。假设我们有一个用户反馈矩阵:
$$ A \in R^{m \times n} $$
其中 \(m\) 代表用户总数,\(n\) 代表物品总数。
用户嵌入矩阵$$ U \in \mathbb R^{m \times d} $$
商品嵌入矩阵$$ V \in \mathbb R^{n \times d} $$
这两个嵌入矩阵的点积,可以近似还原出原始矩阵 \(A\):
$$ U V^T(i, j) = \langle U_i, V_j \rangle \approx A_{i, j} $$
如何选择目标函数
目标函数该如何选取?最常用的方式是欧几里得距离,我们就以此为例。直观来看,需要最小化所有已观察到的用户-物品对的误差平方和:
$$ \min_{U \in \mathbb R^{m \times d}, V \in \mathbb R^{n \times d}} \sum_{(i, j) \in \text{obs}} (A_{ij} - \langle U_{i}, V_{j} \rangle)^2 $$
这个公式仅对观察到的 \((i, j)\) 求和,即反馈矩阵中非零的项。但问题随之而来——只考虑观察到的值真的可行吗?实际上并不理想。如果只依赖正样本,模型会忽略用户未交互的负样本,导致推荐效果下降,泛化能力变差。简而言之,正负样本缺一不可。
那么如何改进呢?更常见的做法是将未观察到的项也纳入优化。具体来说,将未观察到的 \((i, j)\) 的值设为 0,然后对矩阵中所有元素求和:
$$ \min_{U \in \mathbb R^{m \times d}, V \in \mathbb R^{n \times d}} \|A - U V^T\|_F^2 $$
这个形式看起来可以直接用奇异值分解(SVD)求解。但 SVD 在实际应用中并不好用,原因在于用户反馈矩阵通常极其稀疏——例如在视频或新闻 App 中,热门内容被大量用户浏览,而长尾内容几乎无人问津。稀疏矩阵使用 SVD 后,结果容易趋近于零,泛化能力自然较弱。
于是,加权矩阵分解(Weighted Matrix Factorization)应运而生。它将目标函数拆分为两部分:
- 观察到的条目对应的总和
- 未观察到的条目对应的总和
公式如下:
$$ \min_{U \in \mathbb R^{m \times d}, V \in \mathbb R^{n \times d}} \sum_{(i, j) \in \text{obs}} (A_{ij} - \langle U_{i}, V_{j} \rangle)^2 + w_0 \sum_{(i, j) \notin \text{obs}} (\langle U_i, V_j\rangle)^2 $$
也可以写成带权重的形式:
$$ \sum_{(i, j) \in \text{obs}} w_{i, j} (A_{i, j} - \langle U_i, V_j \rangle)^2 + w_0 \sum_{i, j \notin \text{obs}} \langle U_i, V_j \rangle^2 $$
最小化目标函数的方法
如何最小化目标函数?常用的算法有两种:
- 随机梯度下降(SGD)——通用方法,灵活但收敛较慢
- 加权交替最小二乘(WALS)——专门针对该目标设计
目标函数对 \(U\) 和 \(V\) 都是二次的。SGD 作为通用工具,这里不再展开。WALS 的思路是先随机初始化嵌入,然后交替进行:固定 \(U\),对 \(V\) 求解;再固定 \(V\),对 \(U\) 求解。具体流程可参考下图:

SGD 与 WALS 的对比
SGD 和 WALS 各有所长,也各有短板。
SGD
- 非常灵活:可以换用其他损失函数
- 可以并行化处理
- 收敛速度较慢
- 处理未观察到的物品时更困难
WALS
- 依赖于均方误差
- 支持并行化
- 收敛速度比 SGD 更快
- 更容易处理未观察到的物品
具体选择哪个,取决于实际场景——如果数据稀疏且对收敛速度要求较高,WALS 通常更顺手;如果损失函数需要定制,SGD 的灵活性则成为优势。
