1. 背景
在數據爆炸我們每天被數據困擾的今天,數據量發生指數級的增長,每一年產生的大數據是過去歷史的總和。那么在茫茫數據大海中,對於數據生產者,怎么將自己的信息精准投放給所需的用戶呢?而對於數據消費者,怎么從海量數據中快速獲取自己需要的信息呢?這時推薦引擎應運而生。
推薦應用其實已經走進我們的生活,典型的推薦應用:
a. 豆瓣網站
豆瓣根據我的讀書列表,及相應評論,為我推薦出如下結果:
b. 購物網站
亞馬孫根據我的購書清單和相應書評,為我推薦的如下結果:
1.1 問題抽象
我們可以把推薦模型進行數據的抽象,假設有1,2,3,4和5個用戶分別各自購買了101,102,103至107物品中的物品,並對其進行了各自購買的物品進行評分,那么推薦引擎如何根據用戶喜好,推薦他們各自未買的物品呢?如下數據集:
2. 推薦引擎背景及算法
如果你最近想看電影,你可能咨詢周圍的人,而且通常你更傾向詢問與“興趣相投”的人,從他們的口味中,得出一個最近電影排名集,來敲定你想看的電影,這是協同過濾的基本思想。圖解:
算法具體解決的兩個問題:
a. 如何找到“興趣相投的”人群,形成一個群體,我們稱之為鄰域“neighborhood“。
b. 如何從既定的鄰域中,排序出喜好的項目列表?
2.1 相似度
如果我們把每個用戶的評分列表看成一個一維度向量,那么可以根據向量的相似度定量化用戶之間的相似性。相似度分類主要有:
a. 基於歐幾里德距離計算相似度
b. 基於 Cosine 相似度
c. 基於皮爾遜相關系數計算相似度
d. 基於 Tanimoto 系數計算相似度
2.2 鄰域
物以類聚,既然我們獲取了用戶之間的相似度值,那么那些人群歸為一個鄰域呢?通常有兩種方法:
a. 規定 Fix-size,根據相似度值排名,取前fix-size的個數作為一個鄰域
b. 規定threshold,相似度值大於規定值歸為一個鄰域
2.3 推薦值計算
定量的計算推測用戶a對某個物品v的喜好程度,公式如下: ΣSim(i,j)Vi
即為其它各位用戶對物品v的評分值的線性加權平均,其中,權重是相似度值sim,v表示對各物品的評分。
所以整個計算的過程是通過2.1和2.2解決問題a,然后根據2.3解決問題b的過程。
2.4 推薦算法分類
典型的推薦算法有:基於用戶的推薦和基於物品的推薦兩種。
上述的思路從基於用戶的推薦方法講解的,而在實際電商網站應用中,由於物品數量是遠遠大於每位用戶的購買數量的,而且用戶之間購買的物品重疊性較低,很難找到鄰域。這時,可以換個從商品的角度思考,商品間的相似性。根據用戶購物歷史偏好,計算物品間的相似性,流程和基於用戶的推薦一樣。
3. mahout協同過濾的應用接口
3.1 相似度
相似度接口有UserSimilarity和ItemSimilarity,其主要方法如下:
double userSimilarity(long userID1, long userID2) double itemSimilarity(long itemID1, long itemID2)
其繼承類分別:
a. PearsonCorrelationSimilarity:基於皮爾遜相關系數計算相似度
b. EuclideanDistanceSimilarity:基於歐幾里德距離計算相似度
c. TanimotoCoefficientSimilarity:基於 Tanimoto 系數計算相似度
d. UncerteredCosineSimilarity:計算 Cosine 相似度
3.2 鄰域
鄰域類UserNeighborhood,其主要接口如下:
long[] getUserNeighborhood(long userID)
輸入給定用戶,給出與其相似的用戶列表
其繼承類分別:
a. NearestNUserNeighborhood:對每個用戶取固定數量 N 的最近鄰居
b. ThresholdUserNeighborhood:對每個用戶基於一定的限制,取落在相似度門限內的所有用戶為鄰居。
3.3 Recommender
關聯數據模型和相似度算法模型,進行具體的計算,接口如下:
List<RecommendedItem> recommend(long userID, int howMany)
給定用戶id和推薦的結果個數,返回推薦的結果
3.4 各類圖關系如下:
4. 代碼樣例和推薦結果
4.1 mahout代碼樣例
public static void main(String[] args) throws IOException, TasteException { // TODO Auto-generated method stub System.out.println("starting "); String file = "item.csv"; DataModel model = new FileDataModel(new File(file)); System.out.println("starting userCF "); userCF(model); System.out.println(); System.out.println("starting itemCF"); itemCF(model); } public static void userCF(DataModel model) { try { UserSimilarity user = new UncenteredCosineSimilarity(model); NearestNUserNeighborhood neighbor = new NearestNUserNeighborhood(NEIGHBORHOOD_NUM, user, model); Recommender r = new GenericUserBasedRecommender(model, neighbor, user); LongPrimitiveIterator iter = model.getUserIDs(); while (iter.hasNext()) { long uid = iter.nextLong(); List<RecommendedItem> list = r.recommend(uid, RECOMMENDER_NUM); System.out.printf("user:%s", uid); for (RecommendedItem rItem : list) { System.out.printf("(%s,%f)", rItem.getItemID(), rItem.getValue()); } System.out.println(); } } catch (TasteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void itemCF(DataModel dataModel) { try { ItemSimilarity itemSimilarity = new UncenteredCosineSimilarity(dataModel); Recommender recommender = new GenericItemBasedRecommender(dataModel, itemSimilarity); Recommender cachingRecommender = new CachingRecommender(recommender); LongPrimitiveIterator iter = dataModel.getUserIDs(); while (iter.hasNext()) { long uid = iter.nextLong(); List<RecommendedItem> list = cachingRecommender.recommend(uid, RECOMMENDER_NUM); System.out.printf("user:%s", uid); for (RecommendedItem rItem : list) { System.out.printf("(%s,%f)", rItem.getItemID(), rItem.getValue()); } System.out.println(); } } catch (TasteException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
4.2 推薦結果
5. 評估標准
結果推薦出來,自然需要一套標准評判各自方法推薦的結果好壞。評價標准可以參考http://blog.fens.me/mahout-recommendation-api/#gsc.tab=0的2節。
參考:
1. http://en.wikipedia.org/wiki/Collaborative_filtering
2. http://www.ibm.com/developerworks/cn/web/1103_zhaoct_recommstudy1/
3. https://www.ibm.com/developerworks/cn/web/1103_zhaoct_recommstudy2/
4. http://www.ibm.com/developerworks/cn/web/1103_zhaoct_recommstudy3/