基本介绍
伴随的电商业务蓬勃发展,推荐系统也受到了格外重视,在通常电商系统中都是采用基于CF(Collaborative filtering)算法原型来做的。该算法是基于这样基本假设:people who agreed in the past will agree in the future 。
说到SVD算法不能不说到Netflix举办的推荐大赛,这次比赛对推荐系统工业界产生了很大影响,伴随着提出了很多算法思路,所以本文也是以这次比赛为主线,参考其中相关的两篇经典论文,两篇文章会上传。
CF算法的挑战:对于每个user ,未知的item 评分会大体上相似,因此对于那些只给少量item有评分的user将会有比较大的预测误差,因为这样的user缺乏足够信息做预测。
Netflix比赛中最简单直观思路就是利用KNN算法思想,对于某个user 某个未评分的item,找到那个user 近邻的users,然后多个近邻user通过相似性加权来预测这个未评分的item,这个思路很简单实现上也不难,但是这里有个关键问题就是如何计算近邻,如何定义近邻的相似性,我们通过计算users 和 objects特征来计算相似性,但是这个特征很难去构建好。
另外一种思路:matrix factorization 矩阵分解,这其中最常用的就是Singular Value Decomposition算法,SVD直接找到每个user和object的特征,通过user 和object特征对未评分的item进行评分预测。
SVD
SVD跟别的矩阵分解算法相比:没有强加任何限制并且更容易实现
上面是最基本的SVD算法形态,预测函数p 通常就是用简单的dot product 运算,我们知道当我们把一个问题损失函数定义好了,下面要做的就是最优化的问题。
这个算法考虑到评分区间问题,需要把预测值限定在指定评分区间
对上面损失函数求偏导如公式(5)(6)所示。
Batch learning of SVD
所以整个算法流程就如下:(Batch learning of SVD)
然后论文中也给出了如何给U、V矩阵赋初值的算法。
公式中a是评分的下界,n(r)是在[-r,r]的均匀分布,上面提到的SVD算法是其最标准的形式,但是这个形式不是和大规模并且稀疏的矩阵学习,在这种情况下梯度下降会有很大的方差并且需要一个很小的学习速率防止发散。
前面提到了Batch learning of SVD ,我们会发现一个问题就是:如果评分矩阵V 有一些小的变动,比如某个user增加了评分,这是我们利用Batch learning of SVD 又需要把所有数据学习一次,这会造成很大的计算浪费。
如上所述我们每次针对Ui :feature vector of user i 进行学习更新
更极端的情况我们可以针对每一个评分来进行学习如下图:
Incomplete incremental learning of SVD
针对上面基于某个user 学习或者基于某个评分学习算法我们称为增量学习(incremental learning)
算法 2 是基于某个user 的 叫做非完全的增量学习
Complete incremental learning of SVD
算法 3 是基于某个评分进行更新的算法,叫做完全增量学习算法。
算法3还可以有一些改进提升空间:加入per-user bias and per-object bias
在论文结论部分:batch learning or incomplete incremental learning 需要一个很小的学习速率并且跟 complete incremental learning 相比性能不稳定,所以综合得出结论就是complete incremental learning 是对于millions 训练数据最好选择。
Mahout中实现
在Mahout中也会发现时基于complete incremental learning 算法进行的实现
protected void updateParameters(long userID, long itemID, float rating, double currentLearningRate) { int userIndex = userIndex(userID); int itemIndex = itemIndex(itemID); double[] userVector = userVectors[userIndex]; double[] itemVector = itemVectors[itemIndex]; double prediction = predictRating(userIndex, itemIndex); double err = rating - prediction; // adjust user bias userVector[USER_BIAS_INDEX] += biasLearningRate * currentLearningRate * (err - biasReg * preventOverfitting * userVector[USER_BIAS_INDEX]); // adjust item bias itemVector[ITEM_BIAS_INDEX] += biasLearningRate * currentLearningRate * (err - biasReg * preventOverfitting * itemVector[ITEM_BIAS_INDEX]); // adjust features for (int feature = FEATURE_OFFSET; feature < numFeatures; feature++) { double userFeature = userVector[feature]; double itemFeature = itemVector[feature]; double deltaUserFeature = err * itemFeature - preventOverfitting * userFeature; userVector[feature] += currentLearningRate * deltaUserFeature; double deltaItemFeature = err * userFeature - preventOverfitting * itemFeature; itemVector[feature] += currentLearningRate * deltaItemFeature; } } private double predictRating(int userID, int itemID) { double sum = 0; for (int feature = 0; feature < numFeatures; feature++) { sum += userVectors[userID][feature] * itemVectors[itemID][feature]; } return sum; }