协同过滤的R语言实现及改进
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~
协同过滤算法是推荐系统最常用的算法之一,本文将介绍一种方法来使它可以在大型数据集上快速训练。
协同过滤算法(CF)是构建推荐系统时最常用的技术之一。它可以基于收集到的其他用户的偏好信息(协同)来自动地预测当前用户的兴趣点。协同过滤算法主要分为两种:基于记忆(memory-based)的协同过滤算法和基于模型(model-based)的协同过滤算法。一般来说,将两者融合可以获得预测准确度上的提升。
在本文中,我们将关注基于记忆的协同过滤算法并详细讨论其推导和集成的细节。我们将展示我们最近在改进经典协同过滤算法上的一些工作,使其可以在大规模数据集上实施并达到缩短训练时间的效果。我们的算法是用R语言实现的,但是它也可以被移植到其他语言上。
基于记忆的协同算法又可以分为下面两种:
- 基于用户的协同过滤:如果想要预测用户U对物品I的评价,可以借助其他和U相似的用户的评价来进行预测。这么做的原因是我们认为相同品味的用户会对同一事物做出相似的评价。
- 基于物品的协同过滤:如果想要预测用户U对物品I的评价,可以借助该用户对和物品I相似的物品的评价进行预测。这么做的原因是我们认为同一用户对相似事物做出的评价应当是接近的。
下面让我们从一个例子出发来观察基于用户的协同过滤是如何实现的。下面给出了计算评价ru,i的公式,ru,i 即用户u对物品i的评分。我们把和用户u相似的用户(用户之间的相似度信息通过一个矩阵维护,sim为相似度计算函数)的评价汇总在一起,从公式可以看出,用户间的相似度越大,其中一个用户对另一个用户评价的预测结果影响程度就越大。w为计算最终评分所需的归一化权重因子。


为了验证当前推荐系统的性能,我们需要在测试集上进行预测。为了更高的效率,计算会借助矩阵乘法完成,而不是通过循环的方式完成。下面的图片展示了矩阵计算的过程:

上图展示了预测用户U2对物品I3评分时的计算过程。为了计算预测结果,我们需要知道其他用户对I3的评分(第一个矩阵中蓝色高亮的一行)以及其他用户与U2的相似度(第二个矩阵中蓝色高亮的一列;注意这里我通过设置相似度矩阵对角线的元素为零来避免数据泄露)。到此,我们可以写出用户U2对物品I3评分的计算公式:

计算结果将被存储在第三个矩阵的蓝色单元当中。不过这里还没有进行归一化,为了得到最终的预测结果,我们还需要计算归一化因子w,计算公式已经在上面给出。
基于记忆的协同过滤的主要优点与它的可扩展性和性能密不可分。举例来说,如果这里有500,000个用户,那么我们需要计算所有用户对之间的相似度(最坏的情况需要计算1200亿个值)。显然这需要大量的内存和处理时间,下面我们将尝试用R语言(当然你也可以使用别的编程语言 : ) )对协同过滤算法进行一些改进从而解决这一问题。
我们将用R推荐系统中最常用的包之一——recommenderlab与我们的实现进行比较。这个比较是在4核i7,16G内存的小型电脑上完成的,使用的数据集是 MovieLens 100k, MovieLens 1m, MovieLens 10m。后面,你会看到我们的集成具有两大优势:
- 它的速度有显著的提升。
- 可以支持在庞大的数据集上构建推荐系统,当 recommenderlab 报出内存溢出的错误时,我们的实现仍然可以正常工作。
执行效率的提升
评分矩阵通常是一个庞大(有大量的用户和物品)的稀疏(每个用户往往只对少量的物品打分)矩阵。在R语言中,我们可以通过专门的数据结构来存储稀疏矩阵,缺失值不会被重复存储在内存当中。一般来说,有超过90%的值是缺失的,所以这种做法可以节省下大量的内存。我们的实现以及recommenderlab中都采用了矩阵的稀疏表示。
我们实现的基于用户的协同过滤主要包含以下步骤(基于物品的协同过滤也是如此):
- 导入评分矩阵
- 开发者决定是否对用户评分进行标准化。这一步通常会提高准确率。这是因为标准化可以移除用户评分的偏置,举例来说就是有些用户总是会打高分或者总是打低分。
- 计算用户之间的相似度。
- 使用KNN算法。(只保留k个最相似的用户,即在用户的评分预测计算中,相似度矩阵每列只保存最高的k个值)k值需要开发者手动指定。
- 计算预测值并进行反归一化得到最终的预测评分。
recommenderlab也使用了与上面相同的过程。但是我们在这些过程中引入了一些改进从而显著地提升了算法执行效率。其中主要的两个优化如下:
- 对大型稀疏矩阵的相似性计算进行了优化。具体方法可以参考Recommender Systems: Matrix Operations for Fast Calculation of Similarities。
- 相似度矩阵的k近邻算法不是通过循环完成的,我们采用了更优的实现。首先,我们对相似度矩阵进行了分组(列拆分),然后在每组当中通过函数找到最高的k个值。这个函数已经在R 'data.table'包中被实现。依此,我们通过每组的信息得到了相似度矩阵中每列最大的k个值。
验证
我们通过以下步骤来讲我们的实现与recommenderlab进行比较:
- 10折交叉验证。每次训练使用90%的数据来创建模型、计算相似度,10%的数据用来测试。任一用户和物品都被划分到了训练集或者测试集当中。
- 中心归一化(Center Normalization),用户评分的平均值将从他的实际评分中扣除。
- 用余弦距离来计算相似度。
- k:选取的近邻样本数为100,300以及样本总值。
- rmse:算法的误差(root-mean-square error,均方根误差)
- 执行时间 (exec time) :算法的执行时间,以秒为单位。
在100k MovieLens 数据集上的比较
该数据集包括943个用户和1682个电影(物品),100,000个评分。
基于用户的协同过滤

基于物品的协同过滤

在1M MovieLens 数据集上的比较
该数据集包括6040个用户和3706个电影(物品),100,000个评分。
基于用户的协同过滤

基于物品的协同过滤

可以看到我们的实现在运行速度上更快,精准度也更高(达到了更低的rmse)。其中我们最关注的方面——速度得到了显著的提升。
然而,速度只是经典实现中问题的一个方面。我们在上面提到过,协同过滤的另一个问题是空间占用,即当矩阵过于庞大时我们会面临内存不足的问题。在下一节中,我们将提出一个可行的方案来使传统的协同过滤算法可以被应用在庞大的数据集上。
在庞大的数据集上构建推荐算法
在下面的测试中,我们使用MovieLens 10m的数据集。但是正如上面提到的,我们测试的机器只有16GB的内存并且使用了十折的交叉验证。'recommenderlab'的实现在建立用户相似度矩阵的过程中就因为内存不足而退出了。
在我们的实现当中,我们通过对矩阵进行切分解决了这一问题。即我们不是一次性计算所有的预测值,而是一块一块完成的。下面给出基于用户的协同过滤的实现过程(基于物品的协同过滤同理):
- 取出用户评分矩阵的前N行。在下图图示中,我们提取了I1:I4部分的切片。
- 取出M个用户并计算他们与其他用户的相似度。在下图图示中,我们提取了U1:U2部分的切片。
- 根据公式计算第一步得到的矩阵和第二步得到的矩阵的乘积。结果为MxN的矩阵,矩阵的元素为归一化之前的预测结果。在图示中,计算结果为用户U1:U2对物品I1:I4的评分。
- 重复以上三步计算不同的MxN单元只到结果矩阵被填满。

在10M MovieLens 数据集上的结果
该数据集包括69,878个用户和10,677个电影(物品),10,000,054个评分。我们通过以下步骤来检验我们的算法:
- 10折交叉验证。
- 中心归一化(Center Normalization)。
- 用余弦距离来计算相似度。
- k:选取的近邻样本数为100,1000。
- Chunk size:在算法当中每次取出的块的行列大小。具体的取值取决于你的硬件条件。
比较的结果可以从下面的表格找到,包含:
- rmse:算法的误差(root-mean-square error)
- 执行时间:算法的执行时间,以分钟为单位。
- num predictions:协同过滤能够预测的结果数。通常来说,协同过滤不是一定能够得到预测结果的(比方说与之相似的用户也都还没有给该商品打分)
基于物品的协同过滤

基于用户的协同过滤

正如上图展示的结果所示,我们实现的算法完满地完成了任务。现在我们可以在16Gb内存配置的机器上构建推荐系统了。基于用户和基于物品的协同过滤分别耗费了10分钟和200分钟的时间,基于用户的协同过滤花费了更多的时间是因为数据集中有了更多的用户,这要求我们计算更多的相似度。
通过现在的实现,当我们需要为一个或者多个用户提供实时的推荐时,相似度的计算以及结果的预测将迅速很多,因为我们可以只选取少部分用户进行操作。在MovieLens 10m的数据集上,基于用户的协同过滤只需要一秒就可以对一个用户或者多个用户生成预测,但是基于物品的协同过滤则需要30s左右,这是因为我们需要更多的时间来计算相似度矩阵。这里还可以通过将相似度矩阵存储为模型,不再进行即时的训练从而达到线上预测效果的加速。这个算法实现的一个显著优点就是可扩展性,由于我们将原数据集切分为了不同块进行计算,所以可以进一步实现并行化。我们接下来的工作之一就是在分布式框架上实现并测试这一方法。
总结
在本文中,我们提出了一种新的方法来改进基于记忆的传统协同过滤实现。本文的代码可以从Github上获取。
问答
相关阅读
此文已由作者授权腾讯云+社区发布,原文链接:https://cloud.tencent.com/developer/article/1133912?fromSource=waitui
协同过滤的R语言实现及改进的更多相关文章
- 如何高效地学好R语言?
如何高效地学好R语言? 学R语言主要在于5点三阶段: 第一阶段有一点:基础的文件操作(read.*, write.*).数据结构知识,认识什么是数据框(data.frame).列表(list).矩阵( ...
- R语言书籍的学习路线图
现在对R感兴趣的人越来越多,很多人都想快速的掌握R语言,然而,由于目前大部分高校都没有开设R语言课程,这就导致很多人不知道如何着手学习R语言. 对于初学R语言的人,最常见的方式是:遇到不会的地方,就跑 ...
- R 语言的优劣势是什么?
R 语言的优劣势是什么? 2015-05-27 程序员 大数据小分析 R,不仅仅是一种语言 本文原载于<程序员>杂志2010年第8期,因篇幅所限,有所删减,这里刊登的是全文. 工欲善其 ...
- 皮尔逊相似度计算的例子(R语言)
编译最近的协同过滤算法皮尔逊相似度计算.下顺便研究R简单使用的语言.概率统计知识. 一.概率论和统计学概念复习 1)期望值(Expected Value) 由于这里每一个数都是等概率的.所以就当做是数 ...
- Andrew Ng机器学习课程笔记--week9(下)(推荐系统&协同过滤)
本周内容较多,故分为上下两篇文章. 本文为下篇. 一.内容概要 1. Anomaly Detection Density Estimation Problem Motivation Gaussian ...
- R语言实现关联规则与推荐算法(学习笔记)
R语言实现关联规则 笔者前言:以前在网上遇到很多很好的关联规则的案例,最近看到一个更好的,于是便学习一下,写个学习笔记. 1 1 0 0 2 1 1 0 0 3 1 1 0 1 4 0 0 0 0 5 ...
- 皮尔森相似度计算举例(R语言)
整理了一下最近对协同过滤推荐算法中的皮尔森相似度计算,顺带学习了下R语言的简单使用,也复习了概率统计知识. 一.概率论和统计学概念复习 1)期望值(Expected Value) 因为这里每个数都是等 ...
- 基于用户协同过滤--UserCF
UserCF 本系列文章主要介绍推荐系统领域相关算法原理及其实现.本文以项亮大神的<推荐系统实践>作为切入点,介绍推荐系统最基础的算法(可能也是最好用的)--基于用户的协同过滤算法(Us ...
- R语言 一套内容 从入门 到放弃
[怪毛匠子整理] 1.下载 wget http://mirror.bjtu.edu.cn/cran/src/base/R-3/R-3.0.1.tar.gz 2.解压: tar -zxvf R-3.0. ...
随机推荐
- python---购物车
购物车功能如下: 1. 输入收入多少,购买商品 2. 打印购物清单,根据清单选择商品: 3. 结算,打印购物清单及总金额 # -*- coding:utf-8 -*- # LC goods=[[1,' ...
- Web安全测试工具小集
从此文开始搬家CSDN的博客 本文内容全部节选自<Ajax Security>第14章内容推荐工具: 模糊黑盒测试工具(Fuzzer): Popular free fuzzers incl ...
- DataReport使用手记
06年的一篇blog,转过来: 前几天,帮同事改一个VB的课业程序,具体任务就是在程序中添加报表功能,由于考虑到部署环境的问题,所以没有采用我以前惯用的Excel实现,而采用了同事提出的VB自带的Da ...
- Java经验杂谈(2.对Java多态的理解)
多态是面向对象的重要特性之一,我试着用最简单的方式解释Java多态: 要正确理解多态,我们需要明确如下概念:・定义类型和实际类型・重载和重写・编译和运行 其中实际类型为new关键字后面的类型. 重载发 ...
- PHP判断客户端是否使用代理服务器及其匿名级别
要判断客户端是否使用代理服务器,可以从客户端所发送的环境变量信息来判断. 具体来说,就是看HTTP_VIA字段,如果这个字段设置了,说明客户端使用了代理服务器. 匿名级别可以参考下表来判断. 给出一个 ...
- Effective C++ 读书笔记(39-45)
条款三十九:明智而审慎的使用private继承 1.C++裁定凡是独立(非附属)对象都必须有非零大小. class Empty{};//没有数据,所以其对象应该不使用任何内存 class HoldAn ...
- IDEA修改编辑背景图片
1.打开File -> Setting -> Plugs -> 搜索BackgroundImage. 然后安装.如图 2.按快捷键ctrl+shift+A,搜索set backgro ...
- __new__()方法的使用和实例化
Python中__new__()方法的使用和实例化 1 2 new()是在新式类中新出现的方法,它作用在构造方法init()建造实例之前,可以这么理解,在Python 中存在于类里面的构造方法init ...
- java中你不知道的字符串知识!!!
声明:这是上次写完String和StringBuffer后的补充(看上次的请复制链接在搜索栏粘贴访问) 链接:http://www.cnblogs.com/ytsbk/p/7420581.html 一 ...
- i++ 和 ++i;&& 和 &
一.算数运算符(自增运算符i++.自减运算符i++) ※ i++是先赋值(计算)再加1 :++i是先加1再赋值(计算) : int m = 5; boolean bool = ++m > 5 ...