標籤:
這個轉自csdn,很貼近工程。
協同過濾(Collective Filtering)可以說是推薦系統的標配演算法。
在談推薦必談協同的今天,我們也來談一談基於KNN的協同過濾在實際的推薦應用中的一些心得體會。
我們首先從協同過濾的兩個假設聊起。
兩個假設:
- 使用者一般會喜歡與自己喜歡物品相似的物品
- 使用者一般會喜歡與自己相似的其他使用者喜歡的物品
上述假設分別對應了協同過濾的兩種實現方式:基於物品相似(item_cf)及基於使用者相似(user_cf)。
因此,協同過濾在實現過程中,最本質的任務就是計算相似性的問題,包括計算item之間的相似性,或user之間的相似性。要計算相似性,就需要一個用來計算相似性的表達。
輸入資料:
而協同過濾的輸入是一個使用者對item的評分矩陣,如所示:
這種輸入資料中,沒有關於item或user的任何擴充描述。
因此item和user之間只能互為表達,用所有使用者對某item的評分來描述一個item,以及用某個使用者所有評分的item來描述該使用者本身。分別對應途中的行向量和列向量。
這樣,我們只需要搞定向量之間相似性的計算問題就OK了。
相似性計算:
以Item相似性為例,我們一般使用如下的公式完成相似性的計算:
其中:
Wij表示標號為i,j的兩個item的相似性
U(i,j)表示同時對i,j有評分的使用者的集合
Rui表示使用者u對item i的評分
參數lambda為平滑(懲罰)參數
當然,相似性的計算有很多可選的方案, 例如直接計算兩個向量夾角的cos值。只是通過實驗發現,文中提到的方法在Lz的實際推薦應用中,效果要略好而已。
對於user相似性則只需要把輸入矩陣轉置看待即可。
評分預測過程:
仍以item_cf為例,在得到item相似性之後,可以通過一個矩陣乘法運算來為輸入資料中的空白處計算一個預測值。
將輸入矩陣看成一個n*m的矩陣,記為A,n為使用者數,m為item數。而計算得到的相似性矩陣為一個m*m的矩陣,記為B。
則預測評分的過程就是A*B的過程,結果也是一個n*m的矩陣。
具體公式如下:
其中bi為歌曲本身的流行程度,可通過其他方式計算得到。
相似性計算的複雜度:
item相似性計算的複雜度:輸入矩陣A(n*m) 中的使用者數為n,item數為m,則要計算出一個m*m的相似性矩陣的話共需要O=m*m*n*d,d為資料的稀疏性。
假設n=200w,m=5w,資料稀疏性為1%,則O=50萬億。好在該過程可以很方便地使用map-reducer的方式在hdfs上分布式實現。
再假設你通過1000個計算節點來完成,則每個計算節點上的複雜度O1=5000億。
user相似性計算的複雜度:O=n*n*m*d,假設計算資源仍為1000,則每個計算節點的複雜度為O1=2000萬億。
相似性計算的兩種實現方式:
一般來講,item的量都是比較小的,而且固定,而使用者的量會比較大,而且每天都可能會不一樣。
因此,在計算過程中的實用策略也不同,大體上有如下兩種:
1. 倒排式計算:
這種方式尤其適用於使用者量較大,而item量較小,且每個使用者已評分的item數較少的情況。
具體做法就是:
在MR的map階段將每個使用者的評分item組合成pair <left,right,leftscore,rightscore> 輸出,left作為分發鍵,left+right作為排序鍵。
在reduce階段,將map中過來的資料掃一遍即可求得所有item的相似性。
詳細Hadoop過程如下所示:
該方式的好處在於沒有關聯的item之間不會產生相似性的計算開銷,所謂沒有關聯指沒有任何一個使用者對這兩個item都有評分。
不好的地方在於,如果某些使用者評分資料較多,則產生的pair對會十分巨大,在map和reducer之間會有極大的IO開銷。
2. 矩陣分塊計算:
這種計算方式適用於使用者量遠大於item量的情況下,使用者相似性的計算過程。
具體做法:將評分矩陣M切分成多個小塊,每個小塊與原矩陣的轉置矩陣做類似矩陣乘法的運算(按照文中提到的相似性計算公式,而非向量內積),
最後將計算得到的結果合并即可。
詳細Hadoop過程如下所示:
該方式的好處是避免map與reducer之間的大量緩衝,而且這種方式壓根不需要reducer。
不好的地方在於transpose(M)需要在任務開始計算前緩衝在每個hadoop計算節點,在矩陣M較大的時候會影響任務的啟動效率。
兩種方式各有優劣,精確計算user之間或item之間的相似性時需要根據實際的資料特點選擇合適的計算方式。
然而,隨著業務的增長,評分矩陣會越來越大,同時也會越來越稠密。
這時無論採用哪一種方式,精確地計算user之間或item之間的相似性已可望而不可及。
而且,考慮實際的推薦情境,在資料足夠多的情況下,相似性的精確計算也沒有必要。
基於SimHash的相似性計算:
當資料量太大時,往往只需要求得一個與最優解相近的近似解即可,相似性的計算也是如此。
基於SimHash計算使用者之間或item之間的相似性是推薦中較為常用的技巧。
該方法之所以能夠work,主要基於如下兩點:1.hash的隨機性,2.資料足夠多。
這兩點決定了通過simhash計算得到的相似性結果是否靠譜。
而關於SimHash的具體原理,本文不做詳述,不熟悉的同學可參閱連結simhash演算法原理或其他相關資料。
這裡主要介紹下自己在實際工作中的做法:
首先Hash函數的選擇:
本人在實際工作中嘗試了兩種Hash函數:
1. DJBX33A,將字串映射成一個64位的不帶正負號的整數。具體實現參考:DJBX33A雜湊函數實現
2. MD5, 將字串映射成一個128為的二進位流。本人在實際工作中使用char[16]來表示。
漢明距離與相似性:
在計算得到每個對象的hashsign之後,可進一步得到兩個hashsign的漢明距離。假設最終的sign為n維,則漢明距離最大為n,最小為0.分別對應相似性為-1,1。
當漢明距離為n/2時,兩個hashsign對應的對象之間的相似性為0,也可以理解為他們的夾角為 PI/2。
可以通過如下公式對漢明距離與相似性之間做轉換:
其中N為sign維數,Hij為i,j之間的漢明距離。
漢明距離的計算:
1. 如果hashcode在64位以內,最終計算的hashsign可以使用電腦內建的類型來表示,如unsigned long。
然後對兩個對象的hashsig按位異或,計算結果中有多少位為1,即為漢明距離值。
2. 如果hashcode大於64位,例如MD5,則需要一個複合結構來表示。我們可以使用char[16]來表示128bit。
然後在計算漢明距離時分別使用兩個unsigned long 來表示高位的64bit和低位的64bit。
實踐證明,通過simhash計算的到的相似性,與真實的相似性之間幾乎等同。
而且,在實際應用中,通過simhash的推薦結果,魯棒性也更強。
推薦系統之協同過濾