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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章