基於鄰域的協同過濾主要分爲兩類,基於用戶的協同過濾和基於物品的協同過濾。前者給用戶推薦和他興趣類似的其餘用戶喜歡的物品,後者則是推薦和他以前喜歡過的物品類似的物品。算法
基於用戶的協同過濾算法app
這裏介紹基於用戶的協同過濾,從定義來講,能夠分爲如下兩步進行:函數
計算用戶類似度的基本算法:ui
(1)Jaccard 公式spa
(2)餘弦類似度:3d
獲得用戶之間的興趣類似度後,UserCF算法會給用戶推薦和他興趣最類似的K個用戶喜歡的物品,下面的公式表示用戶u對物品i的感興趣程度:rest
其中S(u,K)包含和用戶u興趣最相近的K個用戶,N(i)是對物品i有過行爲的用戶集合,wuv是用戶u和用戶v的興趣類似度,rvi表明用戶v對物品i的興趣,在這種狀況下rvi=1code
能夠創建物品到用戶的倒查表,對每一個物品都保存對該物品產生過行爲的用戶列表,blog
能夠給上圖中的A推薦,選取K=3,用戶A對物品c,e沒有過行爲,所以能夠把這兩個物品推薦給用戶A,用戶A對物品c,e的興趣是:get
改進:
上邊的算法是有問題的,好比兩我的都買過《新華字典》這本書,但這絲絕不能說明他們兩個興趣類似,由於大多數人都買過這本書,若是兩個用戶都買過《數據挖掘導論》,那能夠認爲兩我的的興趣比較類似,由於只要研究數據挖掘的人才會買這本書。即兩我的對冷門物品採起過一樣的行爲更能說明他們興趣的類似性,所以類似性度量函數爲:
基於物品的協同過濾算法
下面介紹基於物品的協同過濾算法,其過程主要分爲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))