矩阵分解


文档摘要

矩阵分解 矩阵分解(Matrix Factorization,MF)是推荐系统文献中公认的一种算法。最初版本的矩阵分解模型由Simon Funk发表在一篇非常知名的博文中,他在这篇博文描述了分解矩阵的想法。随后,由于2006年举办的Netflix竞赛,该模型变得广为人知。那时,流媒体和视频租赁公司Netflix为了增进其推荐系统的性能而举办了一项比赛。如果最佳团队(例如Cinematch)能够将Netflix的基线提高10%,那么他们将赢得100万美元的奖励。这一比赛在推荐系统领域引起了广泛的关注。随后,BellKor's Pragmatic Chaos团队(一个由BellKor、Pragmatic Theory和BigChaos混合组成的团队)赢得了这一大奖。

矩阵分解

矩阵分解(Matrix Factorization,MF)是推荐系统文献中公认的一种算法。最初版本的矩阵分解模型由Simon Funk发表在一篇非常知名的博文中,他在这篇博文描述了分解矩阵的想法。随后,由于2006年举办的Netflix竞赛,该模型变得广为人知。那时,流媒体和视频租赁公司Netflix为了增进其推荐系统的性能而举办了一项比赛。如果最佳团队(例如Cinematch)能够将Netflix的基线提高10%,那么他们将赢得100万美元的奖励。这一比赛在推荐系统领域引起了广泛的关注。随后,BellKor's Pragmatic Chaos团队(一个由BellKor、Pragmatic Theory和BigChaos混合组成的团队)赢得了这一大奖。尽管他们的最终评分来自一个集成解决方案,矩阵分解算法仍在其中起到了关键作用。Netflix Grand Prize的技术报告:cite:Toscher.Jahrer.Bell.2009详细解释了该方案所采用的模型。本节将深入研究矩阵分解模型的细节和实现过程。

矩阵分解模型

矩阵分解是一种协同过滤模型。具体来说,该模型将用户-物品交互矩阵(例如评分矩阵)分解为两个低秩矩阵的乘积,从而得到用户和物品的低秩架构。

使用\mathbf{R} \in \mathbb{R}^{m \times n}表示具有m个用户和n个物品的交互矩阵,矩阵\mathbf{R}的数值表示显式评分。用户-物品交互矩阵将被分解成用户潜矩阵\mathbf{P} \in \mathbb{R}^{m \times k}和物品潜矩阵\mathbf{Q} \in \mathbb{R}^{n \times k}。其中,表示潜因子尺寸的k \ll m, n。使用\mathbf{p}_u表示矩阵\mathbf{P}的第u行,同时使用\mathbf{q}_i表示矩阵\mathbf{Q}的第i行。对于某一物品i\mathbf{q}_i中的数值衡量了特征(例如电影风格和语言等)的大小。对于某一用户u\mathbf{p}_u中的数值衡量他对物品相应特征的感兴趣程度。这些潜因子可能代表了之前提到的一些维度,但同时它们也可能是完全无法理解的。 用户对物品的预测评分可以通过下式计算:

\hat{\mathbf{R}} = \mathbf{PQ}^\top

上式中的\hat{\mathbf{R}}\in \mathbb{R}^{m \times n}表示预测评分矩阵,它的形状和真实评分矩阵\mathbf{R}是一致的。这种预测方式的主要问题是无法建模表示用户和物品的偏置。例如,有一些用户倾向于给出较高的评分,而有一些物品由于质量较差得到的评分普遍较低。这类偏置在实际应用中很常见。为了表示这种偏置,我们在此处引入了用户偏置和物品偏置。具体来说,用户u对物品i的评分由下式计算得到。

\hat{\mathbf{R}}_{ui} = \mathbf{p}_u\mathbf{q}^\top_i + b_u + b_i

然后,我们通过减少预测评分和实际评分的均方误差来训练矩阵分解模型。目标函数如下所示:

\underset{\mathbf{P}, \mathbf{Q}, b}{\mathrm{argmin}} \sum_{(u, i) \in \mathcal{K}} \| \mathbf{R}_{ui} - \hat{\mathbf{R}}_{ui} \|^2 + \lambda (\| \mathbf{P} \|^2_F + \| \mathbf{Q} \|^2_F + b_u^2 + b_i^2 )

上式中的\lambda表示正则化率。通过惩罚参数大小,正则化公式$\lambda (| \mathbf{P} |^2_F + | \mathbf{Q}
|^2_F + b_u^2 + b_i^2 )被用来规避过拟合问题。已知\mathbf{R}{ui}(u, i)对储存在集合\mathcal{K}={(u, i) \mid \mathbf{R}{ui} \text{ is known}}$当中。模型参数可以通过优化算法(例如随机梯度下降法和Adam)学习得到。

矩阵分解模型的直观示意图如下所示:

矩阵分解模型的图示

在本节的最后,我们将解释矩阵分解模型的实现过程,并使用MovieLens数据集训练模型。

from d2l import mxnet as d2l from mxnet import autograd, gluon, np, npx from mxnet.gluon import nn import mxnet as mx npx.set_np()

模型实现

首先,我们按照上述描述实现矩阵分解模型。用户和物品的潜因子可以通过nn.Embedding构造。input_dim为用户和物品的数量,而output_dim为潜因子k的维度。将output_dim设置为1后,我们也可以使用nn.Embedding构造用户和物品的偏置量。在forward函数中,用户和物品的id被用于索引嵌入向量。

class MF(nn.Block): def __init__(self, num_factors, num_users, num_items, **kwargs): super(MF, self).__init__(**kwargs) self.P = nn.Embedding(input_dim=num_users, output_dim=num_factors) self.Q = nn.Embedding(input_dim=num_items, output_dim=num_factors) self.user_bias = nn.Embedding(num_users, 1) self.item_bias = nn.Embedding(num_items, 1) def forward(self, user_id, item_id): P_u = self.P(user_id) Q_i = self.Q(item_id) b_u = self.user_bias(user_id) b_i = self.item_bias(item_id) outputs = (P_u * Q_i).sum(axis=1) + np.squeeze(b_u) + np.squeeze(b_i) return outputs.flatten()

评估方法

接下来,我们使用均方根误差(root-mean-square error,RMSE)作为度量。该度量方式常用于测量模型的预测评分和实际评分(真值)之间的差异:cite:Gunawardana.Shani.2015。RMSE定义如下:

\mathrm{RMSE} = \sqrt{\frac{1}{|\mathcal{T}|}\sum_{(u, i) \in \mathcal{T}}(\mathbf{R}_{ui} -\hat{\mathbf{R}}_{ui})^2}

其中,\mathcal{T}为包含了用户-物品对的待评估集合,|\mathcal{T}|为集合的大小。我们可以使用mx.metric提供的RMSE函数。

def evaluator(net, test_iter, ctx): rmse = mx.metric.RMSE() # Get the RMSE rmse_list = [] for idx, (users, items, ratings) in enumerate(test_iter): u = gluon.utils.split_and_load(users, ctx, even_split=False) i = gluon.utils.split_and_load(items, ctx, even_split=False) r_ui = gluon.utils.split_and_load(ratings, ctx, even_split=False) r_hat = [net(u, i) for u, i in zip(u, i)] rmse.update(labels=r_ui, preds=r_hat) rmse_list.append(rmse.get()[1]) return float(np.mean(np.array(rmse_list)))

训练和评估模型

在训练函数中,我们采用了L_2损失作为权重衰减函数。该权重衰减机制和L_2正则化具有相同的效果。

#@save def train_recsys_rating(net, train_iter, test_iter, loss, trainer, num_epochs, ctx_list=d2l.try_all_gpus(), evaluator=None, **kwargs): timer = d2l.Timer() animator = d2l.Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0, 2], legend=['train loss', 'test RMSE']) for epoch in range(num_epochs): metric, l = d2l.Accumulator(3), 0. for i, values in enumerate(train_iter): timer.start() input_data = [] values = values if isinstance(values, list) else [values] for v in values: input_data.append(gluon.utils.split_and_load(v, ctx_list)) train_feat = input_data[0:-1] if len(values) > 1 else input_data train_label = input_data[-1] with autograd.record(): preds = [net(*t) for t in zip(*train_feat)] ls = [loss(p, s) for p, s in zip(preds, train_label)] [l.backward() for l in ls] l += sum([l.asnumpy() for l in ls]).mean() / len(ctx_list) trainer.step(values[0].shape[0]) metric.add(l, values[0].shape[0], values[0].size) timer.stop() if len(kwargs) > 0: # It will be used in section AutoRec test_rmse = evaluator(net, test_iter, kwargs['inter_mat'], ctx_list) else: test_rmse = evaluator(net, test_iter, ctx_list) train_l = l / (i + 1) animator.add(epoch + 1, (train_l, test_rmse)) print(f'train loss {metric[0] / metric[1]:.3f}, ' f'test RMSE {test_rmse:.3f}') print(f'{metric[2] * num_epochs / timer.sum():.1f} examples/sec ' f'on {str(ctx_list)}')

最后,把所有的东西全都结合起来然后开始训练模型。此处,我们将潜因子的维度设置为30。

ctx = d2l.try_all_gpus() num_users, num_items, train_iter, test_iter = d2l.split_and_load_ml100k( test_ratio=0.1, batch_size=512) net = MF(30, num_users, num_items) net.initialize(ctx=ctx, force_reinit=True, init=mx.init.Normal(0.01)) lr, num_epochs, wd, optimizer = 0.002, 20, 1e-5, 'adam' loss = gluon.loss.L2Loss() trainer = gluon.Trainer(net.collect_params(), optimizer, {"learning_rate": lr, 'wd': wd}) train_recsys_rating(net, train_iter, test_iter, loss, trainer, num_epochs, ctx, evaluator)

下面,我们使用训练过的模型来预测ID为20的用户对ID为30的物品的评分。

scores = net(np.array([20], dtype='int', ctx=d2l.try_gpu()), np.array([30], dtype='int', ctx=d2l.try_gpu())) scores

小结

  • 矩阵分解模型在推荐系统中有着广泛的应用。它可以用于预测用户对物品的评分。
  • 我们可以为推荐系统实现和训练一个矩阵分解模型。

练习

  • 修改潜因子的维度。潜因子的维度将怎样影响模型的性能呢?
  • 尝试不同的优化器、学习率和权重衰减率。
  • 检查其他用户对某一电影的评分。

Discussions


发布者: 作者: 转发
评论区 (0)
U