本隨筆主要記錄本人對協同過濾算法的學習理解與Python的實現,主要參考資料爲項亮老師的《推薦系統實踐》和Prateek Joshi 老師的《Python機器學習經典實例》兩本書。python
一.基於用戶的協同過濾簡介算法
利用用戶行爲數據構建推薦系統有三類算法:基於鄰域的算法、隱語義模型和基於圖的模型。json
基於鄰域的算法主要有基於用戶的協同過濾算法和基於物品的協同過濾算法,這裏要學習的是基於用戶的協同過濾算法。數據結構
基於用戶的協同過濾算法(UserCF)是推薦系統中最古老的算法。能夠不誇張地說,這個算法的誕生標誌了推薦系統的誕生。app
二.算法思路機器學習
當一個用戶 A 須要個性化推薦時,能夠先找到和他有類似興趣的其餘用戶,而後把那些用戶喜歡的、而用戶 A 沒有據說過的物品推薦給 A 。這種方法稱爲基於用戶的協同過濾算法。學習
從上面的描述中能夠看到,基於用戶的協同過濾算法主要包括兩個步驟。spa
(1) 找到和目標用戶興趣類似的用戶集合。
(2) 找到這個集合中的用戶喜歡的,且目標用戶沒有據說過的物品推薦給目標用戶。code
幾個關鍵的問題:blog
用戶之間的興趣類似度如何度量?
答:目前提供四種選擇
首先獲得下面的評分矩陣數據結構(矩陣或是字典),橫軸爲用戶,縱軸爲物品,Fij爲用戶i對物品j的評分值(能夠看做已經選擇了該物品),空值爲未選擇該物品。
1. 歐式距離,有選擇物品爲1,未選擇物品爲0,能夠計算兩個用戶選擇物品向量的歐式距離。
2.Jaccard 公式,分子爲兩個用戶共同選擇的物品數,分母爲兩個用戶選擇物品的並集物品數。
3.餘弦類似度,分子爲兩個用戶共同選擇的物品數,分母爲兩個用戶選擇物品數乘積的平方根。
4.對餘弦類似度的改進,|N(i)|爲選擇物品i的用戶數(物品i的流行度),物品i是兩個用戶共同選擇的物品。分子爲基於這些物品的流行度的算式結果的和,分母爲兩個用戶選擇物品數乘積的平方根。
三.Python實現
1.數據
這裏使用MovieLens電影評分數據集進行實驗,包含6040個用戶,3900個電影,1000209個評分。能夠本身下載,或者網盤鏈接提取。
連接:https://pan.baidu.com/s/1ZLnw4-z88GAYTdgf_ZAq9A
提取碼:fqx3
數據文件夾以下,
README爲數據介紹,
movies爲電影信息數據,有電影ID,電影名稱,電影風格三個屬性。
users爲用戶信息數據,有用戶ID,性別,年齡,職業,zip-code五個屬性。
ratings爲用戶評分數據,有用戶ID,電影ID,評分三個屬性。
2.目標
使用MovieLens電影評分數據集構建一個推薦系統引擎。推薦引擎能夠對數據集中的用戶推薦若干電影(不涉及冷啓動問題),輸入用戶ID,返回給該用戶的電影推薦候選集。
3.代碼結構
代碼結構以下,
UserDF_1用於讀取原始數據,並將評分數據構建成字典並存儲爲json文件持久化,即獲得評分矩陣(見二部分)。
UserDF_2用於讀取評分矩陣,計算用戶興趣類似度矩陣且建成字典存儲爲json文件持久化,即獲得用戶興趣類似度矩陣。
圖用戶興趣類似度矩陣。
UserDF_3用於基於評分矩陣和用戶興趣類似度矩陣計算目標用戶的TopN個候選推薦電影的集合。
UserDF_1.py
#coding=utf-8 #基於用戶的協同過濾算法 #1.讀取數據 #2.構建須要的數據結構 #3.計算用戶間的興趣類似度 (餘弦類似度),取K個與目標用戶興趣最類似用戶 #4.計算目標用戶對其未觀看電影感興趣程度 (在K的興趣最類似用戶觀看的且目標用戶未觀看的電影中,計算目標用戶對每一個電影的感興趣程度, # 感興趣程度=K個類似用戶對電影有評分記錄的用戶的類似度之和) import pandas as pd import numpy as np from sklearn.model_selection import KFold import json import pickle #1-n:讀取數據,將原始數據轉換爲方便計算的數據結構 r_nanes = ['userid','movieid','rating','timestamp'] ratings_data = pd.read_table(filepath_or_buffer=u'E:\DataSet\ml-1m-電影推薦\ml-1m\\ratings.dat', sep='::',header=None,names=r_nanes,engine='python') print(ratings_data.columns) print(ratings_data.shape) print(ratings_data.head(5)) u_nanes = ['userid','gender','age','occupation','zip-code'] users_data = pd.read_table(filepath_or_buffer=u'E:\DataSet\ml-1m-電影推薦\ml-1m\\users.dat', sep='::',header=None,names=u_nanes,engine='python') print(users_data.columns) print(users_data.shape) m_nanes = ['movieid','title','genres'] movies_data = pd.read_table(filepath_or_buffer=u'E:\DataSet\ml-1m-電影推薦\ml-1m\\movies.dat', sep='::',header=None,names=m_nanes,engine='python') print(movies_data.columns) print(movies_data.shape) num_user = len(users_data['userid']) num_movie = len(movies_data['movieid']) print(num_user) print(num_movie) userids = users_data['userid'].values movieids = movies_data['movieid'].values ratings_data_narray = ratings_data.values userid_movieid_rating = {} key = 0 for userid in userids: print('userid:',userid) ratings_dict = {} for i in (range(len(ratings_data_narray))[key:]): if ratings_data_narray[i][0]==userid: ratings_dict[str(ratings_data_narray[i][1])]=str(ratings_data_narray[i][2]) print(key) key += 1 else: continue userid_movieid_rating[str(userid)]=ratings_dict print('----------------') print(key) print(len(userid_movieid_rating)) with open(file='userid_movieid_rating.json',mode='w') as f: json.dump(userid_movieid_rating,f)
UserDF_2.py
import numpy as np import json # 2-n讀取持久化數據,計算用戶類似度,獲得用戶類似度矩陣 def simlar_score(dataset, user1, user2): if user1 not in dataset: raise TypeError('User ' + user1 + ' not present in the dataset') if user1 not in dataset: raise TypeError('User ' + user2 + ' not present in the dataset') Itemset_user1 = list(dataset[user1].keys()) Itemset_user2 = list(dataset[user2].keys()) #print(type(Itemset_user1)) intersection = np.intersect1d(Itemset_user1, Itemset_user2) # if intersection!=[]: # print(type(Itemset_user1)) # print(Itemset_user2) # print(intersection) numerator = len(intersection) #print(numerator) denominator = np.sqrt(len(Itemset_user1) * len(Itemset_user2)) #print(denominator) simlar_score = numerator / denominator #print(simlar_score) return simlar_score with open(file='userid_movieid_rating.json',mode='r') as f: dataset = json.load(f) users_simlar_score = {} for user1 in dataset: print(user1) user_user_simlar_scores = {} for user2 in dataset: simlar_sc = simlar_score(dataset,user1,user2) user_user_simlar_scores[user2] = str(simlar_sc) users_simlar_score[user1] = user_user_simlar_scores with open(file='users_simlar_score.json',mode='w') as f: json.dump(users_simlar_score,f)
UserDF_3.py
import numpy as np import json #取得一個用戶的做品 # def getItemOfUser(dataset_users_rating,userid): # # items = list[dataset_users_rating[userid].keys()] # # return items #獲取目標用戶的推薦候選集 def getRecommendationItemForUser(dataset_users_rating,dataset_users_simlar,tuserid,K=5,topN=500): #獲得目標用戶與其餘用戶的類似度 item_ranks = np.array([[userid,float(users_simalar)] for userid,users_simalar in dataset_users_simlar[tuserid].items()]) #按類似度排序(由大到小),取得前K個其餘用戶的[用戶ID,類似度值] item_ranks_K_ousers = item_ranks[np.argsort(item_ranks[:,1])[::-1]][1:K+1] #print(item_ranks_K_ousers) #獲得類似度最大的K的用戶的用戶ID K_ousers = item_ranks_K_ousers[:,0] print('與目標用戶興趣最類似的',str(K),'個用戶') print(K_ousers) print('----------') #獲得似度最大的K的用戶的選擇物品的並集 items_candidate = [] for userid in K_ousers: items_user = list(dataset_users_rating[userid].keys()) #print(len(items_user)) items_candidate = np.union1d(items_candidate,items_user) print('似度最大的K的用戶的選擇物品的並集,物品數量爲:') print(len(items_candidate)) print('----------') #獲得在似度最大的K的用戶的選擇物品的並集中且不在目標用戶選擇物品集合中的物品做爲推薦候選集合 RecommendationItems = np.setdiff1d(items_candidate,list(dataset_users_rating[tuserid].keys())) print('在似度最大的K的用戶的選擇物品的並集中且不在目標用戶選擇物品集合中的物品做爲推薦候選集合,物品數量爲:') print(len(RecommendationItems)) print('----------') #print(RecommendationItems) #計算用戶對於全部候選集合物品的偏好度,偏好度=K個用戶中對物品有行爲的用戶與目標用戶的類似度的和 user_like_scores = {} for item_r in RecommendationItems: user_like_score = [] for user in K_ousers: if item_r in list(dataset_users_rating[user].keys()): #print(tuserid,user) #print(dataset_users_simlar[tuserid][user]) user_like_score.append(float(dataset_users_simlar[tuserid][user])) user_like_scores[item_r] = str(sum(user_like_score)) #按偏好度排序取topN user_like_scores_list = np.array([[item,like_score] for item,like_score in user_like_scores.items()]) RecommendationItems_topN = user_like_scores_list[np.argsort(user_like_scores_list[:,1])[::-1]][:topN] #print(len(RecommendationItems_topN),'--------------') print('興趣最高的top',str(topN),'個物品及用戶興趣度:') print(RecommendationItems_topN) print('----------') #返回topN的物品名稱 return RecommendationItems_topN[:,0] if __name__ == '__main__': with open(file='userid_movieid_rating.json', mode='r') as f: dataset_users_rating = json.load(f) with open(file='users_simlar_score.json', mode='r') as f: dataset_users_simlar = json.load(f) tuserid = '10' topN = 100 K = 3 RecommendationItems_topN = getRecommendationItemForUser(dataset_users_rating, dataset_users_simlar, tuserid,K=K,topN=topN) print('用戶',tuserid,'的top',str(topN),'推薦物品列表:') print(RecommendationItems_topN)