Mahout并行基于物品的协同过滤算法源码分析(Distributed item-based CF)

mahout的taste框架是协同过滤算法的实现。它支持DataModel,如文件、数据库、NoSQL存储等,也支持Hadoop的MapReduce。这里主要分析的基于MR的实现。

基于MR的CF实现主要流程就在org.apache.mahout.cf.taste.Hadoop.item.RecommenderJob类中(注意mahout有两个RecommendJob,要看清楚是哪一个包)。这 个类的run方法就包含了所有的步骤。从上到下,完整的其实有10步(中间计算item相似度其实拆分成了3个job,我们也当做是一个phase吧), 也就是说,如果指定了所有的必要参数,运行一次item-based CF算法,会执行12个JOB,当然有的步骤是可以忽略的,下面会讲。以下就是详细的每一步骤的分析


phase1: itemIDIndex

这步主要是将itemId转成一个int。这里设计上其实有点小问题,如果item的数量非常多,比如超过int的最大值,那就有可能会出现重合了。所以用long其实更为合适。

   input:用户评分文件(这也是我们最原始的输入了),格式一般为:userId t itemId t score。注意输入必须是textfile的。可能是为了方便测试吧,mahout的很多包默认输出都是textfile格式的。

   map:(index, itemId)

   reduce: (index, itemId)

phase2: toUserVector

   input:用户评分文件

   param: --userBooleanData如果这个参数为true,则会忽略评分列,对于买或不买这类数据有时需要指这定这个值。

   map: (userId, itemId,pref)

   reduce: 以用户为key,输出成向量形式è (userId, VectorWritable<itemId, pref>)

phase3: countUser,计算用户数

   map: (userId)

   reduce: 输出总用户数count

phase4: maybePruneAndTranspose

   input: phase2的输出:userVector

   param: --maxCooccurrences

   map: (userId,Vector<itemId, pref>) è(itemId,DistributedRowMatrix<userId,pref>),注意如果指定了—maxCooccurrences参数,这里会有裁剪,www.codesky.net 每个userId最多对maxCooccurrences的itemId打分

   这里的DistributedRowMatrix,分布式行矩阵:行:itemId, 列:userId

   reduce: (itemId, VectorWritable<userId,pref>)

phase5: RowSimilarityJob

这一步比较关键,计算item相似度,它拆分成了三个JOB。

   param: --numberOfColumns, --similarityClassname,--maxSimilaritiesPerRow(默认:100)

   job1:weight

      input:phase4的输出

       map: (itemId, VectorWritable <userId, pref>) ==>(userId, WeightedOccurrence<itemId, pref, weight>)

       这里的weight,对于欧氏向量距离,或者Pearson距离等,均为Double.NaN,即无效。在LoglikelihoodVectorSimilarity中有用到weight的值。

       reduce:(userId, WeightedOccurrenceArray<itemId, pref, weight>)

   job2:pairwise similarity *item相似度计算*

      map: 对同一用户的所有item-rating,输出两两item之间的关系 ==>(WeightedRowPair<itemA, itemB, weightA, weightB>, coocurrence<userId,valueA, valueB>) (同上,这里的权重weightA,B对于欧氏距离等可以忽略)

       reduce: 在这端,以<itemA,itemB>为key聚合了来自不同map的所有用户的打分,最后输出itemA和B的对称相似度(即以itemA为key或以itemB为key)==>(SimilarityMatrixEntryKey<itemA,similarity>, MatrixEntryWritable<WeightedRowPair<itemA, itemB,weightA, weightB>>) ,(SimilarityMatrixEntryKey<itemB,similarity>, MatrixEntryWritable<WeightedRowPair<itemB, itemA,weightB, weightA>>)

   job3:entries2vectors *汇总item的相似items*

      param: --maxSimilaritiesPerRow

      map: (itemA, itemB, similarity) & (itemB,itemA, similarity) 这里在group的时候按相似度降序做了排序,如果有--maxSimilaritiesPerRow参数,则会做裁剪。

      reduce: (itemA, VectorWritable <item,similarity>)

至此,item相似度计算完毕。

phase6: prePartialMultiply1 

   input: phase5的最后输出(即item相似度)

   map: 直接输出item对应的相似items,这里用VectorOrPrefWritable做了封装,表明有可能是相似度向量,也有可能是对item的打分,并且对item为自己的,将相似度设为Double.NaN,以过滤自身。è(itemId,VectorOrPrefWritable<item, similarity>)

    reduce: IdentityReducer

phase7: prePartialMultiply2

   input: phase2的输出userVectors

   map: 输出:(itemId, VectorOrPrefWritable<userId, pref>)

   这里默认考虑用户对10个item的评分,可以通过maxPrefsPerUserConsidered参数调整。

   如果指定了usersFile,则在setup时会把所有的userId读入内存,用于过滤。如果map输入数据的userID不在usersFile中,则会被忽略。注意,这是mahout的设计bug,对于比较大的数据集,很有可能造成OOM(事实上在我的测试程序中已经出现OOM了…),这种bug下面还会出现。输出的是用户的评分,同phase6的VectorOrPrefWritable的封装。

   reduce: IdentityReducer

phase8: partialMultiply

     input: 6和7的输出:prePartialMultiply1, prePartialMultiply2

     map: Identity。由于6和7的输出的key均为itemId,因而在reduce端同一item的相似item以及对应的用户评分会聚合到一起。

     reduce:(itemId, VectorAndPrefsWritable<similarityMatrix, List<userId>,List<pref>>) 没做特殊处理,直接合在一起,输出相似度矩阵,所有的userId及对item的打分。

phase9: itemFiltering 

    将过滤文件输出成<userId, itemId>。如果指定了--filterFile参数,则在最后的聚合推荐中会过滤userId对应的items。这一步在实际中多数是可以忽略的,只要不指定这个参数即可。

phase10: aggregateAndRecommend

    map: 对每个用户,输出对当前item的评分,以及与当前item的所有相似itemsè(userId, PrefAndSimilarityColumnWritable<pref,vector<item, similarity>>)

   reduce: 聚合了这个用户所有的评分历史,以及相似items,计算对该用户的推荐结果 è (userId, List<itemId>)。

注意在reduce的setup中,会将phase1产生的所有itemId到index的映射读入内存,这里只要Item数据集稍大,就会OOM。这是比较严重的设计bug

事实上,如果item是正规的整数,而不是guid之类的,phase1和这一步的读入内存是完全可以略掉的。这样的话就完全可以在企业级的数据集上使用(我的测试数据集是15亿+的user-item-rating,1.5亿+的用户,在最后这一步挂掉了,前面所有phase都能跑成功)。

至此,已经形成了推荐结果,CF完成。

以上的所有步骤中,phase5的计算item相似度是最慢的(这个其实也很直觉)。


转:http://www.codesky.net/article/201206/171862.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Mahout是一个开的机器学习框架,其中包括了协同过滤算法的实现。Mahout支持基于用户的协同过滤算法和基于物品协同过滤算法。 基于用户的协同过滤算法 基于用户的协同过滤算法是基于用户的历史行为数据来推荐物品算法。它的主要思想是找到和目标用户相似的用户,然后把这些相似用户喜欢的物品推荐给目标用户。 Mahout中实现基于用户的协同过滤算法可以通过以下步骤实现: 1. 加载用户行为数据 Mahout中用户行为数据可以是一个矩阵,每行代表一个用户,每列代表一个物品,矩阵中的每个元素表示该用户对该物品的评分。Mahout中提供了多种加载用户行为数据的方法,可以从文件中读取、从数据库中读取,或者从其他数据中读取。 2. 计算用户相似度 Mahout中提供了多种计算用户相似度的方法,例如基于皮尔逊相关系数、基于余弦相似度等。通过计算用户之间的相似度,可以找到和目标用户相似的用户。 3. 找到相似用户喜欢的物品 找到和目标用户相似的用户后,可以根据这些用户的历史行为数据,找到这些用户喜欢的物品。 4. 推荐物品给目标用户 根据相似用户喜欢的物品,可以推荐一些物品给目标用户。推荐的物品可以是和相似用户喜欢的物品相似的物品,也可以是和目标用户历史行为数据相似的物品。 基于物品协同过滤算法 基于物品协同过滤算法是基于物品之间的相似度来推荐物品算法。它的主要思想是找到和目标物品相似的物品,然后把这些相似物品推荐给目标用户。 Mahout中实现基于物品协同过滤算法可以通过以下步骤实现: 1. 加载用户行为数据 和基于用户的协同过滤算法一样,Mahout中也需要加载用户行为数据。 2. 计算物品相似度 Mahout中提供了多种计算物品相似度的方法,例如基于皮尔逊相关系数、基于余弦相似度等。通过计算物品之间的相似度,可以找到和目标物品相似的物品。 3. 找到相似物品 找到和目标物品相似的物品后,可以将这些物品推荐给目标用户。 4. 推荐物品给目标用户 推荐的物品可以是和相似物品相似的物品,也可以是和目标用户历史行为数据相似的物品。 以上就是Mahout实现基于用户的协同过滤算法和基于物品协同过滤算法的java实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值