使用Mahout实现协同过滤
摘要:?Mahout算法框架自帶的推薦器有下面這些: GenericUserBasedRecommender:基于用戶的推薦器,用戶數量少時速度快; GenericItemBasedRecommender:基于商品推薦器,商品數量少時速度快,尤其當外部提供了商品相似度數據后效率更好; SlopeOneRe...
Mahout算法框架自帶的推薦器有下面這些:
- GenericUserBasedRecommender:基于用戶的推薦器,用戶數量少時速度快;
- GenericItemBasedRecommender:基于商品推薦器,商品數量少時速度快,尤其當外部提供了商品相似度數據后效率更好;
- SlopeOneRecommender:基于slope-one算法的推薦器,在線推薦或更新較快,需要事先大量預處理運算,物品數量少時較好;
- SVDRecommender:奇異值分解,推薦效果較好,但之前需要大量預處理運算;
- KnnRecommender:基于k近鄰算法(KNN),適合于物品數量較小時;
- TreeClusteringRecommender:基于聚類的推薦器,在線推薦較快,之前需要大量預處理運算,用戶數量較少時效果好;
Mahout最常用的三個推薦器是上述的前三個,本文主要討論前兩種的使用。
接口相關介紹
基于用戶或物品的推薦器主要包括以下幾個接口:
- DataModel?是用戶喜好信息的抽象接口,它的具體實現支持從任意類型的數據源抽取用戶喜好信息。Taste 默認提供 JDBCDataModel 和 FileDataModel,分別支持從數據庫和文件中讀取用戶的喜好信息。
- UserSimilarity?和?ItemSimilarity。UserSimilarity 用于定義兩個用戶間的相似度,它是基于協同過濾的推薦引擎的核心部分,可以用來計算用戶的“鄰居”,這里我們將與當前用戶口味相似的用戶稱為他的鄰居。ItemSimilarity 類似的,計算內容之間的相似度。
- UserNeighborhood?用于基于用戶相似度的推薦方法中,推薦的內容是基于找到與當前用戶喜好相似的鄰居用戶的方式產生的。UserNeighborhood 定義了確定鄰居用戶的方法,具體實現一般是基于 UserSimilarity 計算得到的。
- Recommender?是推薦引擎的抽象接口,Taste 中的核心組件。程序中,為它提供一個 DataModel,它可以計算出對不同用戶的推薦內容。實際應用中,主要使用它的實現類 GenericUserBasedRecommender 或者 GenericItemBasedRecommender,分別實現基于用戶相似度的推薦引擎或者基于內容的推薦引擎。
- RecommenderEvaluator:評分器。
- RecommenderIRStatsEvaluator:搜集推薦性能相關的指標,包括準確率、召回率等等。
目前,Mahout為DataModel提供了以下幾種實現:
- org.apache.mahout.cf.taste.impl.model.GenericDataModel
- org.apache.mahout.cf.taste.impl.model.GenericBooleanPrefDataModel
- org.apache.mahout.cf.taste.impl.model.PlusAnonymousUserDataModel
- org.apache.mahout.cf.taste.impl.model.file.FileDataModel
- org.apache.mahout.cf.taste.impl.model.hbase.HBaseDataModel
- org.apache.mahout.cf.taste.impl.model.cassandra.CassandraDataModel
- org.apache.mahout.cf.taste.impl.model.mongodb.MongoDBDataModel
- org.apache.mahout.cf.taste.impl.model.jdbc.SQL92JDBCDataModel
- org.apache.mahout.cf.taste.impl.model.jdbc.MySQLJDBCDataModel
- org.apache.mahout.cf.taste.impl.model.jdbc.PostgreSQLJDBCDataModel
- org.apache.mahout.cf.taste.impl.model.jdbc.GenericJDBCDataModel
- org.apache.mahout.cf.taste.impl.model.jdbc.SQL92BooleanPrefJDBCDataModel
- org.apache.mahout.cf.taste.impl.model.jdbc.MySQLBooleanPrefJDBCDataModel
- org.apache.mahout.cf.taste.impl.model.jdbc.PostgreBooleanPrefSQLJDBCDataModel
- org.apache.mahout.cf.taste.impl.model.jdbc.ReloadFromJDBCDataModel
從類名上就可以大概猜出來每個DataModel的用途,奇怪的是竟然沒有HDFS的DataModel,有人實現了一個,請參考MAHOUT-1579。
UserSimilarity?和?ItemSimilarity?相似度實現有以下幾種:
- CityBlockSimilarity:基于Manhattan距離相似度
- EuclideanDistanceSimilarity:基于歐幾里德距離計算相似度
- LogLikelihoodSimilarity:基于對數似然比的相似度
- PearsonCorrelationSimilarity:基于皮爾遜相關系數計算相似度
- SpearmanCorrelationSimilarity:基于皮爾斯曼相關系數相似度
- TanimotoCoefficientSimilarity:基于谷本系數計算相似度
- UncenteredCosineSimilarity:計算 Cosine 相似度
以上相似度的說明,請參考Mahout推薦引擎介紹。
UserNeighborhood 主要實現有兩種:
- NearestNUserNeighborhood:對每個用戶取固定數量N個最近鄰居
- ThresholdUserNeighborhood:對每個用戶基于一定的限制,取落在相似度限制以內的所有用戶為鄰居
Recommender分為以下幾種實現:
- GenericUserBasedRecommender:基于用戶的推薦引擎
- GenericBooleanPrefUserBasedRecommender:基于用戶的無偏好值推薦引擎
- GenericItemBasedRecommender:基于物品的推薦引擎
- GenericBooleanPrefItemBasedRecommender:基于物品的無偏好值推薦引擎
RecommenderEvaluator有以下幾種實現:
- AverageAbsoluteDifferenceRecommenderEvaluator:計算平均差值
- RMSRecommenderEvaluator:計算均方根差
RecommenderIRStatsEvaluator的實現類是GenericRecommenderIRStatsEvaluator。
單機運行
首先,需要在maven中加入對mahout的依賴:
<dependency><groupId>org.apache.mahout</groupId><artifactId>mahout-core</artifactId><version>0.13.0</version> </dependency><dependency><groupId>org.apache.mahout</groupId><artifactId>mahout-integration</artifactId><version>0.13.0</version> </dependency><dependency><groupId>org.apache.mahout</groupId><artifactId>mahout-math</artifactId><version>0.13.0</version> </dependency><dependency><groupId>org.apache.mahout</groupId><artifactId>mahout-examples</artifactId><version>0.13.0</version> </dependency>基于用戶的推薦,以FileDataModel為例:
File modelFile modelFile = new File("intro.csv");DataModel model = new FileDataModel(modelFile);//用戶相似度,使用基于皮爾遜相關系數計算相似度 UserSimilarity similarity = new PearsonCorrelationSimilarity(model);//選擇鄰居用戶,使用NearestNUserNeighborhood實現UserNeighborhood接口,選擇鄰近的4個用戶 UserNeighborhood neighborhood = new NearestNUserNeighborhood(4, similarity, model);Recommender recommender = new GenericUserBasedRecommender(model, neighborhood, similarity);//給用戶1推薦4個物品 List<RecommendedItem> recommendations = recommender.recommend(1, 4);for (RecommendedItem recommendation : recommendations) {System.out.println(recommendation); }注意: FileDataModel要求輸入文件中的字段分隔符為逗號或者制表符,如果你想使用其他分隔符,你可以擴展一個FileDataModel的實現,例如,mahout中已經提供了一個解析MoiveLens的數據集(分隔符為::)的實現GroupLensDataModel。
GenericUserBasedRecommender是基于用戶的簡單推薦器實現類,推薦主要參照傳入的DataModel和UserNeighborhood,總體是三個步驟:
- (1) 從UserNeighborhood獲取當前用戶Ui最相似的K個用戶集合{U1, U2, …Uk};
- (2) 從這K個用戶集合排除Ui的偏好商品,剩下的Item集合為{Item0, Item1, …Itemm};
- (3) 對Item集合里每個Itemj計算Ui可能偏好程度值pref(Ui, Itemj),并把Item按此數值從高到低排序,前N個item推薦給用戶Ui。
對相同用戶重復獲得推薦結果,我們可以改用CachingRecommender來包裝GenericUserBasedRecommender對象,將推薦結果緩存起來:
Recommender cachingRecommender = new CachingRecommender(recommender);上面代碼可以在main方法中直接運行,然后,我們可以獲取推薦模型的評分:
//使用平均絕對差值獲得評分 RecommenderEvaluator evaluator = new AverageAbsoluteDifferenceRecommenderEvaluator(); // 用RecommenderBuilder構建推薦引擎 RecommenderBuilder recommenderBuilder = new RecommenderBuilder() {@Overridepublic Recommender buildRecommender(DataModel model) throws TasteException {UserSimilarity similarity = new PearsonCorrelationSimilarity(model);UserNeighborhood neighborhood = new NearestNUserNeighborhood(4, similarity, model);return new GenericUserBasedRecommender(model, neighborhood, similarity);} }; // Use 70% of the data to train; test using the other 30%. double score = evaluator.evaluate(recommenderBuilder, null, model, 0.7, 1.0); System.out.println(score);接下來,可以獲取推薦結果的查準率和召回率:
RecommenderIRStatsEvaluator statsEvaluator = new GenericRecommenderIRStatsEvaluator(); // Build the same recommender for testing that we did last time: RecommenderBuilder recommenderBuilder = new RecommenderBuilder() {@Overridepublic Recommender buildRecommender(DataModel model) throws TasteException {UserSimilarity similarity = new PearsonCorrelationSimilarity(model);UserNeighborhood neighborhood = new NearestNUserNeighborhood(4, similarity, model);return new GenericUserBasedRecommender(model, neighborhood, similarity);} }; // 計算推薦4個結果時的查準率和召回率 IRStatistics stats = statsEvaluator.evaluate(recommenderBuilder,null, model, null, 4,GenericRecommenderIRStatsEvaluator.CHOOSE_THRESHOLD,1.0); System.out.println(stats.getPrecision()); System.out.println(stats.getRecall());如果是基于物品的推薦,代碼大體相似,只是沒有了UserNeighborhood,然后將上面代碼中的User換成Item即可,完整代碼如下:
File modelFile modelFile = new File("intro.csv");DataModel model = new FileDataModel(new File(file));// Build the same recommender for testing that we did last time: RecommenderBuilder recommenderBuilder = new RecommenderBuilder() {@Overridepublic Recommender buildRecommender(DataModel model) throws TasteException {ItemSimilarity similarity = new PearsonCorrelationSimilarity(model);return new GenericItemBasedRecommender(model, similarity);} };//獲取推薦結果 List<RecommendedItem> recommendations = recommenderBuilder.buildRecommender(model).recommend(1, 4);for (RecommendedItem recommendation : recommendations) {System.out.println(recommendation); }//計算評分 RecommenderEvaluator evaluator =new AverageAbsoluteDifferenceRecommenderEvaluator(); // Use 70% of the data to train; test using the other 30%. double score = evaluator.evaluate(recommenderBuilder, null, model, 0.7, 1.0); System.out.println(score);//計算查全率和查準率 RecommenderIRStatsEvaluator statsEvaluator = new GenericRecommenderIRStatsEvaluator();// Evaluate precision and recall "at 2": IRStatistics stats = statsEvaluator.evaluate(recommenderBuilder,null, model, null, 4,GenericRecommenderIRStatsEvaluator.CHOOSE_THRESHOLD,1.0); System.out.println(stats.getPrecision()); System.out.println(stats.getRecall());在Spark中運行
在Spark中運行,需要將Mahout相關的jar添加到Spark的classpath中,修改/etc/spark/conf/spark-env.sh,添加下面兩行代碼:
SPARK_DIST_CLASSPATH="$SPARK_DIST_CLASSPATH:/usr/lib/mahout/lib/*" SPARK_DIST_CLASSPATH="$SPARK_DIST_CLASSPATH:/usr/lib/mahout/*"然后,以本地模式在spark-shell中運行下面代碼交互測試:
//注意:這里是本地目錄 val model = new FileDataModel(new File("intro.csv"))val evaluator = new RMSRecommenderEvaluator() val recommenderBuilder = new RecommenderBuilder {override def buildRecommender(dataModel: DataModel): Recommender = {val similarity = new LogLikelihoodSimilarity(dataModel)new GenericItemBasedRecommender(dataModel, similarity)} }val score = evaluator.evaluate(recommenderBuilder, null, model, 0.95, 0.05) println(s"Score=$score")val recommender=recommenderBuilder.buildRecommender(model) val users=trainingRatings.map(_.user).distinct().take(20)import scala.collection.JavaConversions._val result=users.par.map{user=>user+","+recommender.recommend(user,40).map(_.getItemID).mkString(",") }https://github.com/sujitpal/mia-scala-examples上面有一個評估基于物品或是用戶的各種相似度下的評分的類,叫做 RecommenderEvaluator,供大家學習參考。
分布式運行
Mahout提供了org.apache.mahout.cf.taste.hadoop.item.RecommenderJob類以MapReduce的方式來實現基于物品的協同過濾,查看該類的使用說明:
$ hadoop jar /usr/lib/mahout/mahout-examples-0.9-cdh5.4.0-job.jar org.apache.mahout.cf.taste.hadoop.item.RecommenderJob 15/06/10 16:19:34 ERROR common.AbstractJob: Missing required option --similarityClassname Missing required option --similarityClassname Usage:[--input <input> --output <output> --numRecommendations <numRecommendations> --usersFile <usersFile> --itemsFile <itemsFile> --filterFile <filterFile> --booleanData <booleanData> --maxPrefsPerUser <maxPrefsPerUser> --minPrefsPerUser <minPrefsPerUser> --maxSimilaritiesPerItem <maxSimilaritiesPerItem> --maxPrefsInItemSimilarity <maxPrefsInItemSimilarity> --similarityClassname <similarityClassname> --threshold <threshold> --outputPathForSimilarityMatrix <outputPathForSimilarityMatrix> --randomSeed <randomSeed> --sequencefileOutput --help --tempDir <tempDir> --startPhase <startPhase> --endPhase <endPhase>] --similarityClassname (-s) similarityClassname Name of distributedsimilarity measures class toinstantiate, alternativelyuse one of the predefinedsimilarities([SIMILARITY_COOCCURRENCE,SIMILARITY_LOGLIKELIHOOD,SIMILARITY_TANIMOTO_COEFFICIENT, SIMILARITY_CITY_BLOCK,SIMILARITY_COSINE,SIMILARITY_PEARSON_CORRELATION,SIMILARITY_EUCLIDEAN_DISTANCE])可見,該類可以接收的命令行參數如下:
- --input(path): 存儲用戶偏好數據的目錄,該目錄下可以包含一個或多個存儲用戶偏好數據的文本文件;
- --output(path): 結算結果的輸出目錄
- --numRecommendations (integer): 為每個用戶推薦的item數量,默認為10
- --usersFile (path): 指定一個包含了一個或多個存儲userID的文件路徑,僅為該路徑下所有文件包含的userID做推薦計算 (該選項可選)
- --itemsFile (path): 指定一個包含了一個或多個存儲itemID的文件路徑,僅為該路徑下所有文件包含的itemID做推薦計算 (該選項可選)
- --filterFile (path): 指定一個路徑,該路徑下的文件包含了[userID,itemID]值對,userID和itemID用逗號分隔。計算結果將不會為user推薦[userID,itemID]值對中包含的item (該選項可選)
- --booleanData (boolean): 如果輸入數據不包含偏好數值,則將該參數設置為true,默認為false
- --maxPrefsPerUser (integer): 在最后計算推薦結果的階段,針對每一個user使用的偏好數據的最大數量,默認為10
- --minPrefsPerUser (integer): 在相似度計算中,忽略所有偏好數據量少于該值的用戶,默認為1
- --maxSimilaritiesPerItem (integer): 針對每個item的相似度最大值,默認為100
- --maxPrefsPerUserInItemSimilarity (integer): 在item相似度計算階段,針對每個用戶考慮的偏好數據最大數量,默認為1000
- --similarityClassname (classname): 向量相似度計算類
- outputPathForSimilarityMatrix:SimilarityMatrix輸出目錄
- --randomSeed:隨機種子 –sequencefileOutput:序列文件輸出路徑
- --tempDir (path): 存儲臨時文件的目錄,默認為當前用戶的home目錄下的temp目錄
- --startPhase
- --endPhase
- --threshold (double): 忽略相似度低于該閥值的item對
一個例子如下,使用SIMILARITY_LOGLIKELIHOOD相似度推薦物品:
$ hadoop jar /usr/lib/mahout/mahout-examples-0.9-cdh5.4.0-job.jar org.apache.mahout.cf.taste.hadoop.item.RecommenderJob --input /tmp/mahout/part-00000 --output /tmp/mahout-out -s SIMILARITY_LOGLIKELIHOOD默認情況下,mahout使用的reduce數目為1,這樣造成大數據處理時效率較低,可以通過參數mahout執行腳本中的MAHOUT_OPTS中的-Dmapred.reduce.tasks參數指定reduce數目。
上面命令運行完成之后,會在當前用戶的hdfs主目錄生成temp目錄,該目錄可由--tempDir (path)參數設置:
$ hadoop fs -ls temp Found 10 items -rw-r--r-- 3 root hadoop 7 2015-06-10 14:42 temp/maxValues.bin -rw-r--r-- 3 root hadoop 5522717 2015-06-10 14:42 temp/norms.bin drwxr-xr-x - root hadoop 0 2015-06-10 14:41 temp/notUsed -rw-r--r-- 3 root hadoop 7 2015-06-10 14:42 temp/numNonZeroEntries.bin -rw-r--r-- 3 root hadoop 3452222 2015-06-10 14:41 temp/observationsPerColumn.bin drwxr-xr-x - root hadoop 0 2015-06-10 14:47 temp/pairwiseSimilarity drwxr-xr-x - root hadoop 0 2015-06-10 14:52 temp/partialMultiply drwxr-xr-x - root hadoop 0 2015-06-10 14:39 temp/preparePreferenceMatrix drwxr-xr-x - root hadoop 0 2015-06-10 14:50 temp/similarityMatrix drwxr-xr-x - root hadoop 0 2015-06-10 14:42 temp/weights觀察yarn的管理界面,該命令會生成9個任務,任務名稱依次是:
- PreparePreferenceMatrixJob-ItemIDIndexMapper-Reducer
- PreparePreferenceMatrixJob-ToItemPrefsMapper-Reducer
- PreparePreferenceMatrixJob-ToItemVectorsMapper-Reducer
- RowSimilarityJob-CountObservationsMapper-Reducer
- RowSimilarityJob-VectorNormMapper-Reducer
- RowSimilarityJob-CooccurrencesMapper-Reducer
- RowSimilarityJob-UnsymmetrifyMapper-Reducer
- partialMultiply
- RecommenderJob-PartialMultiplyMapper-Reducer
從任務名稱,大概可以知道每個任務在做什么,如果你的輸入參數不一樣,生成的任務數可能不一樣,這個需要測試一下才能確認。
在hdfs上查看輸出的結果,用戶和推薦結果用\t分隔,推薦結果中物品之間用逗號分隔,物品后面通過冒號連接評分:
843 [10709679:4.8334665,8389878:4.833426,9133835:4.7503786,10366169:4.7503185,9007487:4.750272,8149253:4.7501993,10366165:4.750115,9780049:4.750108,8581254:4.750071,10456307:4.7500467] 6253 [10117445:3.0375953,10340299:3.0340924,8321090:3.0340924,10086615:3.032164,10436801:3.0187714,9668385:3.0141575,8502110:3.013954,10476325:3.0074399,10318667:3.0004222,8320987:3.0003839]使用Java API方式執行,請參考Mahout分步式程序開發 基于物品的協同過濾ItemCF。
在Scala或者Spark中,可以以Java API或者命令方式運行,最后還可以通過Spark來處理推薦的結果,例如:過濾、去重、補足數據,這部分內容不做介紹。
總結
以上是生活随笔為你收集整理的使用Mahout实现协同过滤的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: sql中长度
- 下一篇: 自媒体从零到大v,这个方法你得知道