mahout推薦演算法——協同過濾推薦演算法(java程式碼實現)
什麼是協同過濾
協同過濾是利用集體智慧的一個典型方法。要理解什麼是協同過濾 (Collaborative Filtering, 簡稱 CF),首先想一個簡單的問題,如果你現在想看個電影,但你不知道具體看哪部,你會怎麼做?大部分的人會問問周圍的朋友,看看最近有什麼好看的電影推薦,而我們一般更傾向於從口味比較類似的朋友那裡得到推薦。這就是協同過濾的核心思想。
協同過濾一般是在海量的使用者中發掘出一小部分和你品位比較類似的,在協同過濾中,這些使用者成為鄰居,然後根據他們喜歡的其他東西組織成一個排序的目錄作為推薦給你。當然其中有一個核心的問題:
- 如何確定一個使用者是不是和你有相似的品位?
- 如何將鄰居們的喜好組織成一個排序的目錄?
協同過濾相對於集體智慧而言,它從一定程度上保留了個體的特徵,就是你的品位偏好,所以它更多可以作為個性化推薦的演算法思想。可以想象,這種推薦策略在 Web 2.0 的長尾中是很重要的,將大眾流行的東西推薦給長尾中的人怎麼可能得到好的效果,這也回到推薦系統的一個核心問題:瞭解你的使用者,然後才能給出更好的推薦。
協同過濾的步驟是:
建立資料模型 —> 使用者相似度演算法—>使用者近鄰演算法 —>推薦演算法。
基於使用者的協同過濾演算法在Mahout庫中已經模組化了,通過4個模組進行統一的方法呼叫。首先,建立資料模型(DataModel),然後定義使用者的相似度演算法(UserSimilarity),接下來定義使用者近鄰演算法(UserNeighborhood ),最後呼叫推薦演算法(Recommender)完成計算過程。而基於物品的協同過濾演算法(ItemCF)過程也是類似的,去掉第三步計算使用者的近鄰演算法就行了。
計算推薦
經過前期的計算已經得到了相鄰使用者和相鄰物品,下面介紹如何基於這些資訊為使用者進行推薦。基於協同過濾的推薦演算法可以分為基於使用者的 CF 和基於物品的 CF,下面我們深入介紹這兩種方法的計算方法
基於使用者的 CF(User CF)
基於使用者的 CF 的基本思想相當簡單,基於使用者對物品的偏好找到相鄰鄰居使用者,然後將鄰居使用者喜歡的推薦給當前使用者。計算上,就是將一個使用者對所有物品的偏好作為一個向量來計算使用者之間的相似度,找到 K 鄰居後,根據鄰居的相似度權重以及他們對物品的偏好,預測當前使用者沒有偏好的未涉及物品,計算得到一個排序的物品列表作為推薦。圖 2 給出了一個例子,對於使用者 A,根據使用者的歷史偏好,這裡只計算得到一個鄰居 - 使用者 C,然後將使用者 C 喜歡的物品 D 推薦給使用者 A。
圖 2.基於使用者的 CF 的基本原理
基於物品的 CF(Item CF)
基於物品的 CF 的原理和基於使用者的 CF 類似,只是在計算鄰居時採用物品本身,而不是從使用者的角度,即基於使用者對物品的偏好找到相似的物品,然後根據使用者的歷史偏好,推薦相似的物品給他。從計算的角度看,就是將所有使用者對某個物品的偏好作為一個向量來計算物品之間的相似度,得到物品的相似物品後,根據使用者歷史的偏好預測當前使用者還沒有表示偏好的物品,計算得到一個排序的物品列表作為推薦。圖 3 給出了一個例子,對於物品 A,根據所有使用者的歷史偏好,喜歡物品 A 的使用者都喜歡物品 C,得出物品 A 和物品 C 比較相似,而使用者 C 喜歡物品 A,那麼可以推斷出使用者 C 可能也喜歡物品 C。
圖 3.基於物品的 CF 的基本原理
Java程式碼:
UserCF:
package com.pt;
import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.impl.common.LongPrimitiveIterator;
import org.apache.mahout.cf.taste.impl.model.file.*;
import org.apache.mahout.cf.taste.impl.neighborhood.*;
import org.apache.mahout.cf.taste.impl.recommender.*;
import org.apache.mahout.cf.taste.impl.similarity.*;
import org.apache.mahout.cf.taste.model.*;
import org.apache.mahout.cf.taste.recommender.*;
import org.apache.mahout.cf.taste.similarity.*;
import java.io.*;
import java.util.*;
public class UserCF {
final static int NEIGHBORHOOD_NUM = 2;//臨近的使用者個數
final static int RECOMMENDER_NUM = 3;//推薦物品的最大個數
public static void main(String[] args) throws IOException, TasteException {
String file = "src/data/testCF.csv";
DataModel model = new FileDataModel(new File(file));//資料模型
UserSimilarity user = new EuclideanDistanceSimilarity(model);//使用者相識度演算法
NearestNUserNeighborhood neighbor = new NearestNUserNeighborhood(NEIGHBORHOOD_NUM, user, model);
//使用者近鄰演算法
Recommender r = new GenericUserBasedRecommender(model, neighbor, user);//使用者推薦演算法
LongPrimitiveIterator iter = model.getUserIDs();///得到使用者ID
while (iter.hasNext()) {
long uid = iter.nextLong();
List<RecommendedItem> list = r.recommend(uid, RECOMMENDER_NUM);
System.out.printf("uid:%s", uid);
for (RecommendedItem ritem : list) {
System.out.printf("(%s,%f)", ritem.getItemID(), ritem.getValue());
}
System.out.println();
}
}
}
ItemCF:
package com.pt;
import java.io.File;
import java.io.IOException;
import java.util.List;
import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.impl.common.LongPrimitiveIterator;
import org.apache.mahout.cf.taste.impl.model.file.FileDataModel;
import org.apache.mahout.cf.taste.impl.recommender.GenericItemBasedRecommender;
import org.apache.mahout.cf.taste.impl.similarity.EuclideanDistanceSimilarity;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.recommender.RecommendedItem;
import org.apache.mahout.cf.taste.recommender.Recommender;
import org.apache.mahout.cf.taste.similarity.ItemSimilarity;
public class ItemCF {
final static int RECOMMENDER_NUM = 3;//推薦物品的最大個數
public static void main(String[] args) throws IOException, TasteException {
String file = "src/data/testCF.csv";
DataModel model = new FileDataModel(new File(file));//資料模型
ItemSimilarity item=new EuclideanDistanceSimilarity(model);//使用者相識度演算法
Recommender r=new GenericItemBasedRecommender(model,item);//物品推薦演算法
LongPrimitiveIterator iter =model.getUserIDs();
while(iter.hasNext()){
long uid=iter.nextLong();
List<RecommendedItem> list = r.recommend(uid, 1);
System.out.printf("uid:%s",uid);
for (RecommendedItem ritem : list) {
System.out.printf("(%s,%f)", ritem.getItemID(), ritem.getValue());
}
System.out.println();
}
}
}
資料集:
第一列:userid;第二列:itemid;第三列評分。
1,101,5.0
1,102,3.0
1,103,2.5
2,101,2.0
2,102,2.5
2,103,5.0
2,104,2.0
3,101,2.5
3,104,4.0
3,105,4.5
3,107,5.0
4,101,5.0
4,103,3.0
4,104,4.5
4,106,4.0
5,101,4.0
5,102,3.0
5,103,2.0
5,104,4.0
5,105,3.5
5,106,4.0