一直以为在Mahout的在分布式上做了很多东西,很高深。最近一段时间由于工作中要实现一些分布式算法,所以硬着头皮看了一下它的源码。当时我匆忙的看过KMeans的实现,这次我的工作是在搜索引擎日志记录中找相似query。我是按照query以及它对应的点击商品来进行相似query匹配的,其实就是一个协同推荐问题。
开始时,我实现了一个单机版的算法,每个query对整个query集合进行相似度计算,这相当于一个笛卡尔积的计算。在独立query较小的情况下,这个计算了还可以接受,但是一旦query的量表达,例如10w级别,单机就无法容忍了。
所以很有必要把这个计算搬到hadoop上来做。于是我就分析了一下Mahout taste部分的源码。分享下来。
Taste提交job的流程:
1. 获得job处理所需要的样本信息;推荐引擎定义的有几种文件格式,有从数据库读取,有从文件系统里读取,我觉得从文件系统里最方便,可能是我现在使用Hadoop的缘故吧。不同的数据来源会由不同的DataModel来进行数据读取。例如文件系统的是FileDataModel,文件系统内的文件格式是 userID ItemID value,中间通过”\t”或者”,”进行分割。数据库系统的读取也是通过指定userID ItemID value相对于的数据表字段就可以了。这些熟悉Taste的人应该都了解。
2. 获取需要得到相似对象的userID队列,这个队列应该是每行一个用户ID
3. 将第一个获取到的样本信息全部下载到本地
4. 在本地构建一个推荐引擎
5. 对map中的每一个userID,通过推荐引擎给其推荐相似对象
看到这个流程后,大家也就明白了,所谓的分布式,其实只是对需要计算相似对象的userID进行了分布运算而已,而计算相似度的本身还是在本地构建推荐引擎,然后计算。
这里最要注意的问题是,这个userIDS列表,我们一定不要再Map中就直接做计算,因为默认的通过TextFileFormat中,Hadoop按照文件的大小来划分Map,所以如果在Map做计算的话,很有可能所有的userID寻找相似对象的工作都在一个Map或者少量的几个Map中做了(笔者就犯了这个错误,结果Map就启动了两个,计算速度并没有比单机快多少)。
具体解决办法由两种
1. 在Map端,使用NLineFileFormat对userIDS进行切割,这样一个列表就可以划分到多个Map中了;
2. 在Reduce端,如果我们在Map端不做任何工作,仅仅是将userIDS输出到Reduce中,这时通过对userID的hash,userIDS这个列表就会被划分到多个Reduce中了,那么计算的速度自然就提高了。
笔者在对10万的记录做笛卡尔积时,1G内存,CPU不详(公司的机器应该也不差)。几个小时下来也不能结束。将计算搬到Hadoop上,通过第二种方案,计算时间被压缩到了10分钟以内。