推薦系統之基於鄰域的算法---協同過濾算法

基於鄰域的協同過濾主要分爲兩類,基於用戶的協同過濾和基於物品的協同過濾。前者給用戶推薦和他興趣類似的其餘用戶喜歡的物品,後者則是推薦和他以前喜歡過的物品類似的物品。算法

基於用戶的協同過濾算法app

這裏介紹基於用戶的協同過濾,從定義來講,能夠分爲如下兩步進行:函數

  1. 找到和目標用戶興趣類似的用戶集合
  2. 找和這個集合中的用戶喜歡的,且目標用戶沒有據說過的物品推薦給目標用戶

計算用戶類似度的基本算法: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步:

  1. 計算物品之間的類似度
  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))
複製代碼
相關文章
相關標籤/搜索