出处http://blog.csdn.net/zhongkejingwang/article/details/43083603

前面文章SVD原理及推导已经把SVD的过程讲的很清楚了,本文介绍如何将SVD应用于推荐系统中的评分预测问题。其实也就是复现Koren在NetFlix大赛中的使用到的SVD算法以及其扩展出的RSVD、SVD++。

记得刚接触SVD是在大二,那会儿跟师兄在做项目的时候就用到这个东西,然后到大三下学期刚好百度举办了一个电影推荐算法大赛,跃跃欲试地参加了,当时就用的SVD而且只会用这个,后来觉得效果还不错,接着就又找来了Koren的论文,看了一下把SVD++也实现了,把两者结果融合得到不少的提升。下面是最终比赛结果:

其实这个最终结果是和第11名组队后融合了他的结果,所以成绩是一样的。但是单纯地使用SVD、SVD++融合得到的结果最好的也能到0.606左右,这个结果已经很牛逼了,记得当时0.6111在榜上维持了两个多星期的No.1。当时的遗憾就是没能实现更多的SVD扩展算法,导致融合的结果没法再提升,代码也由于时间仓促写的很臃肿,前段时间有空把算法重写了一下并实现了更多的扩展算法,已经共享到github上了,有兴趣的可以看看:https://github.com/jingchenUSTC/SVDRecommenderSystem

下面开始介绍SVD算法,假设存在以下user和item的数据矩阵:

这是一个极其稀疏的矩阵,这里把这个评分矩阵记为R,其中的元素表示user对item的打分,“?”表示未知的,也就是要你去预测的,现在问题来了:如何去预测未知的评分值呢?上一篇文章用SVD证明了对任意一个矩阵A,都有它的满秩分解:

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

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

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

假设已知的评分为:

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

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

只要通过训练把SSE降到最小那么P、Q就能最好地拟合R了。那又如何使SSE降到最小呢?下面介绍一个常用的局部优化算法。

梯度下降法

为了说明梯度下降法,我找了一张PPT:

也就是说如果要最小化目标函数,必须往其负梯度方向搜索。这就是梯度下降法,注意它是一个局部优化算法,也就是说有可能落到局部最优解而不是全局最优解。

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)。

ASVD

全称叫Asymmetric-SVD,即非对称SVD,其预测式子为

R(u)表示用户u评过分的商品集合,N(u)表示用户u浏览过但没有评过分的商品集合,Xj和Yj是商品的属性。这个模型很有意思,看预测式子,用户矩阵P已经被去掉了,取而代之的是利用用户评过分的商品和用户浏览过尚未评分的商品属性来表示用户属性,这有一定的合理性,因为用户的行为记录本身就能反应用户的喜好。而且,这个模型可以带来一个很大的好处,一个商场或者网站的用户数成千上万甚至过亿,存储用户属性的二维矩阵会占用巨大的存储空间,而商品数却没有那么多,所以这个模型的好处显而易见。但是它有个缺点,就是迭代时间太长了,这是可以预见的,以时间换空间嘛。

同样的,需要计算其各个参数的偏导数,求出更新式,显然,bu和bi的更新式和刚才求出的一样

其中的向量z等于下面这坨

现在要求qik、x和y的更新式,在这里如果把z看成Pu则根据前面求得的更新式可得qik的更新式:

求Xj的导数需要有点耐心,SSE的第二项对Xj的导数很容易得到,现在来求第一项对Xj的导数:

上式求和符中的i,j都是属于R(u)的,可以看到Zm只有当m==k时才与Xjk有关,所以

因为

所以

所以得到SSE对Xjk的导数为

同理可得SSE对Yjk的导数为

所以得到Xjk和Yjk的更新方程

这就叫ASVD。。。。。。

SVDPP

最后这个模型也是Koren文章中提到的,SVDPlusPlus(SVD++),它的预测式子为

这里的N(u)表示用户u行为记录(包括浏览的和评过分的商品集合)。看了ASVD的更新式推导过程再来看这个应该很简单,Puk和qik的更新式子不变,Yjk的更新式子和ASVD的更新式一样:

这些就是Koren在NetFlix大赛中用到的SVD算法,最后,还有一个需要提的:

对偶算法

将前面预测公式中的u和i调换位置得到其对偶算法,对于RSVD而言,u和i的位置是等价的、对称的,所以其对偶算法和其本身没有区别,但对于ASVD和SVD++则不同,有时候对偶算法得到的结果更加精确,并且,如果将对偶算法和原始算法的预测结果融合在一起的话,效果的提升会让你吃惊!

对偶的ASVD预测公式:

这里R(i)表示评论过商品i的用户集合,N(i)表示浏览过商品i但没有评论的用户集合。由于用户数量庞大,所以对偶的ASVD会占用很大空间,这里需要做取舍了。

对偶的SVD++预测公式:

这里N(i)表示对商品i有过行为(浏览或评分)的用户集合。

实现这一对偶的操作其实很简单,只要读取数据的时候把用户id和商品id对调位置即可,也就是将R矩阵转置后再训练。

暂时实现的算法就这些,代码都在github上了,有兴趣的可以看看,地址:https://github.com/jingchenUSTC/SVDRecommenderSystem

SVD在推荐系统中的应用详解以及算法推导的更多相关文章

  1. php中关于引用(&)详解

    php中关于引用(&)详解 php的引用(就是在变量或者函数.对象等前面加上&符号) 在PHP 中引用的意思是:不同的变量名访问同一个变量内容. 与C语言中的指针是有差别的.C语言中的 ...

  2. JavaScript正则表达式详解(二)JavaScript中正则表达式函数详解

    二.JavaScript中正则表达式函数详解(exec, test, match, replace, search, split) 1.使用正则表达式的方法去匹配查找字符串 1.1. exec方法详解 ...

  3. AngularJS select中ngOptions用法详解

    AngularJS select中ngOptions用法详解   一.用法 ngOption针对不同类型的数据源有不同的用法,主要体现在数组和对象上. 数组: label for value in a ...

  4. 【转载】C/C++中extern关键字详解

    1 基本解释:extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义.此外extern也可用来进行链接指定. 也就是说extern ...

  5. oracle中imp命令详解 .

    转自http://www.cnblogs.com/songdavid/articles/2435439.html oracle中imp命令详解 Oracle的导入实用程序(Import utility ...

  6. Android中Service(服务)详解

    http://blog.csdn.net/ryantang03/article/details/7770939 Android中Service(服务)详解 标签: serviceandroidappl ...

  7. python中threading模块详解(一)

    python中threading模块详解(一) 来源 http://blog.chinaunix.net/uid-27571599-id-3484048.html threading提供了一个比thr ...

  8. Android中mesure过程详解

    我们在编写layout的xml文件时会碰到layout_width和layout_height两个属性,对于这两个属性我们有三种选择:赋值成具体的数值,match_parent或者wrap_conte ...

  9. Android中Intent组件详解

    Intent是不同组件之间相互通讯的纽带,封装了不同组件之间通讯的条件.Intent本身是定义为一个类别(Class),一个Intent对象表达一个目的(Goal)或期望(Expectation),叙 ...

随机推荐

  1. 跨平台图表控件TeeChart使用教程:导入XML数据

    TeeChart的最新版中包含了一个自动加载XML数据的新组件.这个组件的名字叫做TTeeXMLSource,用户可以在TeeXML.pas unit中找到这个组件. 加载XML图表所需的最小属性是& ...

  2. 解决 MVC4 Code First 数据迁移 数据库发生更改导致调试失败解决方法(二)

    文章转载自:http://www.cnblogs.com/amoniyibeizi/p/4486617.html 前几天学MVC过程中,遇到更改Model类以后,运行程序就会出现数据已更改的问题导致调 ...

  3. KinSlideshow焦点图轮播插件

    KinSlideshow默认设置效果代码: *焦点图显示的标题为 img 中 alt 属性中的文字 *当只有一张图片时不显示按钮,但也会有无缝切换效果 * jquery ..以上版本 jvascrip ...

  4. WiFi调试手机

     转自http://blog.csdn.net/Yejianyun1/article/details/55511726 使用场景: 1.多设备执行测试用例 2.数据线无法满足使用 电脑与手机的网络需要 ...

  5. org.springframework.beans.factory.BeanNotOfRequiredTypeException

    写一个代码:关于Spring Bean的装配.基于annotation实现的范例代码. 出现了错误: 十一月 14, 2018 4:51:01 下午 org.springframework.conte ...

  6. *459. Repeated Substring Pattern (O(n^2)) two pointers could be better?

    Given a non-empty string check if it can be constructed by taking a substring of it and appending mu ...

  7. Selenium入门12 鼠标和键盘事件

    1 鼠标 集成在webdriver.ActionChains.单击.双击.右击.拖放等等.   2 键盘 引入包from selenium.webdriver.common.keys import K ...

  8. AJAX(四):XHR2支持的方法

    XMLHttpRequest 1级只是把已有的XHR对象的实现细节描述了出来.而XMLHttpRequest 2级则进一步发展了XHR FormDataFormData为序列化表单以及创建与表单格式相 ...

  9. apache以天为单位生成日志

    编辑/etc/httpd/conf.d/vhost.conf,修改ErrorLog和CustomLog: ErrorLog "|rotatelogs /var/log/httpd/phpdd ...

  10. jeDate日期控件

    http://www.jayui.com/jedate/     这是日期控件官网,可以去里面下载使用 前台 <%@ Page Language="C#" AutoEvent ...