亚马逊AWS官方博客

如何使用Apache Mahout在 Amazon Elastic Mapreduce上构建推荐系统

本篇博文首先简单介绍了机器学习,并给出了Apache Mahout项目的背景情况以及推荐系统中需要注意的一些细节,然后我们会构建一个电影推荐系统并写一个简单的web服务来提供给客户端查询结果。最后我们会列出学习和参与到Mahout社区中的一些方法。

机器学习

机器学习植根于人工智能,这意味着机器学习工具具有对数据问题的认知和自动决策能力,但是目前的机器学习算法并不包含真正意义上的计算思想。尽管如此,机器学习经常采用某种类型的自动化决策,并采用迭代的方式来最大化或最小化与模型性能相关的特征值。

机器学习领域包含许多主题和方法,通常有分类、聚类以及推荐等。

分类是基于其他已知(独立)变量的组合来预测未知(从属)变量的过程,例如预测银行的客户流失或某项音乐服务的订阅者数量。在这两种情况下,我们使用关于客户的已知变量来预测他们停止使用相关服务的趋势。 下表列出了几个可能的已知变量:

变量类型 例子
用户信息 城市、州、年龄、性别
行为方式 银行客户消费习惯、用户播放特定艺术家音乐的频率
环境因素 银行客户的费用评估、用户播放音乐时遭遇缓冲的频率

在进行分类工作时,我们通常针对包含从属变量真实取值的数据进行训练,然后通过比较留存测试数据的预测值和真实值的差距来评估模型的性能。

聚类是寻找事物集合和分组的过程。在几何学中,我们经常讨论N维空间中的聚类向量。举个例子,下面图中在二维空间中展示了四对由向量代表的人,每一个维度是一种支出类别,在本例中是娱乐支出和杂项支出。

左上角图中显示两人的支出数量是相近的,但是消费习惯则完全指向了不同的方向,所以这两人是不相似的。基于同样的原因,左下图中的两人就拥有相近的消费习惯。当然通常我们在分类时引入的维度是多于两个的,甚至是成千上万的维度,并且从数学上可以推广到任意有限的维度空间。

聚类的关键在于我们用来定义相似性的距离度量或者测量方法。一些常用的方法如欧几里德距离、余弦相似度(相似向量间的夹角的余弦值接近0,反之则接近1)以及Tanimoto系数(两个向量相同点占两个向量所有点的比例)。

推荐系统通常接收数据输入,通常是基于行为的数据,然后预测用户最终会倾向于哪个选项。过去十年推荐系统的流行很大程度上得益于Netflix Prize的推广,它在2006年到2009年间奖励给那些击败Netflix现有推荐系统的人。

推荐系统的性能以预测值和真实值之间的比较来得出,在生产系统中通常还结合A/B测试来进行优化。

Apache Mahout

大多数的Hadoop发行版本都包含了Apache Mahout,这其中就有Apache Bigtop和EMR。本质上Apache Mahout是一个机器学习的算法库,包含分类、聚类以及推荐等多种工具,可以被用于计算相似性和生成用户推荐等目的。Mahout使用Hadoop的Mapreduce框架来执行具体的计算过程,现在也支持新的计算框架,比如UC Berkeley’s AMPLab的Spark。

Mahout第一次bug修复发生在2008年1月,直到本篇博文写就之时,总共有1700个Jira tickets,其中54个依然是open状态。时至今日,社区中的贡献者和用户仍然在不停地针对代码和文档进行优化工作。

推荐系统

大多数人感觉到推荐系统的存在是从网页开始的,后台推荐系统会将结果显示在用户浏览网页的某一部分之上。这些包含推荐结果的网页帮助用户找到想买的商品、想听的音乐、想看的电影、想雇佣的人,甚至是约会的对象。

GroupLens研究团队于上个世纪90年代早期构建了USENET文章推荐系统,自此推荐系统技术开始了快速发展。随着时间的推移,推荐对象本身也发生了变化,除了新的文章,还有大量的在线行为值得分析,比如用户在网页上的点击链接行为;点赞和档案浏览;购物行为以及听音乐和看电影。

这些发展提供了基于用户行为进行推荐的可能性,能够帮助我们进一步改进推荐系统。 在USENET示例中,用户扫描作者和主题的文章列表,点击,阅读和关闭文章。 在线零售网站,购物者搜索产品,浏览产品页面,点击照片放大,阅读评论,并将产品添加到购物车。 在流媒体音乐网站上,音乐消费者搜索艺术家或专辑,播放曲目,通过曲目快速播放,并将艺术家添加到收藏夹,流媒体视频网站的工作方式类似。 在专业或个人联系的社交网站上,用户搜索并与其他人互动。 每个示例包括用户以某种方式与某些类型的项目进行交互的用户。

构建推荐系统

为了说明如何在EMR上通过Mahout建立分析型作业,我们决定构建一个电影推荐系统。我们会使用用户打分的电影评分数据,这些数据由GroupLens团队提供。推荐系统最终会为每一位用户推荐自己最感兴趣的电影。

1.  登录AWS账户

2.  配置EMR ruby客户端

3.  启动EMR集群

./elastic-mapreduce --create --alive --name mahout-tutorial --num-instances 4
--master-instance-type m1.xlarge --slave-instance-type m2.2xlarge --ami-version 3.1 --ssh

4.  获取电影评分数据

wget http://files.grouplens.org/datasets/movielens/ml-1m.zip

unzip ml-1m.zip

处理rating.dat文件,用”,”替换”::”,取出每一行的前3列

cat ml-1m/ratings.dat | sed 's/::/,/g' | cut -f1-3 -d, > ratings.csv

上传处理后的评分数据到HDFS

hadoop fs -put ratings.csv /ratings.csv

5.  启动推荐作业

mahout recommenditembased --input /ratings.csv --output recommendations --numRecommendations 10
--outputPathForSimilarityMatrix similarity-matrix --similarityClassname SIMILARITY_COSINE

6.  查看结果数据

hadoop fs -ls recommendations

hadoop fs -cat recommendations/part-r-00000 | head

你应该看到类似下面文件的输出结果

User ID           (Movie ID : Recommendation Strength) Tuples

35        [ 2067:5.0, 17:5.0, 1041:5.0, 2068:5.0, 2087:5.0, 1036:5.0, 900:5.0, 1:5.0, 2081:5.0, 3135:5.0 ]

70        [ 1682:5.0, 551:5.0, 1676:5.0, 1678:5.0, 2797:5.0, 17:5.0, 1:5.0, 1673:5.0, 2791:5.0, 2804:5.0 ]

105     [ 21:5.0, 3147:5.0, 6:5.0, 1019:5.0, 2100:5.0, 2105:5.0, 50:5.0, 1:5.0, 10:5.0, 32:5.0 ]

140     [ 3134:5.0, 1066:5.0, 2080:5.0, 1028:5.0, 21:5.0, 2100:5.0, 318:5.0, 1:5.0, 1035:5.0, 28:5.0 ]

175     [ 1916:5.0, 1921:5.0, 1912:5.0, 1914:5.0, 10:5.0, 11:5.0, 1200:5.0, 2:5.0, 6:5.0, 16:5.0 ]

210     [ 19:5.0, 22:5.0, 2:5.0, 16:5.0, 20:5.0, 21:5.0, 50:5.0, 1:5.0, 6:5.0, 25:5.0 ]

245    [ 2797:5.0, 3359:5.0, 1674:5.0, 2791:5.0, 1127:5.0, 1129:5.0, 356:5.0, 1:5.0, 1676:5.0, 3361:5.0 ]

280     [ 562:5.0, 1127:5.0, 1673:5.0, 1663:5.0, 551:5.0, 2797:5.0, 223:5.0, 1:5.0, 1674:5.0, 2243:5.0 ]

每一行的第一个数字是用户ID,后面的键值对是每一部电影的ID以及它的推荐得分。本例中推荐得分是5分制,从结果集(10个推荐结果)可以看出针对每位用户的推荐电影都是满分,所以我们应该进一步计算超过10个推荐结果的情况。

构建推荐服务

接下来我们要通过一个web服务来查看特定用户的推荐结果

1.  安装Twisted,Klein和Redis的Python模块

sudo easy_install twisted

sudo easy_install klein

sudo easy_install redis

2.  安装Redis并启动服务

wget http://download.redis.io/releases/redis-2.8.7.tar.gz

tar xzf redis-2.8.7.tar.gz

cd redis-2.8.7

make

./src/redis-server &

3.  构建一个web服务负责加载推荐结果数据到Redis并响应查询需求

把下面的代码放入一个文件内,如“hello.py”

from klein import run, route

import redis

import os

 

# Start up a Redis instance

r = redis.StrictRedis(host='localhost', port=6379, db=0)

 

# Pull out all the recommendations from HDFS

p = os.popen("hadoop fs -cat recommendations/part*")

 

# Load the recommendations into Redis

for i in p:

 

# Split recommendations into key of user id

# and value of recommendations

# E.g., 35^I[2067:5.0,17:5.0,1041:5.0,2068:5.0,2087:5.0,

#       1036:5.0,900:5.0,1:5.0,081:5.0,3135:5.0]$

k,v = i.split('t')

 

# Put key, value into Redis

r.set(k,v)

 

# Establish an endpoint that takes in user id in the path

@route('/<string:id>')

 

def recs(request, id):

# Get recommendations for this user

v = r.get(id)

return 'The recommendations for user '+id+' are '+v

 

 

# Make a default endpoint

@route('/')

 

def home(request):

return 'Please add a user id to the URL, e.g. http://localhost:8080/1234n'

 

# Start up a listener on port 8080

run("localhost", 8080)

4.  启动web服务

twistd -noy hello.py &

5.  使用用户ID 37测试服务

curl localhost:8080/37

6.  你应该看到如下的类似输出

针对37号用户的推荐结果是

[7:5.0,2088:5.0,2080:5.0,1043:5.0,3107:5.0,2087:5.0,2078:5.0,3108:5.0,1042:5.0,1028:5.0]

7.  当你完成所有操作时,别忘了关闭EMR集群

./elastic-mapreduce --list

 

j-UNIQUEJOBID      WAITING        ec2-AA-BB-CC-DD.compute-1.amazonaws.com         mahout-tutorial

 

./elastic-mapreduce --terminate j-UNIQUEJOBID

8.  确认是否关闭

./elastic-mapreduce --list

 

j-UNIQUEJOBID     SHUTTING_DOWN     ec2-AA-BB-CC-DD.compute-1.amazonaws.com         mahout-tutorial

几分钟之后

./elastic-mapreduce --list

 

j-UNIQUEJOBID     TERMINATED     ec2-AA-BB-CC-DD.compute-1.amazonaws.com         mahout-tutorial

总结

恭喜!你已经构建了一个简单的推荐系统,其中包含了开发更复杂推荐系统的关键模块:

  • 采集原始数据
  • 进行数据预处理
  • 运行分析作业
  • 分析结果数据
  • 通过web服务发布结果数据

你可以从以下几个途径进一步探索本示例,比如替换SIMILARITY_COSINE为VectorSimilarityMeasure中的另外一个度量,以此来通过不同的距离度量而不是余弦相似性来判断两个用户是否喜欢同一类型的电影。当然你还可以让推荐结果变得更容易解读,将电影ID和电影名字以及介绍关联在一起返回给查询请求是一个不错的主意。

Mahout还包含了其他的推荐算法,其中还包括和Netflix奖相关的算法:交替最小二乘法(ALS)。该项目还包含了用于分类和聚类的算法,这些算法被封装为一个个的工具,从命令行就能访问和使用。

Mahout社区倡导积极与用户和开发人员合作。 您可以通过在EMR上尝试使用Mahout,或者通过下载自己的副本并在本地或在自己的集群上运行Mahout。 您可以提交有关如何使用工具的问题,并向用户邮件列表中提出可能的错误,您还可以在这里讨论开发人员邮件列表中的问题,从而为开发做出贡献。 如果您遇到要更正的错误或者新的功能要更新到项目中,请您在项目Jira页面上提供故障单并说明具体问题。如果您想要在页面顶部看到创建问题按钮,请确保创建一个帐户。

 

作者介绍

刘磊,曾供职于中国银联电子支付研究院,期间获得上海市科技进步一等奖,并申请7项国家发明专利。现任职于AWS中国专业服务团队,致力于为客户提供基于AWS服务的专业大数据解决方案、项目实施工作以及咨询服务。