深度自编码器在推荐中的应用

出品:贪心科技(公众号:贪心科技)

作者:Artem Oppermann

前言

协同过滤是推荐系统通过收集来自许多其他用户的品味或偏好来预测某个特定用户的兴趣所使用的一种方法。协同过滤技术的基本假设是,如果用户A与B在某件事上有相同的品味或意见,那么A就更有可能在不同的问题上得出与B相同的意见。通过本文你将学到:如何预测某个用户对一部电影的评价,这个预测将会基于这个用户的偏好以及其他用户对该部电影以及其他电影的影评。


目录:

  • 简介

  • 深度自编码器

  • 模型实现


1. 简介


自编码器是一种在协同过滤领域达到最优性能的深度神经网络架构。作为文章的第一部分,在这里你将会看到简单自编码器的理论背景以及数学基础,学习简单自编码器到深度自编码器的延伸过渡。第二部分我们将主要关注实战部分,我们会学习如何用tensorflow逐步实现自编码器。在这篇文章中,我们仅学习、评价自编码器模型中较为重要的部分,而全部的模型以及输入和预处理可以参考github的文档。


1. 深度自编码器


  • 自编码器


在我们介绍深度自编码器之前,我们需要了解它的简化版本。其实,自编码器就是一种学习输入数据的表示(编码)的一种人工神经网络,通常作为一种降维的方法被我们使用。从结构上说,自编码器是一种前馈神经网络,它包含一个输入层,一个隐层以及一个输出层。其中的输出层所包含的神经元个数与输入层的神经元个数相同,这样便可以对输入进行重构。不难发现,自编码器是一种无监督学习的方法,在学习过程中没有标记,我们只能拿到的必要的数据而不是数据标签对。

简单自编码器结构


自编码器的隐层规模比输入层规模小的设定是很必要的。这种设定会迫使模型通过学习数据中的相关性从而在隐层中创建输入数据的压缩表示。从输入到隐藏层的转换称为编码步骤,从隐藏层到输出层的转换称为解码步骤。我们也可以将这些转换用数学的方式定义为两种映射:

这种映射其实就是将输入数据向量与权重矩阵相乘,再加一个偏置项,然后对这个结果用sigmoid函数、tanh函数等进行非线性变换。


  • 自编码器的训练


在训练过程中,编码器首先接收输入数据的一个样本x,然后将其映射为隐层表示z。然后解码器再将z映射到输出向量x’中。而这个输出向量(最好的情况下)就与输入数据x有相同的表示。但我们必须要明确,完全对x的重构是不可能实现的。得到输出向量后了,我们就要用随机梯度下降的方法来优化(最小化)预先设定的损失函数,例如MSE:

  • 深度自编码器


将简单的自编码器做一个扩展,我们就会得到深度自编码器。与简单自编码器不同之处便体现在隐层的数目上。

多出的隐层便会赋予自编码器学习更复杂的数据中潜在模式的能力。深度自编码器的第一层可以学到原始输入的第一层特征(例如图片的边际曲线);第二层便可以学习第一层特征中的模式(例如:哪些边会一起出现,形成轮廓或角点)。越深的深度自编码器就更有可能学到更高层次的特征表示。总的来说:我们需要更多的层来处理更复杂的数据,例如我们在协同过滤中使用的数据。


  • 模型使用


如前所述,在这里我们将学习如何预测用户给电影的评级。为了解决这个问题,我们将使用著名的MovieLens数据集。MovieLens是一个基于Web的推荐系统和在线社区,推荐用户观看电影。具体来说,我们会用到其中的ml-1m.zip数据集,它包含了1000209个由6040个用户对将近3900部电影的匿名评级。我们需要的关键数据就是ratings.dat:这个文件包含1000209行以user_id::movie_id::rating:time_stamp为格式的向量。


例如,ratings.dat的第一行是:1::595::5::978824268。这就表示:用户 Nr. 1 给电影Nr. 595的评价等级是5,而时间戳不用考虑因为它对于我们的任务没用。


我们用的深度模型需要对训练和测试使用特定的数据结构。这种数据结构其实就是U乘M的矩阵,其中U是用户数量,M是电影的数量。矩阵的每行每列分别对应了唯一的用户和唯一的一部电影。

因为我们的教程主要关注深度模型的应用,所以从ratings.dat中提取数据矩阵的操作在这里就不叙述了。如果大家感兴趣,那么可以去github上来参考Python代码。


  • 训练、测试数据集


在实现训练模型之前,另一个数据再处理步骤是必要的——将数据集分成训练和测试集。这一步非常简单明了。到目前为止,我们有一个用户-电影矩阵,其中每行是一个评级列表。为了获得这个矩阵中的训练和测试集,我们必须抽取某些行然后只使用它们进行训练,而只使用剩下作为测试数据集。


作为上述过程的一个例子,让我们考虑一个更小的只包含15个电影的数据集。某个特定用户已经给了这些电影以下评级:

Movie Nr. : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

Rating:     5 0 2 4 0 0 2 1 5  1  0  4  5  1  3

 

需要注意的是,0意味着电影没有被评定。现在我们把前10部电影的评级作为训练集,假设剩下电影的还没有被评级:

Movie Nr. : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

Rating:     5 0 2 4 0 0 2 1 5  0  0  0  0  0  0

 

因此,原数据集的最后5个电影评级被用作测试数据,而电影1-10被设定为未被评级:

Movie Nr. : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

Rating:     0 0 0 0 0 0 0 0 0  1  0  4  5  1  3

 

这只是对如何获得不同的集合一个简单的演示。在原始的MovieLens数据集中,我为每个用户只使用10个电影评级来做测试,而其余的(绝大多数)用于模型的训练集。


3. TensorFlow的实现方法


  • 模型结构


深层自动编码器可以用一个类(class)来实现,这个类中包含所有必要的操作:如推理,优化,损失,精度等等。在构造函数中,我们需要设置权值和偏置的初始化器。在下一步,网络中的所有权值和偏差都被初始化。权重是服从正态分布,均值为0,方差为0.02,而在偏置均设置为0。在这个特定的例子中,网络有三个隐层,每个隐层包含128个神经元。输入层(和输出层)的大小对应于数据集中所有出现的电影的数量。

  • 训练


给定输入数据样本x(用户电影矩阵的一行),前向传递并计算网络输出。隐层使用sigmoid作为激活函数。要注意的是,最后一层既不需要非线性变换,也不要有偏置项。

拿到了网络预测后,我们就可以计算这些预测和相应标签(网络输入x)之间的损失了。为了计算损失的平均值,我们还需要知道非0标签的数目,也就是用户在训练集中的总评级数。

网络的优化/训练步骤是有一些技巧的,我们来一步一步地讨论:给定输入x,计算相应的输出这个过程中,正如我们已经注意到的,输入x中的大部分值都是零值,因为用户不大可能观看和评价数据集中的所有5953部电影。因此,最好不要直接使用网络的原始预测。相反,我们必须识别数据输入x中的零值指数,并将预测向量中与这些指数相对应的值设置为零值。这种对预测的操作极大地减少了网络的训练时间,进而给网络提供了更多集中在用户实际所做的评级上的训练。因此,最好不要直接使用网络的原始预测。相反,我们必须识别数据输入x中的零值索引,并将预测向量中与这些索引对应的值设置为零。


在此步骤之后,我们便可以计算损失函数或者带正则项的损失(可选)。在这里我们可以使用Adam作为优化器。需要说明的是:该方法返回一个均方根误差(RMSE)而不是均方误差(MSE),以获得更好的精度测量。



  • 测试


在训练几轮之后,神经网络已经将所有用户的评分“学习”很多次了。在这个时候,模型应该已经学会了潜在的隐层模式中的数据和相应的多用户的电影评价。给定用户评级训练样本x,模型预测输出x′。这个输出向量是由输入x的再现(如预期)组成的,但现在也包含了输入x中的先前零评级的值。这意味着该模型给出了未评级的电影的等级。这个评级对应于用户的审美——模型从数据集中识别和学习的审美方式。


为了能够测量模型的精度,训练和测试数据集都是必要的:在训练集的基础上进行预测。类似于训练阶段,我们只考虑测试集中的对应非零值索引的输出值。现在我们可以计算预测和实际评级之间的均方根误差(RMSE)了。RMSE代表样本的预测值与观测值之间的标准差。例如,RMSE为0.5意味着平均预测评级偏离实际评级有0.5星。


  • 训练结果


最后一步包括执行训练过程以及对模型性能的评估。在这里,我们不会深入讨论使用TensorFlow训练的细节,因为这些步骤大家都比较熟悉了。对这个主题感兴趣的读者可以查看github代码。


在这里你可以看到训练的前50轮的训练和测试表现。经过50轮迭代,我们在测试集上得到了RMSE为0.929的结果:

epoch_nr: 0,  train_loss: 1.169, test_loss: 1.020

epoch_nr: 10, train_loss: 0.936, test_loss: 0.959

epoch_nr: 20, train_loss: 0.889, test_loss: 0.931

epoch_nr: 30, train_loss: 0.873, test_loss: 0.923

epoch_nr: 40, train_loss: 0.859, test_loss: 0.925

epoch_nr: 50, train_loss: 0.844, test_loss: 0.929


如果对本文有自己的见解,欢迎在评论区留言,   或者扫码关注公众号交流。



此为贪心科技编译,转载请联系本公众号获得授权。



加入贪心科技、广告&商务合作:[email protected]


猜你喜欢

转载自blog.csdn.net/mlooker/article/details/80332864