協同過濾(collaborative filtering)
推薦系統:
百度百科的定義是:它是利用電子商務網站向客戶提供商品信息和建議,幫助用戶決定應該購買什么產品,模擬銷售人員幫助客戶完成購買過程
主要有有以下幾種推薦的方式:
- 基於內容的推薦
- 協同過濾
- 關聯推薦
- 混合推薦
協同過濾
這里我們主要考慮的是協同過濾,這也是最經典的推薦算法。協同過濾的思想很簡單,就是像我們平時需要找一部好看的電影最簡單的方式就是找興趣相同的人推薦。
相似度計算:
相似度的計算主要有以下幾種方法:
- 基於歐氏距離 相似度=1/(1+歐式距離)
- 基於皮爾遜相關系數(Pearson correlation) 0.5+0.5*corrcoef()
- 余弦相似度 0.5+0.5*cos
item-based CF & user-based CF:
item-based CF
基於item的協同過濾,通過用戶對不同item的評分來評測item之間的相似性,基於item之間的相似性做出推薦;
user-based CF
基於user的協同過濾,通過不同用戶對item的評分來評測用戶之間的相似性,基於用戶之間的相似性做出推薦;
推薦系統的評價:
最小均方根誤差(Root Mean Squared Error,RMSE):首先計算均方誤差值,然后取其平方根。(如果用戶的評價在一星到五星,而我們的RMSE=1,說明我們的預估和用戶評價相差一個星級)
面臨的挑戰:
實例和SVD優化
背景:
構建一個餐館食物推薦引擎,推薦給用戶他沒有嘗試過的最適合他的選擇。
做法:
我們這里采用的是item-based CF。就是推斷出用戶對那些沒有嘗試過的評分再根據評分推薦。另外由於現實中客戶是遠遠不可能嘗試所有的產品的,所以實際的矩陣中很多的值都是0,這時候我們可以采 用SVD進行降維,在小的多的數據情況下得到相似的結果。
函數:
ecludSim(inA, inB)
基於歐式距離的相似度計算pearsSim(inA, inB)
基於皮爾森距離的相似度計算cosSim(inA, inB)
余弦相似度的計算standEst(dataMat, user, simMeans, item)
求出用戶對物品的估計分值,計算出相似度和評分成績,最后相似度評分進行歸一化使結果在0到5之間svdEst(dataMat, user, simMeas, item)
在估計分數的過程中使用svd降維,保留90%的能量值。def recommend(dataMat, user, N=3, simMeas=cosSim, estMethod=standEst)
對給定的用戶推薦給他N個產品。過程很簡單,首先找出那些他沒有評級的產品,然后調用estMethod給出評分,再選評分前面幾個。
-
#coding=utf-8 from numpy import * def loadExData(): return[[4, 4, 0, 2, 2], [4, 0, 0, 3, 3], [4, 0, 0, 1, 1], [1, 1, 1, 2, 0], [2, 2, 2, 0, 0], [5, 5, 5, 0, 0], [1, 1, 1, 0, 0]] def loadExData2(): return[[0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 5], [0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 3], [0, 0, 0, 0, 4, 0, 0, 1, 0, 4, 0], [3, 3, 4, 0, 0, 0, 0, 2, 2, 0, 0], [5, 4, 5, 0, 0, 0, 0, 5, 5, 0, 0], [0, 0, 0, 0, 5, 0, 1, 0, 0, 5, 0], [4, 3, 4, 0, 0, 0, 0, 5, 5, 0, 1], [0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 4], [0, 0, 0, 2, 0, 2, 5, 0, 0, 1, 2], [0, 0, 0, 0, 5, 0, 0, 0, 0, 4, 0], [1, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0]] def ecludSim(inA, inB): return 1.0 / (1.0 + linalg.norm(inA - inB)) def pearsSim(inA, inB): if len(inA) < 3: return 1.0 return 0.5 + 0.5*corrcoef(inA , inB, rowVal = 0)[0][1] def cosSim(inA, inB): num = float(inA.T*inB) denom = linalg.norm(inA)*linalg.norm(inB) return 0.5 + 0.5 * (num/denom) def standEst(dataMat, user, simMeans, item): n =shape(dataMat)[1] simTotal = 0.0 ratSimTotal = 0.0 for j in range(n): userRating = dataMat[user,j] if userRating == 0: continue overLap = nonzero(logical_and(dataMat[:,item].A>0, \ dataMat[:,j].A>0))[0] if len(overLap) == 0: similarity = 0 else: similarity = simMeans(dataMat[overLap,item], dataMat[overLap,j]) simTotal += similarity ratSimTotal += similarity * userRating if simTotal == 0: return 0 else: return ratSimTotal / simTotal def svdEst(dataMat, user, simMeas, item): n = shape(dataMat)[1] simTotal = 0.0; ratSimTotal = 0.0 U,Sigma,VT = linalg.svd(dataMat) sig2 = Sigma ** 2 cut = 0 for i in range(n): if sum(sig2[:i]) / sum(sig2) > 0.9: print i cut = i break Sig4 = mat(eye(cut)*Sigma[:cut]) #arrange Sig4 into a diagonal matrix xformedItems = dataMat.T * U[:,:cut] * Sig4.I #create transformed items for j in range(n): userRating = dataMat[user,j] if userRating == 0 or j==item: continue similarity = simMeas(xformedItems[item,:].T,\ xformedItems[j,:].T) print 'the %d and %d similarity is: %f' % (item, j, similarity) simTotal += similarity ratSimTotal += similarity * userRating if simTotal == 0: return 0 else: return ratSimTotal/simTotal def recommend(dataMat, user, N=3, simMeas=cosSim, estMethod=standEst): unratedItems = nonzero(dataMat[user,:].A==0)[1]#find unrated items if len(unratedItems) == 0: return 'you rated everything' itemScores = [] for item in unratedItems: estimatedScore = estMethod(dataMat, user, simMeas, item) itemScores.append((item, estimatedScore)) return sorted(itemScores, key=lambda jj: jj[1], reverse=True)[:N] def main(): myMat = mat(loadExData2()) print recommend(myMat,2,estMethod=svdEst) if __name__ == '__main__': main()