在新手接触推荐系统这个领域时,遇到第一个理解起来比较困难的就是协同过滤法。那么如果这时候百度的话,得到最多的是奇异值分解法,即(SVD)。SVD的作用大致是将一个矩阵分解为三个矩阵相乘的形式。如果运用在推荐系统中,首先我们将我们的训练集表示成矩阵的形式,这里我们以movielen数据集为例。这个数据集包含了用户对电影的评分。那么矩阵形式大致为:

  movie1  movie2  movie3 moive4
user1 1      
user2 2     3
user3   5 4  
user4 2     4

  其中1~5就是对应用户对电影的评分。空余处表示数据集中没有对应用户和电影的信息。如果我们想使用SVD,一般讲空余处都填为0.假设此矩阵为V.那么运用SVD可以得到

                          V = UΣVT

  但是上面的等号并不能取到,只能是约等于。那么我们将得到的三个矩阵相乘得到V'(注意与V不同)。那么原来的空白处(也就是0处)可能就不再为0,那么这就是对该user-moive对的预测。这就是SVD的主要原理。因为SVD有很多现成的算法,也不用迭代就可直接得到,所以使用比较方便。

 

  但是我们会看到,上面方法有一个致命的缺陷,那就是将未知的评分全都设为0.这其实是极其不合理的,因为用户不给某个电影打分并不是很不喜欢(0分),而是有可能还没有看过这个电影。这样就加入了我们主观臆断的信息,最后造成错误。解决的方法就是使用隐因子的矩阵分解法。注意矩阵分解法和SVD有相似的地方也有不同的地方,下面我就对矩阵分解法进行详细介绍。

  在矩阵分解法中,有一个假设,就是每一个用户都有一个长度为k的特征向量,每一部电影也有一个相同长度的特征向量(k一般需要用户指定)。那么所有用户的特征向量排列成一个矩阵 U 的维度为UserNum * k。其中用户i对应的向量为Ui。所有电影的特征向量排列成一个矩阵M 的维度为MoiveNum * k。其中电影j对应的向量为Uj。那么用户i对电影j的评分 Vij = <Ui , Mj>(<>代表点乘)。那么所有用户和所有电影之间的评分就可以用两个矩阵相乘来得到:

                          V' = UMT

  注意这里是V'而不是V。那么问题来了,我们如何确定这个U和M?一个自然的想法就是让V'和V尽可能地相等。那么这又有一个问题,那就是V(即数据集)有很多地方是没有评分的,如何判断和V'是否相等?那么我们在这里只计算有评分处的MSE。这样既没有使用额外的信息,又能判断两者是否接近。那么自然而然得就引入了我们的lost function:

      

  这里Iij表示用户i对电影j有评分记录。后面两项是惩罚因子,目的是防止过拟合。那么利用梯度下降法,我们就可以通过迭代得到U和M的值。其对U和对M的求到如下:

  到这里我们就已经完成了基础的矩阵分解法。那么进一步,为了实现更好的效果,我们要考虑每个个体打分的影响,因为有些用户打分偏高,有些用户打分偏低。同样对于电影和所有的评分。所以我们评分的计算公式应该改为:

      Vij = <Ui,Mj> + overall_mean + ai +bj

  其中overall_mean是所有评分的平均值,ai是用户i打分的平均值,bj是电影j得分的平均值。其中overall_mean认为是一个常数,而ai和bj都是需要优化的参数。这里我们就不给出对他们求导的式子,我直接给出矩阵形式的算法,便于大家具体实现。(INCOMING) (拖了一周左右放上去,这拖延症也是没sei了 0.0)

 

  我自己写了一个python版的,有兴趣可以参考 https://github.com/ccienfall/RecommandSystem/blob/master/script/Factorize.py

  在2016Byte Cup国际机器学习竞赛的数据集上分别运用SVD和矩阵分解(MF),最后结果是MF的方法能比SVD的效果高20%左右。

  参考文献:《A Guide to Singular Value Decomp osition for Collaborative Filtering》

posted on 2016-10-24 22:51  Ccien  阅读(4303)  评论(0编辑  收藏  举报