采用1M MovieLensz数据(80%train, 20%test, UserIDs range between 1 and 6040 ,MovieIDs range between 1 and 3952, From http://files.grouplens.org/datasets/movielens/)

进行训练和测试,在k仅为10时,得到最佳RMSE为0.854743。在100k数据上k=100时最佳RMSE为0.916602。

以下公式和文字来自陈靖_的博文 http://blog.csdn.net/zhongkejingwang/article/details/43083603,包含详尽的描述和推导。

参考论文为《Factorization Meets the Neighborhood: a Multifaceted Collaborative Filtering Model Yehuda Koren

SVD

通过对A矩阵进行奇异值分解:

现在可以来对A矩阵的映射过程进行分析了:如果在n维空间中找到一个(超)矩形,其边都落在A'A的特征向量的方向上,那么经过A变换后的形状仍然为(超)矩形!

vi为A'A的特征向量,称为A的右奇异向量,ui=Avi实际上为AA'的特征向量,称为A的左奇异向量。下面利用SVD证明文章一开始的满秩分解:

利用矩阵分块乘法展开得:

可以看到第二项为0,有

则A=XY即是A的满秩分解。

对任意一个矩阵A,都有它的满秩分解(k=Rank(A)):

那么刚才的评分矩阵R也存在这样一个分解,所以可以用两个矩阵P和Q的乘积来表示评分矩阵R:

上图中的U表示用户数,I表示商品数。然后就是利用R中的已知评分训练P和Q使得P和Q相乘的结果最好地拟合已知的评分,那么未知的评分也就可以用P的某一行乘上Q的某一列得到了:

这是预测用户u对商品i的评分,它等于P矩阵的第u行乘上Q矩阵的第i列。这个是最基本的SVD算法,那么如何通过已知评分训练得到P和Q的具体数值呢?

假设已知的评分为:

则真实值与预测值的误差为:

继而可以计算出总的误差平方和:

Basic SVD

利用梯度下降法可以求得SSE在Puk变量(也就是P矩阵的第u行第k列的值)处的梯度:

利用求导链式法则,e^2先对e求导再乘以e对Puk的求导:

由于

所以

上式中括号里的那一坨式子如果展开来看的话,其与Puk有关的项只有PukQki,其他的无关项对Puk的求导均等于0

所以求导结果为:

所以

为了让式子更简洁,令

这样做对结果没有影响,只是为了把求导结果前的2去掉,更好看点。得到

现在得到了目标函数在Puk处的梯度了,那么按照梯度下降法,将Puk往负梯度方向变化:

令更新的步长(也就是学习速率)为

则Puk的更新式为

同样的方式可得到Qik的更新式为

得到了更新的式子,现在开始来讨论这个更新要怎么进行。有两种选择:1、计算完所有已知评分的预测误差后再对P、Q进行更新。2、每计算完一个eui后立即对Pu和qi进行更新。这两种方式都有名称,分别叫:1、批梯度下降。2、随机梯度下降。两者的区别就是批梯度下降在下一轮迭代才能使用本次迭代的更新值,随机梯度下降本次迭代中当前样本使用的值可能就是上一个样本更新的值。由于随机性可以带来很多好处,比如有利于避免局部最优解,所以现在大多倾向于使用随机梯度下降进行更新。

RSVD

上面就是基本的SVD算法,但是,问题来了,上面的训练是针对已知评分数据的,过分地拟合这部分数据有可能导致模型的测试效果很差,在测试集上面表现很糟糕。这就是过拟合问题,关于过拟合与欠拟合可以看一下这张图

第一个是欠拟合,第二个刚好,第三个过拟合。那么如何避免过拟合呢?那就是在目标函数中加入正则化参数(加入惩罚项),对于目标函数来说,P矩阵和Q矩阵中的所有值都是变量,这些变量在不知道哪个变量会带来过拟合的情况下,对所有变量都进行惩罚:

这时候目标函数对Puk的导数就发生变化了,现在就来求加入惩罚项后的导数。

括号里第一项对Puk的求导前面已经求过了,第二项对Puk的求导很容易求得,第三项与Puk无关,导数为0,所以

同理可得SSE对qik的导数为

将这两个变量往负梯度方向变化,则更新式为

这就是正则化后的SVD,也叫RSVD。

加入偏置的SVD、RSVD

关于SVD算法的变种太多了,叫法也不统一,在预测式子上加点参数又会出来一个名称。由于用户对商品的打分不仅取决于用户和商品间的某种关系,还取决于用户和商品独有的性质,Koren将SVD的预测公式改成这样

第一项为总的平均分,bu为用户u的属性值,bi为商品i的属性值,加入的这两个变量在SSE式子中同样需要惩罚,那么SSE就变成了下面这样:

由上式可以看出SSE对Puk和qik的导数都没有变化,但此时多了bu和bi变量,同样要求出其更新式。首先求SSE对bu的导数,只有第一项和第四项和bu有关,第一项对bu的求导和之前的求导类似,用链式法则即可求得,第四项直接求导即可,最后可得偏导数为

同理可得对bi的导数为

所以往其负梯度方向变化得到其更新式为

这就是修改后的SVD(RSVD)。

  1. #coding:utf8
  2. import numpy as np
  3.  
  4. class SVD():
  5. def __init__(self,trainfile,testfile,factorNum=10):
  6. self.trainfile=trainfile
  7. self.testfile=testfile
  8. self.factorNum=factorNum
  9. self.userNum= 6040 # max user id
  10. self.itemNum= 3952 # max movie id
  11. self.learningRate=0.01
  12. self.regularization=0.02
  13. score=[float(line.split('::')[2])for line in open(self.trainfile)]
  14. self.av= np.mean(score)
  15. self.bu=np.zeros(self.userNum)
  16. self.bi=np.zeros(self.itemNum)
  17. temp=np.sqrt(self.factorNum)
  18. self.pu=np.array([[(0.1*np.random.random()/temp) for i in range(self.factorNum)] for j in range(self.userNum)])
  19. self.qi=np.array([[0.1*np.random.random()/temp for i in range(self.factorNum)] for j in range(self.itemNum)])
  20.  
  21. def train(self,iterTimes=100):
  22. preRmse=10000.0
  23. for iter in range(iterTimes):
  24. fi=open(self.trainfile,'r')
  25. for line in fi:
  26. content=line.split('::')
  27. user=int(content[0])-1
  28. item=int(content[1])-1
  29. rating=float(content[2])
  30. pscore=self.predict(self.av,self.bu[user],self.bi[item],self.pu[user],self.qi[item])
  31. eui=rating-pscore
  32. self.bu[user]+=self.learningRate*(eui-self.regularization*self.bu[user])
  33. self.bi[item]+=self.learningRate*(eui-self.regularization*self.bi[item])
  34. temp=self.pu[user]
  35. self.pu[user]+=self.learningRate*(eui*self.qi[item]-self.regularization*self.pu[user])
  36. self.qi[item]+=self.learningRate*(temp*eui-self.regularization*self.qi[item])
  37. fi.close()
  38. curRmse=self.test(self.av,self.bu,self.bi,self.pu,self.qi)
  39. print "Iteration %d times,RMSE is : %f" % (iter+1,curRmse)
  40. if curRmse>preRmse:
  41. print "The best RMSE is : %f" % (preRmse)
  42. break
  43. else:
  44. preRmse=curRmse
  45.  
  46. def test(self,av,bu,bi,pu,qi):
  47. rmse=0.0
  48. cnt=0
  49. fi=open(self.testfile)
  50. for line in fi:
  51. cnt+=1
  52. content=line.split('::')
  53. user=int(content[0])-1
  54. item=int(content[1])-1
  55. score=float(content[2])
  56. pscore=self.predict(av,bu[user],bi[item],pu[user],qi[item])
  57. rmse+=(score-pscore)**2
  58. fi.close()
  59. return np.sqrt(rmse/cnt)
  60.  
  61. def predict(self,av,bu,bi,pu,qi):
  62. pscore=av+bu+bi+np.dot(pu,qi)
  63. if pscore<1:
  64. pscore=1
  65. elif pscore>5:
  66. pscore=5
  67. return pscore
  68.  
  69. if __name__=='__main__':
  70. s=SVD("train.dat","test.dat")
  71. s.train() # Iteration 24 times,RMSE is : 0.854743
  1. # To convert train.dat and test.dat from ratings.dat.
  2. import numpy as np
  3. f=open("ratings.dat",'r')
  4. a=[line for line in f]
  5. n=len(a)
  6. tn=int(n*0.8)
  7. np.random.shuffle(a)
  8. d=open("train.dat",'w')
  9. [d.write(a[i]) for i in range(tn)]
  10. d.close()
  11. d=open("test.dat",'w')
  12. [d.write(a[i]) for i in range(tn,n)]
  13. d.close()

非对称SVD电影推荐系统的更多相关文章

  1. 使用SVD方法实现电影推荐系统

    http://blog.csdn.net/zhaoxinfan/article/details/8821419 这学期选了一门名叫<web智能与社会计算>的课,老师最后偷懒,最后的课程pr ...

  2. 电影推荐系统---协同过滤算法(SVD,NMF)

    SVD 参考 https://www.zybuluo.com/rianusr/note/1195225 1 推荐系统概述   1.1 项目安排     1.2 三大协同过滤   1.3 项目开发工具 ...

  3. SVD在推荐系统中的应用详解以及算法推导

    SVD在推荐系统中的应用详解以及算法推导     出处http://blog.csdn.net/zhongkejingwang/article/details/43083603 前面文章SVD原理及推 ...

  4. 使用矩阵分解(SVD)实现推荐系统

    http://ling0322.info/2013/05/07/recommander-system.html 这个学期Web智能与社会计算的大作业就是完成一个推荐系统参加百度电影推荐算法大赛,成绩按 ...

  5. 转】用Hadoop构建电影推荐系统

    原博文出自于: http://blog.fens.me/hadoop-mapreduce-recommend/ 感谢! 用Hadoop构建电影推荐系统 Hadoop家族系列文章,主要介绍Hadoop家 ...

  6. 基于Mahout的电影推荐系统

    基于Mahout的电影推荐系统 1.Mahout 简介 Apache Mahout 是 Apache Software Foundation(ASF) 旗下的一个开源项目,提供一些可扩展的机器学习领域 ...

  7. 基于pytorch的电影推荐系统

    本文介绍一个基于pytorch的电影推荐系统. 代码移植自https://github.com/chengstone/movie_recommender. 原作者用了tf1.0实现了这个基于movie ...

  8. 【原创 Hadoop&Spark 动手实践 13】Spark综合案例:简易电影推荐系统

    [原创 Hadoop&Spark 动手实践 13]Spark综合案例:简易电影推荐系统

  9. 从SVD到推荐系统

    最近在学习推荐系统(Recommender System),跟大部分人一样,我也是从<推荐系统实践>学起,同时也想跟学机器学习模型时一样使用几个开源的python库玩玩.于是找到了surp ...

随机推荐

  1. Android 对 properties文件的读写操作

    -. 放在res中的properties文件的读取,例如对放在assets目录中的setting.properties的读取:PS:之所以这里只是有读取操作,而没有写的操作,是因为我发现不能对res下 ...

  2. 2016 - 1 -19 初探NSOperation

    一:简介 1.NSOperation的作用: 配合NSOperation与NSOperationQueue也可以实现多线程. 2.NSOperation与NSOperationQueue实现多线程的步 ...

  3. 20145210姚思羽《Java程序设计》实验一实验报告

    实验一 Java开发环境的熟悉(Linux + Eclipse) 实验内容 1.使用JDK编译.运行简单的Java程序: 2.使用Eclipse 编辑.编译.运行.调试Java程序. 实验知识点 1. ...

  4. Selenium - IWebDriver 控制scroll bar到底部

    有时候我们需要控制页面滚动条上的滚动条,但滚动条并非页面上的元素,这个时候就需要借助js是来进行操作.一般用到操作滚动条的会两个场景: 注册时的法律条文需要阅读,判断用户是否阅读的标准是:滚动条是否拉 ...

  5. javascript js写特效日历

    <!doctype html public "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  6. NSFileHandle 、 沙箱机制 、 属性列表

    1 使用NSFilehandle进行数据读写 1.1 问题 NSFileManager用于实现对文件的操作,而NSFileHandle是IOS提供的对文件内容(二进制数据)进行操作的类,例如数据的读写 ...

  7. matlab 不要图像界面

    参考文献matlab -nodesktop

  8. csdn第九名

    编号:1025时间:2016年7月18日10:45:21功能:csdn第九名URL :http://blog.csdn.net/augusdi

  9. 深入理解Linux修改hostname 转

    当我觉得对Linux系统下修改hostname已经非常熟悉的时候,今天碰到了几个个问题,这几个问题给我好好上了一课,很多知识点,当你觉得你已经掌握的时候,其实你了解的还只是皮毛.技术活,切勿浅尝则止! ...

  10. 【转】Hbase shell 常用命令

    不定时更新常用好用命令. --------------------------------------------------------------------------------------- ...