概率矩阵分解(Probabilistic Matrix Factorization,简称PMF)是一种用于推荐系统中的协同过滤技术。与传统的矩阵分解不同,PMF 引入了概率论来对用户-物品交互中的不确定性进行建模。这使得它在处理稀疏数据集时非常有效,因为在这些数据集中我们只能观测到一小部分的评分。PMF 通过引入一个概率模型扩展了矩阵分解(MF):
- 潜在因子被假设服从高斯分布。
- 观测到的评分被建模为以预测点积为中心的高斯分布。
- 能够自然地捕捉数据中的噪声和不确定性。
矩阵分解(Matrix Factorization,简称MF)是一种协同过滤方法,它通过将用户-物品交互矩阵(R)分解为两个较小的矩阵来预测其中的缺失值。
> R\approx U. V^T
- U:用户潜在因子矩阵。
- V:物品潜在因子矩阵。
- 每个用户和物品都由潜在空间中的一个向量表示。
- 点积 U{i ^T}Vj 用于预测用户 i 对物品 j 的偏好。
核心概念
1. 潜在因子
潜在因子代表了用户和物品的隐藏特征。
- 每个用户由一个向量 U_i 表示。
- 每个物品由一个向量 V_j 表示。
- 它们的点积 Ui^T Vj 给出了预测评分。
> Ui \in \mathbb{R}^k, \quad Vj \in \mathbb{R}^k, \quad \hat{R}{ij} = Ui^T V_j
其中,
- k:潜在维度的数量(例如,对于电影来说,可能是类型偏好)。
- U_i :用户 i 的隐藏表示。
- V_j :物品 j 的隐藏表示。
- \hat R_{ij}:预测评分。
2. 高斯先验
PMF 假设潜在因子是从高斯(正态)分布中抽取的。
> U \sim \mathcal{N}(0, \sigmaU^2 I), \quad V \sim \mathcal{N}(0, \sigmaV^2 I)
其中,
- U:用户潜在因子矩阵。
- V:物品潜在因子矩阵。
- \sigma U^2 , \sigma V^2 :控制分布范围的方差项。
- 这个先验确保了正则化,防止因子数值变得过大。
3. 观测评分
每个评分都被建模为一个以用户和物品向量的点积为中心的高斯分布。
> R{ij} \sim \mathcal{N}(Ui^T Vj, \sigmaR^2)
其中,
- R_{ij }:用户 i 对物品 j 的观测评分。
- Ui^T Vj :期望评分(分布的均值)。
- \sigma _R^2 :方差(捕捉用户偏好中的噪声)。
- 这意味着评分不是精确的,而是具有不确定性。
4. 目标函数
我们的目标是找到能够最大化似然(拟合观测评分)同时避免过拟合的潜在因子。
> L = \sum{(i,j) \in R} (R{ij} – Ui^T Vj)^2 + \lambdaU \sumi \
^2 + \lambdaV \sumj \
^2
其中,
- 第一项:实际评分与预测评分之间的平方误差。
- 第二项:\lambdaU \Sigmai |
U_i ^2 :针对用户的正则化。
- 第三项:\lambdaV \Sigmaj|
V_j ^2 :针对物品的正则化。
- \lambdaU,\lambdaV :控制正则化的强度。
- 最小化 LLL 在准确性(拟合数据)和简单性(避免大数值)之间取得了平衡。
代码实现
接下来,我们将使用 NumPy 通过随机梯度下降(SGD)来实现 PMF。
第1步:导入库
我们需要导入必要的库,比如 NumPy。
Python
CODEBLOCK_2d5e9028
第2步:定义 PMF 类
我们来定义 PMF 模型,其中包含评分、潜在维度、学习率、正则化参数和训练轮数等参数。
Python
CODEBLOCK_54ab5c84
- 初始化用户和物品的潜在因子。
- 使用随机梯度下降(SGD)根据观测评分更新潜在因子。
- 在每个 epoch 结束时计算均方误差(MSE)以跟踪训练进度。
Python
“
def train(self):
self.U = np.random.normal(
scale=1. / self.numfactors, size=(self.numusers, self.num_factors))
self.V = np.random.normal(
scale=1. / self.numfactors, size=(self.numitems, self.num_factors))
for epoch in range(self.num_epochs):
for i in range(self.num_users):
for j in range(self.num_items):
if self.R[i, j] > 0:
prediction = self.predict(i, j)
error = self.R[i, j] – prediction
self.U[i, :] += self.learning_rate * \
(error self.V[j, :] – self.reg_param self.U[i, :])
self.V[j, :] += self.learning_rate * \
(error self.U[i, :] – self.reg_param self.V[j, :])
mse = self.compute_mse()
print(f‘E