转载自 JavaChen Blog,作者: Junez
本文主要记录最近一段时间学习和实现Spark MLlib中的协同过滤的一些总结,希望对大家熟悉Spark ALS算法有所帮助。
更新:
【2016.06.12】Spark1.4.0中MatrixFactorizationModel提供了recommendForAll方法实现离线批量推荐,见 SPARK-3066。
测试环境
为了测试简单,在本地以local方式运行Spark,你需要做的是下载编译好的压缩包解压即可,可以参考 Spark本地模式运行。
测试数据使用 MovieLens的 MovieLens 10M数据集,下载之后解压到data目录。数据的格式请参考README中的说明,需要注意的是ratings.dat中的数据被处理过, 每个用户至少访问了20个商品。
下面的代码均在spark-shell中运行,启动时候可以根据你的机器内存设置JVM参数,例如:
|
|
预测评分
这个例子主要演示如何训练数据、评分并计算根均方差。
准备工作
首先,启动spark-shell,然后引入mllib包,我们需要用到ALS算法类和Rating评分类:
|
|
Spark的日志级别默认为INFO,你可以手动设置为WARN级别,同样先引入log4j依赖:
|
|
然后,运行下面代码:
|
|
加载数据
spark-shell启动成功之后,sc为内置变量,你可以通过它来加载测试数据:
|
|
接下来解析文件内容,获得用户对商品的评分记录:
|
|
查看第一条记录:
|
|
我们可以统计文件中用户和商品数量:
|
|
可以看到如下输出:
|
|
你可以对评分数据生成训练集和测试集,例如:训练集和测试集比例为8比2:
|
|
这里,我们是将评分数据全部当做训练集,并且也为测试集。
训练模型
接下来调用 ALS.train()
方法,进行模型训练:
|
|
训练完后,我们看看model中的用户和商品特征向量:
|
|
评测
我们要对比一下预测的结果,注意:我们将 训练集当作测试集来进行对比测试。从训练集中获取用户和商品的映射:
|
|
显然,测试集的记录数等于评分总记录数,验证一下:
|
|
使用推荐模型对用户商品进行预测评分,得到预测评分的数据集:
|
|
查看其记录数:
|
|
将真实评分数据集与预测评分数据集进行合并,这样得到用户对每一个商品的实际评分和预测评分:
|
|
然后计算根均方差:
|
|
上面这段代码其实就是 对测试集进行评分预测并计算与实际评分的相似度,这段代码可以抽象为一个方法,如下:
|
|
除了RMSE指标,我们还可以计算AUC以及Mean average precision at K (MAPK),关于AUC的计算方法,参考 RunRecommender.scala,关于MAPK的计算方法可以参考 《Packt.Machine Learning with Spark.2015.pdf》一书第四章节内容,或者你可以看本文后面内容。
保存真实评分和预测评分
我们还可以保存用户对商品的真实评分和预测评分记录到本地文件:
|
|
上面这段代码先按用户排序,然后重新分区确保目标目录中只生成一个文件。如果你重复运行这段代码,则需要先删除目标路径:
|
|
我们还可以对预测的评分结果按用户进行分组并按评分倒排序:
|
|
给一个用户推荐商品
这个例子主要是记录如何给一个或大量用户进行推荐商品,例如,对用户编号为384的用户进行推荐,查出该用户在测试集中评分过的商品。
找出5个用户:
|
|
查看用户编号为384的用户的预测结果中预测评分排前10的商品:
|
|
查看该用户的评分记录:
|
|
可以看到该用户对22个商品评过分以及浏览的商品是哪些。
我们可以该用户对某一个商品的实际评分和预测评分方差为多少:
|
|
如何找出和一个已知商品最相似的商品呢?这里,我们可以使用余弦相似度来计算:
|
|
以2055商品为例,计算实际评分和预测评分相似度
|
|
找到和该商品最相似的10个商品:
|
|
显然第一个最相似的商品即为该商品本身,即2055,我们可以修改下代码,取前k+1个商品,然后排除第一个:
|
|
接下来,我们可以计算给该用户推荐的前K个商品的平均准确度MAPK,该算法定义如下(该算法是否正确还有待考证):
|
|
给该用户推荐的商品为:
|
|
给该用户预测的商品为:
|
|
最后的准确度为:
|
|
批量推荐
你可以评分记录中获得所有用户然后依次给每个用户推荐:
|
|
这种方式是遍历内存中的一个集合然后循环调用RDD的操作,运行会比较慢,另外一种方式是直接操作model中的userFeatures和productFeatures,代码如下:
|
|
这种方式其实还不是最优方法,更好的方法可以参考 Personalised recommendations using Spark,当然这篇文章中的代码还可以继续优化一下。我修改后的代码如下,供大家参考:
|
|
2015.06.12 更新:
悲哀的是,上面的方法还是不能解决问题,因为矩阵相乘会撑爆集群内存;可喜的是,如果你关注Spark最新动态,你会发现Spark1.4.0中MatrixFactorizationModel提供了recommendForAll
方法实现离线批量推荐,详细说明见 SPARK-3066。因为,我使用的Hadoop版本是CDH-5.4.0,其中Spark版本还是1.3.0,所以暂且不能在集群上测试Spark1.4.0中添加的新方法。
如果上面结果跑出来了,就可以验证推荐结果是否正确
。还是以384用户为例:
|
|
接下来,我们可以计算所有推荐结果的准确度了,首先,得到每个用户评分过的所有商品:
|
|
然后,预测的商品和实际商品关联求准确度:
|
|
其实,我们也可以使用Spark内置的算法计算RMSE和MAE:
|
|
计算推荐2000个商品时的准确度为:
|
|
保存和加载推荐模型
对与实时推荐,我们需要启动一个web server,在启动的时候生成或加载训练模型,然后提供API接口返回推荐接口,需要调用的相关方法为:
|
|
model中的userFeatures和productFeatures也可以保存起来:
|
|
总结
本文主要记录如何使用ALS算法实现协同过滤并给用户推荐商品,以上代码在Github仓库中的 ScalaLocalALS.scala文件。
如果你想更加深入了解Spark MLlib算法的使用,可以看看 Packt.Machine Learning with Spark.2015.pdf这本电子书并下载书中的源码,本文大部分代码参考自该电子书。