基於鄰域的協同過濾主要分為兩類,基於用戶的協同過濾和基於物品的協同過濾。前者給用戶推薦和他興趣相似的其他用戶喜歡的物品,后者則是推薦和他之前喜歡過的物品相似的物品。
基於用戶的協同過濾算法
這里介紹基於用戶的協同過濾,從定義來說,可以分為以下兩步進行:
- 找到和目標用戶興趣相似的用戶集合
- 找和這個集合中的用戶喜歡的,且目標用戶沒有聽說過的物品推薦給目標用戶
計算用戶相似度的基本算法:
(1)Jaccard 公式
(2)余弦相似度:
得到用戶之間的興趣相似度后,UserCF算法會給用戶推薦和他興趣最相似的K個用戶喜歡的物品,下面的公式表示用戶u對物品i的感興趣程度:
其中S(u,K)包含和用戶u興趣最相近的K個用戶,N(i)是對物品i有過行為的用戶集合,wuv是用戶u和用戶v的興趣相似度,rvi代表用戶v對物品i的興趣,在這種情況下rvi=1
可以建立物品到用戶的倒查表,對每個物品都保存對該物品產生過行為的用戶列表,
可以給上圖中的A推薦,選取K=3,用戶A對物品c,e沒有過行為,因此可以把這兩個物品推薦給用戶A,用戶A對物品c,e的興趣是:
改進:
上邊的算法是有問題的,比如兩個人都買過《新華字典》這本書,但這絲毫不能說明他們兩個興趣相似,因為大多數人都買過這本書,如果兩個用戶都買過《數據挖掘導論》,那可以認為兩個人的興趣比較相似,因為只要研究數據挖掘的人才會買這本書。即兩個人對冷門物品采取過同樣的行為更能說明他們興趣的相似性,因此相似性度量函數為:
基於物品的協同過濾算法
下面介紹基於物品的協同過濾算法,其過程主要分為2步:
- 計算物品之間的相似度
- 根據物品的相似度和用戶的歷史行為給用戶生成推薦列表
計算物品的相似度:
N(i):喜歡物品i的用戶數 |N(i)∩N(j)|:同時喜歡物品i和物品j的用戶數
與UserCF算法類似,用ItenCF算法計算物品相似度時,也可以首先建立用戶-物品倒排表(即對每個用戶建立一個包含他喜歡的物品的列表),然后對於每個用戶,將物品列表中的物品兩兩在共現矩陣C中加1,最終將這些矩陣相加得到上邊的C矩陣,其中C[i][j]記錄同時喜歡物品i和物品j的用戶數,最后將c矩陣歸一化得到物品之間的余弦相似度矩陣W。
得到物品的相似度之后,ItemCF通過如下公式計算用戶u對一個物品j的興趣:
N(u)是用戶喜歡的物品的集合,S(i,k)是和物品i最相似的k個物品的集合,wji 是物品j和i的相似度,rui是用戶u對物品i的興趣。對於隱反饋數據集,如果用戶u對物品i有過行為,即可令rui=1,該公式的含義是,和用戶歷史上感興趣的物品越相似,越有可能在用戶的推薦列表中獲得比較高的排名。
用戶活躍度對用戶的影響
除了上面的分析權重的過程,還可以考慮用戶活躍度對物品相似度的影響IUF,即活躍用戶對物品相似度的貢獻應該小於不活躍的用戶,因襲增加IUF參數來修正物品相似度的計算公式
物品相似度歸一化
如果已經得到了物品的相似性矩陣w,則可以得到歸一化之后的相似度矩陣w'
歸一化之后的好處是不僅僅增加推薦的准確度,還提高了覆蓋率和多樣性。
實現算法:
import math import time import pandas as pd def calcuteSimilar(series1,series2): ''' 計算余弦相似度 :param data1: 數據集1 Series :param data2: 數據集2 Series :return: 相似度 ''' unionLen = len(set(series1) & set(series2)) if unionLen == 0: return 0.0 product = len(series1) * len(series2) similarity = unionLen / math.sqrt(product) return similarity def calcuteUser(csvpath,targetID=1,TopN=10): ''' 計算targetID的用戶與其他用戶的相似度 :return:相似度TopN Series ''' frame = pd.read_csv(csvpath) #讀取數據 targetUser = frame[frame['UserID'] == targetID]['MovieID'] #目標用戶數據 otherUsersID = [i for i in set(frame['UserID']) if i != targetID] #其他用戶ID otherUsers = [frame[frame['UserID'] == i]['MovieID'] for i in otherUsersID] #其他用戶數據 similarlist = [calcuteSimilar(targetUser,user) for user in otherUsers] #計算 similarSeries = pd.Series(similarlist,index=otherUsersID) #Series return similarSeries.sort_values()[-TopN:] def calcuteInterest(frame,similarSeries,targetItemID): ''' 計算目標用戶對目標物品的感興趣程度 :param frame: 數據 :param similarSeries: 目標用戶最相似的K個用戶 :param targetItemID: 目標物品 :return:感興趣程度 ''' similarUserID = similarSeries.index #和用戶興趣最相似的K個用戶 similarUsers = [frame[frame['UserID'] == i] for i in similarUserID] #K個用戶數據 similarUserValues = similarSeries.values #用戶和其他用戶的興趣相似度 UserInstItem = [] for u in similarUsers: #其他用戶對物品的感興趣程度 if targetItemID in u['MovieID'].values: UserInstItem.append(u[u['MovieID']==targetItemID]['Rating'].values[0]) else: UserInstItem.append(0) interest = sum([similarUserValues[v]*UserInstItem[v]/5 for v in range(len(similarUserValues))]) return interest def calcuteItem(csvpath,targetUserID=1,TopN=10): ''' 計算推薦給targetUserID的用戶的TopN物品 :param csvpath: 數據路徑 :param targetUserID: 目標用戶 :param TopN: :return: TopN個物品及感興趣程度 ''' frame = pd.read_csv(csvpath) #讀取數據 similarSeries = calcuteUser(csvpath=csvpath, targetID=targetUserID) #計算最相似K個用戶 userMovieID = set(frame[frame['UserID'] == 1]['MovieID']) #目標用戶感興趣的物品 otherMovieID = set(frame[frame['UserID'] != 1]['MovieID']) #其他用戶感興趣的物品 movieID = list(userMovieID ^ otherMovieID) #差集 interestList = [calcuteInterest(frame,similarSeries,movie) for movie in movieID] #推薦 interestSeries = pd.Series(interestList, index=movieID) return interestSeries.sort_values()[-TopN:] #TopN if __name__ == '__main__': print('start..') start = time.time() a = calcuteItem('ratings.csv') print(a) print('Cost time: %f'%(time.time()-start))
參考:http://blog.csdn.net/sinat_33741547/article/details/52740010