Traditional Recommendation Methods(傳統推薦系統CF,MF)
開篇
此前所總結學習的機器學習演算法無不可以運用到各種資料中,在有些演算法的實現中合適的資料真的不那麼的好找,特別是對於原生的演算法來說甚至是自己生成有噪音的資料。但作為已經出現幾十年的Internet,人們利用它去交流去交易去購買去娛樂同樣也太久,也太多。沒錯,這些就是天然的資訊,也是天然的資料,大資料時代我們了無隱私可言,而技術門檻從這裡開始。
傳統的推薦系統
一般都是根據大量使用者的活動所產生的大量資訊,然後所產生的群體偏好再加以利用,比如某寶的商品推薦【看上圖…】,熱門視訊,猜你喜歡,相親匹配等等。如果想了解商品,影片,最沒有技術含量的方法就是向我們的朋友詢問,然後採納他的建議。所以很自然能想到尋找相同品味的人,然後根據最相似的他人喜好給出推薦就可以了。這就是協同過濾
在展開之前瞭解一下推薦系統所想要達到的目標,不妨換位思考作為一名顧客或者使用者來說,我們最想要得到的效果是什麼呢?首當其衝自然是滿意度要高,所以在推薦的準確度一定要好,儘可能不要推薦不喜歡的,但是新穎性和驚喜度也要好,另外對於商家來說覆蓋率(儘可能所有的商品都有被推薦出去),多樣性也很重要。最後對於開發者時間,空間,任務還有如何實時等等等問題,所以如何在保證大多數使用者推薦準確性的同時增加推薦的多樣性個性,還要使特殊喜好的小群體使用者得到相對完美的快速推薦呢?
協同過濾:包括線上的協同和離線的過濾兩部分。線上協同,就是通過線上資料找到使用者可能喜歡的物品,而離線過濾,則是過濾掉一些不值得推薦的資料,比如推薦值評分低的資料,或者雖然推薦值高但是使用者已經購買的資料。
它的實現主要分為兩個方面:基於內容的實現和基於物品的實現。不過類似上看就是對一大堆使用者或者物品進行搜尋,然後用歐幾里德,皮爾遜,jaccard係數,曼哈頓計算等等去計算相似度。再根據是具體的標量物體或者是應用在類似電影的評分這種連續性資料,看做是一個普通的分類或者回歸問題。另外還存在基於模型的協同過濾,比如它會提前提取出一個特徵向量x,然後針對每個使用者建模,即每個使用者打的分值作為y值,利用這些已有的分值y和特徵值x去訓練迴歸模型(最常見的就是線性迴歸),這樣就可以預測那些使用者沒有評分的分數。從另一個角度來看,也可以是先給定每個使用者對某種物品的喜好程度(即權值),然後學出每種物品的特徵,最後採用迴歸來預測那些沒有被評分的物品。
比如類似的資料集Datas={‘PA’:{‘WA’:2 ,‘WB’:3,‘WC’:5},‘PB’:{‘WA’:4,‘WC’:2},‘PC’:{‘WB’:2,‘WC’:3}},P和W分別表示使用者和物品的相關評判做一個小例子。
from math import sqrt
def sim_distance(prefs,person1,person2): #歐幾里得計算使用者相似度
wu={}
for item in prefs[person1]: #找出都有過操作的物品,key為物品編號,值為1
if item in prefs[person2]:
wu[item]=1
if len(wu)==0: #如果完全沒有相同的
return 0
#計算所有差值的平方和
for item in prefs[person1]:
if item in prefs[person2]:
sum_of_squares=sum([pow(prefs[person1][item]-prefs[person2][item],2)])
return 1/(1+sqrt(sum_of_squares))
'''#皮爾遜計算使用者相似度
sum1=sum([prefs[p1][it] for it in wu]) #分別求和共同擁有物品的評分
sum2=sum([prefs[p2][it] for it in wu])
sum1Sq=sum([pow(prefs[p1][it],2)for it in wu])
sum2Sq=sum([pow(prefs[p2][it],2)for it in wu])
pSum=sum([prefs[p1][it]*prefs[p2][it] for it in wu])
num=pSum-(sum1*sum2/n) #計算皮爾遜相似度
den=sqrt((sum1Sq-pow(sum1,2)/n)*(sum2Sq-pow(sum2,2)/n))
if den == 0:
return 0
return(num/den)
‘’‘
def topN(prefs,person,n=5,similarity=sim_pearson): #找出最相似的N個,預設為5
scores=[(similarity(prefs,person,other),other)for other in prefs if other != person]
scores.sort()
scores.reverse()
return scores[0:n]
def getRecommendations(prefs,person,similarity=sim_pearson): #推薦
totals={}
simSums={}
for other in prefs:
if other==person:
continue
sim=similarity(prefs,person,other)
if sim<=0:
continue
for item in prefs[other]: #只對未操作的進行評價
if item not in prefs[person] or prefs[person][item]==0:
totals.setdefault(item,0)
totals[item]+=prefs[other][item]*sim #相似度*評價值
simSums.setdefault(item,0) #沒有就新建
simSums[item]+=sim
rankings=[(total/simSums[item],item)for item,total in totals.items()] #建立列表
rankings.sort()
rankings.reverse()
return rankings #返回經過排序的列表
如何應對大量的使用者與物品的關係?
首先在實際的計算中兩者關係的存貯都是會用到矩陣的。
MF矩陣分解,MF其實就是基於模型的協同過濾,因為協同過濾的本質就是矩陣分解和矩陣填充 。但凡是學過線代的人應該提到矩陣分解就應該想到它–奇異值分解(SVD)。此時可以將這個使用者物品對應的m×n矩陣M進行SVD分解,並通過選擇部分較大的一些奇異值來同時進行降維(k),也就是說矩陣MM此時分解為:
所以如果我們要預測第i個使用者對第j個物品的評分Mij,則只需要計算
即可。通過這種方法進行評分,然後找到最高的若干個評分對應的物品推薦給使用者就行了。
雖然SVD簡單粗暴,但是SVD分解要求矩陣是稠密的。但是對於推薦系統來說使用者物品矩陣往往稀疏,如果要全部補全難度太大,耗時太長。於是FunkSVD演算法出現了:
它只要求變成兩個矩陣從而避開稀疏性問題,但是兩個矩陣我們沒有公式呀?沒關係,機器學習世界換湯不換藥!!所以嘗試尋找最好的兩個矩陣P,Q使最後的評分誤差最小就行了,也就是
沒錯,讓我們求導,讓我們梯度下降。
設定a步長不斷迭代如
就可以求出P,Q了。
優勢:最後整體來說協同過濾根據使用者對物品或者資訊的偏好,發現物品或者內容本身的相關性,或者是發現使用者的相關性,不需要對物品或者使用者進行嚴格的建模,而且不要求物品的描述是機器可理解的,所以這種方法也是領域無關的。但是缺點在於它方法的核心是基於歷史資料,所以對新物品和新使用者都有“冷啟動”的問題。推薦的效果依賴於使用者歷史偏好資料的多少和準確性。而且使用者歷史偏好是用稀疏矩陣進行儲存的,而稀疏矩陣上的計算有些明顯的問題,包括可能少部分人的錯誤偏好會對推薦的準確度有很大的影響等等。