1.Mahout介紹
1.1概述
根據(jù)百度的解說,Mahout 是 Apache Software Foundation(ASF) 旗下的一個開源項目,提供一些可擴展的機器學(xué)習(xí)領(lǐng)域經(jīng)典算法的實現(xiàn),旨在幫助開發(fā)人員更加方便快捷地創(chuàng)建智能應(yīng)用程序。Mahout包含許多實現(xiàn),包括聚類、分類、推薦過濾、頻繁子項挖掘。此外,通過使用 Apache Hadoop 庫,Mahout 可以有效地擴展到云中。
1.2發(fā)展歷史
mahout一直伴隨Hadoop發(fā)展的,從一開始能夠幫助我們在Hadoop上實現(xiàn)很多機器學(xué)習(xí),到后來發(fā)現(xiàn)它的效率越來越慢,于是放棄使用了一段時間,在一年之后,大概14年開始宣布(0.9版本),截止14年底,mahout不再接受任何MapReduce開發(fā)的算法,轉(zhuǎn)向spark。我們知道,Hadoop也是經(jīng)歷了從1.0到2.0時代的變遷,而mahout也是跟著這樣一個變化而變化的。到了15年,mahout開始更新,更新到0.10、0.12版本后,mahout就開始使用基于Spark/Flink/H2O這樣一些平臺來去開發(fā)數(shù)據(jù)挖掘/機器學(xué)習(xí)庫。雖然改變了開發(fā)平臺,但也不是完全不支持了MapReduce的開發(fā),只是不再接受新的MapReduce算法開發(fā)。
1.3特點
擴展性:mahout本身只是一個機器學(xué)習(xí)庫,并不是一個平臺,不像H2O,H2O是完整的做機器學(xué)習(xí),預(yù)測分析的平臺,而MapReduce只是一個庫,它底層的存儲還是基于HDFS,它的調(diào)度還是使用了Hadoop平臺上的 YARN ,HDFS本身就給mahout帶來了存儲和計算
容錯性:是基于MapReduce/Spark/Flink這些計算引擎來實現(xiàn)的,而MapReduce/Spark/Flink本身具有非常好的容錯性,包括它的推送和執(zhí)行和失敗容錯機制等。
1.4組件
屬于Hadoop生態(tài)系統(tǒng)重要組成部分:如果Hadoop是一頭大象,而mahout就是一個訓(xùn)象師,引導(dǎo)它往什么方向走,做什么樣的事。它也是Hadoop的一個重要組件,伴隨Hadoop成長。
1.5實現(xiàn)的大部分常用的數(shù)據(jù)挖掘算法
聚類算法

分類算法

其他算法

1.6Mahout后端計算引擎

支持mr,后來轉(zhuǎn)向spark并包含原來的mr,spark最大特點是基于內(nèi)存、基于圖調(diào)度的方式、算子簡單易用和適用的語言(底層有實現(xiàn))
H2O本身是一個適用于做機器學(xué)習(xí)和預(yù)測分析的平臺,自身有一套算法支持的庫 ,H2O也可以在Hadoop中集成
Flink:支持流處理和批處理
有些不同的算法支持不同的計算引擎。
1.7Mahout架構(gòu)

Mahout架構(gòu):low-level

Mahout提供的算法架構(gòu)

2.mahout推薦算法介紹
2.1Mahout推薦系統(tǒng)介紹
協(xié)同過濾框架一
使用歷史數(shù)據(jù)(打分,點擊,購買等)作為推薦的依據(jù)
User-based: 通過發(fā)現(xiàn)類似的用戶推薦商品。由于用戶多變的特性,這種方法很那擴展;
Item-based:通過計算item之間相似度推薦商品。商品不易變化,相似度矩陣可離線計算得到。(誕生于Amazon)
?MF-based:通過將原始的user-item矩陣分解成小的矩陣,分析潛在的影響因子,并以解釋用戶的行為。(誕生于Netflix Prize)
協(xié)同過濾框架二
SVD(Singular Value Decomposition)因式分解實現(xiàn)協(xié)同過濾
基于ALS(alternating least squares)的協(xié)同過濾算法
2.2Mahout推薦系統(tǒng)架構(gòu)

2.3利用Mahout構(gòu)建推薦系統(tǒng)
輸入輸出
輸入:原始數(shù)據(jù)(user preferences,用戶偏好)
輸出:用戶偏好估計
步驟
Step 1:將原始數(shù)據(jù)映射到Mahout定義的Data Model中
Step 2: 調(diào)優(yōu)推薦組件
相似度組件,臨界關(guān)系組件等
Step 3: 計算排名估計值
Step 4:評估推薦結(jié)果
2.4Mahout推薦系統(tǒng)組件
Mahout關(guān)鍵抽象是通過Java Interface實現(xiàn)的:
DataModel Interface將原始數(shù)據(jù)映射成Mahout兼容格式
UserSimilarity Interface計算兩個用戶間的相關(guān)度
ItemSimilarity Interface計算兩個商品間的相關(guān)度
UserNeighborhood Interface定義用戶或商品間的“臨近”
Recommender Interface實現(xiàn)具體的推薦算法,完成推薦功能(包括訓(xùn)練,預(yù)測等)
推薦系統(tǒng)組件:DataModel



推薦系統(tǒng)組件:UserSimilarity

相似度舉例:TanimotoDistance

相似度舉例:CosineSimilarity

Pearson vs. Euclidean distance

Pearson vs. Euclidean distance

推薦系統(tǒng)組件:UserNeighborhood


從以上組件可以看出,Mahout提供了大量的基于CF的推薦器:
不同的推薦算法
不同的“鄰接”定義
不同的相似度定義
評估不同的算法實現(xiàn)非常耗時
Mahout提供了評估不同算法組合效果的工具
Mahout提供了標(biāo)準(zhǔn)的推薦系統(tǒng)評估接口
2.5推薦系統(tǒng)評估
Mahout提供了大量方法用于評估推薦系統(tǒng)
1.基于Prediction-based measures:
Mean Average Error 平均絕對誤差
RMSE (Root Mean Square Error) 均方根誤差
Class: AverageAbsoluteDifferenceEvaluator
Method: evaluate()
Parameters:
Recommender implementation
DataModel implementation
TrainingSet size (e.g. 70%)
% of the data to use in the evaluation (smaller % for fast prototyping)
2.基于IR-based measures
Precision, Recall, F1-measure 準(zhǔn)確率,召回率,F(xiàn)1混合
NDCG (ranking measure)
Class: GenericRecommenderIRStatsEvaluator
Method: evaluate()
Parameters:
Recommender implementation
DataModel implementation
Relevance Threshold (mean+standard deviation)
% of the data to use in the evaluation (smaller % for fast prototyping)
3.mahout推薦算法實戰(zhàn)
實例1:preferences
要求:
創(chuàng)建user-item偏好數(shù)據(jù),并輸出
實現(xiàn):
使用GenericUserPreferenceArray創(chuàng)建數(shù)據(jù)
通過PreferenceArray存儲數(shù)據(jù)
代碼如下:
package com.zdd.example;
import org.apache.mahout.cf.taste.impl.model.GenericUserPreferenceArray;
import org.apache.mahout.cf.taste.model.Preference;
import org.apache.mahout.cf.taste.model.PreferenceArray;
public class CreatePreferenceArray {
private CreatePreferenceArray() {
}
public static void main(String[] args) {
PreferenceArray User1Pref = new GenericUserPreferenceArray(2);
User1Pref.setUserID(0, 1L);
User1Pref.setItemID(0, 101L);
User1Pref.setValue(0, 3.0f);
User1Pref.setItemID(1, 102L);
User1Pref.setValue(1, 4.0f);
Preference pref = User1Pref.get(1);
System.out.println(User1Pref);
}
}
運行結(jié)果如下:
GenericUserPreferenceArray[userID:1,{101=3.0,102=4.0}]
表示用戶ID為1的用戶給商品101和102分別打分3.0和4.0
實例2:data model
PreferenceArray存儲了單個用戶的偏好,所有用戶的偏好數(shù)據(jù)如何保存?HashMap? NO!
Mahout引入了一個為推薦任務(wù)優(yōu)化的數(shù)據(jù)結(jié)構(gòu):FastByIDMap
需求:
使用GenericDataModel讀入FastByIDMap數(shù)據(jù)
代碼:
package com.zdd.example;
import org.apache.mahout.cf.taste.impl.common.FastByIDMap;
import org.apache.mahout.cf.taste.impl.model.GenericDataModel;
import org.apache.mahout.cf.taste.impl.model.GenericUserPreferenceArray;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.model.PreferenceArray;
public class CreateGenericDataModel {
private CreateGenericDataModel() {
}
public static void main(String[] args) {
FastByIDMap<PreferenceArray> preferences = new FastByIDMap<PreferenceArray>();
PreferenceArray User1Pref = new GenericUserPreferenceArray(2);
User1Pref.setUserID(0, 1L);
User1Pref.setItemID(0, 101L);
User1Pref.setValue(0, 3.0f);
User1Pref.setItemID(1, 102L);
User1Pref.setValue(1, 4.0f);
PreferenceArray User2Pref = new GenericUserPreferenceArray(2);
User2Pref.setUserID(0, 2L);
User2Pref.setItemID(0, 101L);
User2Pref.setValue(0, 3.0f);
User2Pref.setItemID(1, 102L);
User2Pref.setValue(1, 4.0f);
preferences.put(1L, User1Pref);
preferences.put(2L, User2Pref);
DataModel model = new GenericDataModel(preferences);
System.out.println(model);
System.out.println(preferences);
}
}
輸出如下:
GenericDataModel[users:1,2]
{1=GenericUserPreferenceArray[userID:1,{101=3.0,102=4.0}],2=GenericUserPreferenceArray[userID:2,{101=3.0,102=4.0}]}
實例3:Recommender
需求:通過User-based協(xié)同過濾推薦算法給用戶1推薦20個商品
實現(xiàn):
1.使用FileDataModel讀入文件
2.通過PearsonCorrelationSimilarity來計算相似度
3.使用GenericUserBasedRecommender構(gòu)建推薦引擎
ua.base數(shù)據(jù):

代碼:
package com.zdd.example;
import org.apache.mahout.cf.taste.impl.model.file.*;
import org.apache.mahout.cf.taste.impl.similarity.*;
import org.apache.mahout.cf.taste.impl.neighborhood.*;
import org.apache.mahout.cf.taste.impl.recommender.*;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.similarity.*;
import org.apache.mahout.cf.taste.neighborhood.*;
import org.apache.mahout.cf.taste.recommender.*;
import java.io.File;
import java.util.List;
public class RecommenderIntro {
public static void main(String[] args) throws Exception{
DataModel model = new FileDataModel(new File("data/ua.base"));
UserSimilarity similarity = new PearsonCorrelationSimilarity(model);
UserNeighborhood neighborhood = new NearestNUserNeighborhood(100, similarity, model);
Recommender recommender = new GenericUserBasedRecommender(model, neighborhood, similarity);
List<RecommendedItem> recommendedItems = recommender.recommend(1, 20);
for (RecommendedItem recommendedItem: recommendedItems){
System.out.println(recommendedItem);
}
}
}
推薦結(jié)果如下:

實例4:推薦模型評估(1)
需求:
評估實例3的推薦系統(tǒng)的優(yōu)劣
實現(xiàn):
使用AverageAbsoluteDifferenceRecommenderEvaluator和RMSRecommenderEvaluator來評估模型
通過RecommenderBuilder來實現(xiàn)評估模型
實現(xiàn)如下代碼:
package com.zdd.example;
import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.eval.RecommenderBuilder;
import org.apache.mahout.cf.taste.eval.RecommenderEvaluator;
import org.apache.mahout.cf.taste.impl.eval.AverageAbsoluteDifferenceRecommenderEvaluator;
import org.apache.mahout.cf.taste.impl.eval.RMSRecommenderEvaluator;
import org.apache.mahout.cf.taste.impl.model.file.FileDataModel;
import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood;
import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender;
import org.apache.mahout.cf.taste.impl.similarity.PearsonCorrelationSimilarity;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood;
import org.apache.mahout.cf.taste.recommender.*;
import org.apache.mahout.cf.taste.similarity.UserSimilarity;
import java.io.File;
public class EvaluatorIntro {
private EvaluatorIntro() {
}
public static void main(String[] args) throws Exception {
final DataModel model = new FileDataModel(new File("data/ua.base"));
RecommenderEvaluator evaluator = new AverageAbsoluteDifferenceRecommenderEvaluator();
RecommenderEvaluator recommenderEvaluator = new RMSRecommenderEvaluator();
RecommenderBuilder recommenderBuilder = new RecommenderBuilder() {
@Override
public Recommender buildRecommender(DataModel model) throws TasteException {
UserSimilarity similarity = new PearsonCorrelationSimilarity(model);
UserNeighborhood neighborhood = new NearestNUserNeighborhood(100, similarity, model);
return new GenericUserBasedRecommender(model, neighborhood, similarity);
}
};
//參數(shù)0.7表示評估的訓(xùn)練集為70%,1.0代表所有的用戶來參與評估
double score = evaluator.evaluate(recommenderBuilder, null, model, 0.7, 1.0);
double rmse = recommenderEvaluator.evaluate(recommenderBuilder, null, model, 0.7, 1.0);
System.out.println(score);
System.out.println(rmse);
}
}
輸出結(jié)果如下:
0.8522242111918109
1.0888589811454357
從結(jié)果可以看到,平均絕對誤差大約為0.85,而均方根誤差大約為1.09,在這個不大的數(shù)據(jù)集中,這個結(jié)果還能接受。
我們可以更改第34行代碼來比較不同相似度的評分,這里用的相似度計算方式為皮爾森系數(shù):UserSimilarity similarity = new PearsonCorrelationSimilarity(model);
更改為歐幾里得:
UserSimilarity similarity = new EuclideanDistanceSimilarity(model);
更改為余弦相似度:
UserSimilarity similarity = new UncenteredCosineSimilarity(model);
實例5:推薦模型評估(2)
需求:
通過IR指標(biāo)來評估實例3的推薦系統(tǒng)的優(yōu)劣
實現(xiàn):
使用RecommenderIRStatsEvaluator來進行評估
實現(xiàn)代碼如下:
package com.zdd.example;
import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.eval.*;
import org.apache.mahout.cf.taste.impl.eval.GenericRecommenderIRStatsEvaluator;
import org.apache.mahout.cf.taste.impl.model.file.FileDataModel;
import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood;
import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender;
import org.apache.mahout.cf.taste.impl.similarity.PearsonCorrelationSimilarity;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood;
import org.apache.mahout.cf.taste.recommender.*;
import org.apache.mahout.cf.taste.similarity.UserSimilarity;
import java.io.File;
public class IREvaluatorIntro {
public static void main(String[] args) throws Exception {
final DataModel model = new FileDataModel(new File("data/ua.base"));
RecommenderIRStatsEvaluator evaluator = new GenericRecommenderIRStatsEvaluator();
RecommenderBuilder recommenderBuilder = new RecommenderBuilder() {
@Override
public Recommender buildRecommender(DataModel model) throws TasteException {
UserSimilarity similarity = new PearsonCorrelationSimilarity(model);
UserNeighborhood neighborhood = new NearestNUserNeighborhood(100, similarity, model);
return new GenericUserBasedRecommender(model, neighborhood, similarity);
}
};
// 參數(shù)值5代表推薦5個商品,參數(shù)1.0代表全部用戶參與評估
// 參數(shù)GenericRecommenderIRStatsEvaluator.CHOOSE_THRESHOLD代表Preference為多少時,兩個item時相關(guān)的,這個參數(shù)值代表
// 我們在計算過程中自動調(diào)整這個閾值。
IRStatistics stats = evaluator.evaluate(recommenderBuilder, null, model, null, 5, GenericRecommenderIRStatsEvaluator.CHOOSE_THRESHOLD, 1.0);
System.out.println(stats.getPrecision());
System.out.println(stats.getRecall());
System.out.println(stats.getF1Measure());
}
}
輸出結(jié)果如下:
0.011523687580025595
0.011523687580025595
0.011523687580025593
從結(jié)果可以看到,各項指標(biāo)比較低。這是因為我們的數(shù)據(jù)樣本還是很小,下一個實例將會使用相對大一些的數(shù)據(jù)集,電影數(shù)據(jù)集來進行實踐。
實例6:MovieLens推薦系統(tǒng)
需求:
使用MovieLens 1M數(shù)據(jù)集實現(xiàn)電影推薦系統(tǒng)
步驟:
實現(xiàn)MovieLens數(shù)據(jù)集的DataModel
實現(xiàn)Item-based和User-based的協(xié)同過濾推薦,并保存結(jié)果
實現(xiàn)代碼分三個代碼文件,1.數(shù)據(jù)預(yù)處理,2.Item-based實現(xiàn),3.User-based實現(xiàn)
1.數(shù)據(jù)預(yù)處理:
package com.zdd.MovieLens;
import org.apache.commons.io.Charsets;
import org.apache.mahout.cf.taste.impl.model.file.FileDataModel;
import org.apache.mahout.common.iterator.FileLineIterable;
import java.io.*;
import java.util.regex.Pattern;
public class MovieLensDataModel extends FileDataModel {
private static String COLON_DELIMITER="::";
private static Pattern COLON_DELIMITER_PATTERN=Pattern.compile(COLON_DELIMITER);
public MovieLensDataModel(File ratingsFile) throws IOException{
super(convertFile(ratingsFile));
}
private static File convertFile(File orginalFile) throws IOException{
File resultFile = new File(System.getProperty("java.io.tmpdir"), "ratings.csv");
if (resultFile.exists()){
resultFile.delete();
}
try(Writer writer = new OutputStreamWriter(new FileOutputStream(resultFile), Charsets.UTF_8)) {
for (String line: new FileLineIterable(orginalFile, false)){
int lastIndex = line.lastIndexOf(COLON_DELIMITER);
if (lastIndex < 0 ){
throw new IOException("Invalid data!");
}
String subLine = line.substring(0, lastIndex);
String convertedSubLine = COLON_DELIMITER_PATTERN.matcher(subLine).replaceAll(",");
writer.write(convertedSubLine);
writer.write('\n');
}
} catch (IOException ioe){
resultFile.delete();
throw ioe;
}
return resultFile;
}
}
2.Item-based實現(xiàn):
package com.zdd.MovieLens;
import org.apache.mahout.cf.taste.impl.recommender.GenericItemBasedRecommender;
import org.apache.mahout.cf.taste.impl.similarity.LogLikelihoodSimilarity;
import org.apache.mahout.cf.taste.impl.similarity.precompute.FileSimilarItemsWriter;
import org.apache.mahout.cf.taste.impl.similarity.precompute.MultithreadedBatchItemSimilarities;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.recommender.ItemBasedRecommender;
import org.apache.mahout.cf.taste.similarity.ItemSimilarity;
import org.apache.mahout.cf.taste.similarity.precompute.BatchItemSimilarities;
import org.apache.mahout.cf.taste.similarity.precompute.SimilarItemsWriter;
import java.io.File;
public class BatchItemSimilaritiesMovieLens {
private BatchItemSimilaritiesMovieLens(){
}
public static void main(String[] args) throws Exception{
if (args.length !=1){
System.err.println("Needs MovieLens 1M dataset as arugument!");
System.exit(-1);
}
File resultFile = new File(System.getProperty("java.io.tmpdir"), "similarities.csv");
DataModel dataModel = new MovieLensDataModel(new File(args[0]));
ItemSimilarity similarity = new LogLikelihoodSimilarity(dataModel);
ItemBasedRecommender recommender = new GenericItemBasedRecommender(dataModel, similarity);
//參數(shù)5代表相似物品的數(shù)量
BatchItemSimilarities batchItemSimilarities = new MultithreadedBatchItemSimilarities(recommender, 5);
SimilarItemsWriter writer = new FileSimilarItemsWriter(resultFile);
int numSimilarites = batchItemSimilarities.computeItemSimilarities(Runtime.getRuntime().availableProcessors(), 1, writer);
System.out.println("Computed "+ numSimilarites+ " for "+ dataModel.getNumItems()+" items and saved them to "+resultFile.getAbsolutePath());
}
}
運行代碼,打印結(jié)果如下:

可以看到,在3706個物品中,有18530個相似物品的結(jié)果
并且在C:\Users\ADMINI~1\AppData\Local\Temp目錄下,會產(chǎn)生ratings.csv和similarities.csv兩個文件
similarities.csv數(shù)據(jù)如下:

3.User-based實現(xiàn)
package com.zdd.MovieLens;
import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.eval.RecommenderBuilder;
import org.apache.mahout.cf.taste.impl.eval.RMSRecommenderEvaluator;
import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood;
import org.apache.mahout.cf.taste.impl.recommender.CachingRecommender;
import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender;
import org.apache.mahout.cf.taste.impl.similarity.PearsonCorrelationSimilarity;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood;
import org.apache.mahout.cf.taste.recommender.RecommendedItem;
import org.apache.mahout.cf.taste.recommender.Recommender;
import org.apache.mahout.cf.taste.similarity.UserSimilarity;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
public class UserRecommenderMovieLens {
private UserRecommenderMovieLens(){
}
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.err.println("Needs MovieLens 1M dataset as arugument!");
System.exit(-1);
}
File resultFile = new File(System.getProperty("java.io.tmpdir"), "userRcomed.csv");
DataModel dataModel = new MovieLensDataModel(new File(args[0]));
UserSimilarity similarity = new PearsonCorrelationSimilarity(dataModel);
UserNeighborhood neighborhood = new NearestNUserNeighborhood(100, similarity, dataModel);
Recommender recommender = new GenericUserBasedRecommender(dataModel, neighborhood, similarity);
Recommender cachingRecommender = new CachingRecommender(recommender);
//Evaluate
RMSRecommenderEvaluator evaluator = new RMSRecommenderEvaluator();
RecommenderBuilder recommenderBuilder = new RecommenderBuilder() {
@Override
public Recommender buildRecommender(DataModel dataModel) throws TasteException {
UserSimilarity similarity = new PearsonCorrelationSimilarity(dataModel);
UserNeighborhood neighborhood = new NearestNUserNeighborhood(100, similarity, dataModel);
return new GenericUserBasedRecommender(dataModel, neighborhood, similarity);
}
};
double score = evaluator.evaluate(recommenderBuilder, null, dataModel, 0.9, 0.5);
System.out.println("RMSE score is "+score);
try(PrintWriter writer = new PrintWriter(resultFile)){
for (int userID=1; userID <= dataModel.getNumUsers(); userID++){
List<RecommendedItem> recommendedItems = cachingRecommender.recommend(userID, 2);
String line = userID+" : ";
for (RecommendedItem recommendedItem: recommendedItems){
line += recommendedItem.getItemID()+":"+recommendedItem.getValue()+",";
}
if (line.endsWith(",")){
line = line.substring(0, line.length()-1);
}
writer.write(line);
writer.write('\n');
}
} catch (IOException ioe){
resultFile.delete();
throw ioe;
}
System.out.println("Recommended for "+dataModel.getNumUsers()+" users and saved them to "+resultFile.getAbsolutePath());
}
}
運行代碼,結(jié)果如下:
RMSE score is 1.0747072266152768
Recommended for 6040 users and saved them to C:\Users\ADMINI~1\AppData\Local\Temp\userRcomed.csv
打開userRcomed.csv文件,如下:
1 : 32:5.0,28:5.0
2 : 2726:5.0,2607:5.0
3 : 2624:5.0,1262:5.0
使用電影數(shù)據(jù)集,給每個用戶推薦了2個打分最高的商品。