簡單的基於項目的協同過濾算法
技術概述
協同過濾算法是一種利用集體智慧的方法,它類似與朋友推薦,當你想要看一個電影時,你會去詢問跟你有着相同喜好的人有沒有自己沒看過的好電影。這就是協同過濾的核心思想
技術詳述
簡介
在開始講解本次的前,我們先介紹下常見的幾種協同過濾算法
協同過濾一般分為三類:第一種是基於用戶(user-based)的協同過濾,第二種是基於項目(item-based)的協同過濾,第三種是基於模型(model based)的協同過濾。
-
其中第一種是考慮用戶之間的相似性,先根據被推薦的用戶,尋找相似的用戶,找到一個相似用戶后,預測被推薦用戶對相似用戶喜歡的物品的評分,然后再尋找相似的用戶,直到滿足終止條件為止。然后這些物品中評分最高的一批就是我們要推薦給被推薦的用戶的物品
-
第二種與第一種十分相似,不同點在於,不是先尋找相似用戶,再預測物品的評分,而是直接尋找相似的物品,然后預測物品的評分,評分高的一批就是推薦的物品。
-
基於模型的協同過濾作為目前最主流的協同過濾類型,其相關算法可以寫一本書了,當然我們這里主要是對其思想做有一個歸類概括。我們的問題是這樣的m個物品,m個用戶的數據,只有部分用戶和部分數據之間是有評分數據的,其它部分評分是空白,此時我們要用已有的部分稀疏數據來預測那些空白的物品和數據之間的評分關系,找到最高評分的物品推薦給用戶。
一般對於小型的推薦系統來說,基於項目的協同過濾肯定是主流。但是如果是大型的推薦系統來說,則可以考慮基於用戶的協同過濾,當然更加可以考慮第三種類型,基於模型的協同過濾。
內容
公式
一般的基於項目的協同過濾算法的基礎公式如下
I(ij)是代表用戶i和用戶j共同評價過的物品, R(i,x)代表用戶i對物品x的評分, R(i)頭上有一杠的代表用戶i所有評分的平均分, 之所以要減去平均分是因為有的用戶打分嚴有的松, 歸一化用戶打分避免相互影響。該公式其實就是數學中的Pearson相關系數,對於評分數據不規范時,皮爾遜相關度評價也能夠給出不錯的結果。
步驟
協同過濾的步驟一般分為三步
- 收集用戶偏好
- 找到相似的物品
- 計算推薦
1. 收集用戶偏好
要從用戶的行為和偏好中發現規律,並基於此給予推薦,如何收集用戶的偏好信息成為系統推薦效果最基礎的決定因素。用戶有很多方式向系統提供自己的偏好信息,而且不同的應用也可能大不相同
在本項目中我們推薦的項目是回答。用戶對於回答的操作有瀏覽、點贊、反對、評論、舉報五種操作。這五種操作都需要存儲在數據庫中,方便推薦算法去獲取用戶數據。
在收集到用戶數據后,我們還要對用戶數據進行一定的預處理,其中最重要的就是:減噪和歸一化
- 減噪
用戶行為數據是用戶在使用應用過程中產生的,它可能存在大量的噪音和用戶的誤操作,我們可以通過經典的數據挖掘算法過濾掉行為數據中的噪音,這樣可以是我們的分析更加精確。
在本項目中,由於技術能力不夠,沒有對用戶進行減噪 - 歸一化
如前面講到的,在計算用戶對物品的喜好程度時,可能需要對不同的行為數據進行加權。但可以想象,不同行為的數據取值可能相差很大,比如,用戶的查看數據必然比購買數據大的多,如何將各個行為的數據統一在一個相同的取值范圍中,從而使得加權求和得到的總體喜好更加精確,就需要我們進行歸一化處理。
本項目采用了最簡單的歸一化處理,就是將各類數據除以此類中的最大值,以保證歸一化后的數據取值在 [0,1] 范圍中。
收集到的用戶數據在經過處理后便形成一個用戶偏好的二維矩陣,一維是用戶列表,另一維是物品列表,值是用戶對物品的偏好,是在[0,1]內浮點數值。
這里根據不同的項目有不同的加權和分組,故不展示代碼
2. 找到相似的物品
關於相似度的計算,現有的幾種基本方法都是基於向量(Vector)的,其實也就是計算兩個向量的距離,距離越近相似度越大。
本項目采用的是皮爾遜相關系數(Pearson Correlation Coefficient)
皮爾遜相關系數一般用於計算兩個定距變量間聯系的緊密程度,它的取值在 [-1,+1] 之間。
公式如下:
Java實現代碼:
/**
* 計算皮爾遜相關系數
* @param mapX 向量A
* @param mapY 向量B
* @return 兩個回答的Pearson相關系數,值在[-1,1]間
*/
public static double caculatePearson(Map<BigInteger, Double> mapX, Map<BigInteger, Double> mapY) {
// (sum(X*Y) - (sum(X)*sum(Y)/N))/sqr((sum(pow(X))-(pow(sum(X))/N)) * ((sum(pow(Y))-(pow(sum(Y))/N))))
double sumXY = 0d;
double sumX = 0d;
double sumY = 0d;
double sumPowX = 0d;
double sumPowY = 0d;
Set<BigInteger> setItem = new HashSet<>();
for (Map.Entry<BigInteger, Double> entry : mapX.entrySet()) {
setItem.add(entry.getKey());
}
for (Map.Entry<BigInteger, Double> entry : mapY.entrySet()) {
setItem.add(entry.getKey());
}
for (BigInteger bookId : setItem) {
Double x = mapX.get(bookId);
if (x == null) {
x = 0d;
}
Double y = mapY.get(bookId);
if (y == null) {
y = 0d;
}
sumXY += x * y;
sumX += x;
sumY += y;
sumPowX += Math.pow(x, 2);
sumPowY += Math.pow(y, 2);
}
int n = setItem.size();
double pearson = (sumXY - sumX * sumY / n) / Math.sqrt((sumPowX - Math.pow(sumX, 2) / n) * (sumPowY - Math.pow(sumY, 2) / n));
return pearson;
}
通過計算后,便能得到兩個回答之間的相似度了,
3. 計算推薦
經過了前兩步,我們就可以得到物品之間的相似度。
然后我們就需要根據用戶的歷史偏好,找出用戶喜歡的物品,然后推薦相似的物品給他。從計算的角度看,就是將所有用戶對某個物品的偏好作為一個向量來計算物品之間的相似度,得到物品的相似物品后,根據用戶歷史的偏好預測當前用戶還沒有表示偏好的物品,計算得到一個排序的物品列表作為推薦。
下圖 給出了一個例子,對於物品 A,根據所有用戶的歷史偏好,喜歡物品 A 的用戶都喜歡物品 C,得出物品 A 和物品 C 比較相似,而用戶 C 喜歡物品 A,那么可以推斷出用戶 C 可能也喜歡物品 C。
技術使用中遇到的問題和解決過程
冷啟動問題
描述
冷啟動問題又稱第一評價問題(first- rater),或新物品問題(New-item),從一定角度可以看成是稀疏問題的極端情況。因為傳統的協同過濾推薦是基於相似用戶/物品計算來得到目標用戶的推薦,在一個新的項目首次出現的時候,因為沒有用戶對它作過評價,因此單純的協同過濾無法對其進行預測評分和推薦。
解決方案
在本項目中,對新用戶沒有可推薦的問題時,便推薦數據庫中最新的回答。由於項目本身是一個問答網站,推薦最新的回答也有利於用戶發現新的回答並做出互動。
此外還有一種方案是利用用戶的注冊信息/認證信息。在用戶初次登錄時,由用戶選擇適合自己的標簽/風格,然后去推薦相關領域的物品
總結
過濾協同是一種比較經典的推薦算法了,而本文所介紹的基於項目的過濾協同算法,是過濾協同中基於領域的一類。該算法實現起來相對簡單,對數學知識要求也不高,容易理解其思想。但是協同過濾也有些難以避免的難題,比如令人頭疼的“冷啟動”問題,我們沒有新用戶任何數據的時候,無法較好的為新用戶推薦物品。同時也沒有考慮情景的差異,比如根據用戶所在的場景和用戶當前的情緒。當然,也無法得到一些小眾的獨特喜好,這塊是基於內容的推薦比較擅長的。
總的來說對於軟工實踐普遍的項目來說,實現簡單,初期先用來推薦,但“冷啟動”問題是這個算法很大的弊端,畢竟項目的規模一般不會太多,冷啟動問題不好解決。可以考慮采用混合推薦算法。