輸入
輸入:物品用戶行為矩陣,行為矩陣中的元素只有0和1,0代表行為的負類,1代表行為的正類。比如不喜歡與喜歡、不點贊與點贊、不收藏與收藏。
輸出
輸出1:根據輸入可計算得到物品相似度矩陣;
輸出2:根據輸入中物品用戶行為矩陣得到用戶喜歡的物品,用戶喜歡的物品結合輸出1得到的物品相似度矩陣,可以計算得到用戶喜歡度最高的k個物品,並推薦給用戶。
前言
基於物品的協同過濾算法適用於物品數明顯小於用戶數的場景,適用於長尾物品豐富,用戶個性化需求強烈的領域。但是該算法不適用於物品變化較快的場景,比如新聞類的應用,如果物品一直在變化,那么會增加物品相似度更新的頻率,不夠穩定,此類場景下可以考慮選擇基於用戶的協同過濾算法。
原理
基於物品的協同過濾誕生於1998年,是由亞馬遜首先提出,適用於用戶明顯多於物品的情況。基於物品的協同過濾算法的原始想法是:給用戶推薦用戶喜歡的物品相似的物品。問題在於“相似的物品”是如何度量的,基於內容的推薦系統中,物品相似是用內容的相似性計算出來的,但是實際上人的群體行為還是會有一些內容特征抓不到的相似性。
在基於物品的協同過濾之前,最常用的是基於用戶的協同過濾。基於用戶的協同過濾首秀按計算相似用戶,然后再根據相似用戶的喜好推薦物品,但是基於用戶的協同過濾存在以下問題:
- 用戶的數量往往很多,用戶之間相似度計算起來工作量大;
- 用戶的興趣愛好變化較快,所以這個動態變化的過程很難被跟蹤和記錄。
基於物品的協同過濾可以較好的解決上面的問題。物品的數量遠遠少於用戶的數量,而且物品之間的相似度不易改變,物品對應的消費者數量大,所以計算出來的物品相似度比較可靠。
協同過濾算法依賴於用戶物品之間的關系矩陣(如表1所示)。基於物品的協同過濾算法除了依賴用戶物品之間的關系矩陣,還依賴於物品之間的相似度矩陣(如表2所示)。

計算物品的相似度有好幾種方法,比如余弦相似度、傑卡德(Jaccard)相似度,余弦相似度適用於評分數據,傑卡德(Jaccard)相似度適用於隱式反饋數據,由於這里使用的用戶戶物品矩陣是隱式反饋數據,所以介紹下Jaccard相似度的計算公式:
式子表示的是物品M與N的相似度,其中
表示喜歡物品M的用戶的集合,
表示喜歡物品N的用戶的集合,分子為同時喜歡物品M,N的用戶集合,分母為喜歡物品M,N的用戶集合的並集。
通過上面的公式計算得到物品相似度矩陣后,就可以結合用戶已經喜歡過的物品集合給用戶推薦物品了,可以通過下面的公式計算用戶對待推薦物品的評分:

計算用戶u對物品i的評分,需要遍歷用戶喜歡過的物品集合A,對於集合A中的任意物品j,求用戶u對j評分
與物品i、j的相似度sim(i,j)之積,然后累加得到用戶u對物品i的評分。
可以依次求得用戶u對所有物品的評分,去掉用戶已經喜歡過的物品后,保留分數最高的N個結果推薦給用戶u。
python代碼實現
1 #1、手動構造一個用戶物品行為矩陣,行為物品,列為用戶,矩陣元素值為(0,1),0代表不感興趣,1代表感興趣 2 #2、根據用戶行為矩陣,計算出物品相似度矩陣 3 #3、根據物品相似度矩陣以及用戶喜歡的物品推薦出TopN個物品 4 import numpy as np 5 # 100*200的物品用戶行為矩陣 6 item_user_matrix = np.random.randint(0,2,size=(100, 200),dtype=np.int) 7 8 # 計算物品的相似度矩陣sim,形狀為100*100 9 # 對於隱式反饋數據,使用傑卡德(Jaccard)相似系數計算物品的相似度,sim(i,j)表示物品i與物品j的相似度, 10 # 等於同時喜歡物品i和j的用戶數除以喜歡物品i和物品j的用戶總數 11 rowNum = item_user_matrix.shape[0] 12 columNum = item_user_matrix.shape[0] 13 sim_matrix = np.zeros((rowNum, columNum)).astype('float') 14 #同時喜歡下標為colum的物品和下標為row的物品的用戶數 15 columAndRow_NUM = 0 16 #喜歡下標為colum的物品的用戶數與喜歡下標為row的物品的用戶數之和 17 columOrRow_NUM = 0 18 for row in range(0, rowNum): 19 for colum in range(0, columNum): 20 if colum < row: # 只需要對矩陣下三角更新 21 # 因為矩陣元素只有0和1,求點積即為交集 22 columAndRow_NUM = np.dot(item_user_matrix[colum], item_user_matrix[row]) 23 # 元素相加求和,就求得並集 24 columOrRow_NUM = item_user_matrix[colum].sum() + item_user_matrix[row].sum() 25 sim_matrix[row][colum] = columAndRow_NUM/columOrRow_NUM 26 print('columAndRow_NUM:{}, columOrRow_NUM:{}, sim_matrix[{}][{}]:{}'. 27 format(columAndRow_NUM,columOrRow_NUM, row, colum, sim_matrix[row][colum])) 28 29 # 根據物品相似度矩陣以及用戶喜歡的物品推薦出TopN個物品 30 # 欲求用戶user對物品i的喜歡度,首先求出用戶user喜歡的物品集合,根據物品相似度矩陣,求出物品i與用戶user喜歡的物品對應的相似度 31 # 相似度與喜歡度相乘求和,除以相似度之和,得到的商為用戶user對物品i的相似度 32 # 求得喜歡度之后,選擇topN個做推薦 33 def topNrecom(user, N): 34 # 字典的鍵為物品下標編號,值為喜歡度 35 result = {} 36 # user對應的物品向量 37 itemVec = item_user_matrix[:,user] 38 for item in range(0,rowNum): 39 # 直接做點積即可,因為用戶還沒有發生行為的物品喜歡度為0,與相似度相乘還是0,無影響 40 user_item_sim_sum = np.dot(itemVec, sim_matrix[item]) 41 result[item] = user_item_sim_sum; 42 # 從result中去掉用戶已經發生過行為的物品,然后再推薦出預測喜歡度較高的top N個物品 43 for index in range(0, len(itemVec)): 44 if(1 == itemVec[index]): 45 result.pop(index) 46 return sorted(result.items() , key=lambda x: x[1], reverse=True)[:N] 47 48 user = 122 49 N = 10 50 itemList = topNrecom(user, N) 51 print('user[{}] top [{}] recom result:{}'.format(user, N, itemList))
代碼運行結果
本文代碼手動構造了一個100*200的物品用戶行為矩陣,實際工業環境中肯定不止這點數據量,本中重點在於講解原理和一種實現方法,對於某個用戶而言,推薦喜歡度最高的10個物品,代碼運行結果如下:
1 user[122] top [10] recom result:[(98, 13.734837466951667), (97, 13.534563830373337), (90, 12.351537112348055), (94, 12.194631579469013), (82, 11.25929195188074), (84, 10.662303442296619), (80, 10.60327372276929), (79, 10.570187047487309), (77, 9.94780886977415), (69, 8.431845325504977)]
按照喜歡度從高到底排序,推薦的物品id為98、97、90、94、82、84、80、79、77、69。
