在新手接触推荐系统这个领域时,遇到第一个理解起来比较困难的就是协同过滤法。那么如果这时候百度的话,得到最多的是奇异值分解法,即(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)
我自己写了一个python版的,有兴趣可以参考 https://github.com/ccienfall/RecommandSystem/blob/master/script/Factorize.py
参考文献:《A Guide to Singular Value Decomp osition for Collaborative Filtering》