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


基於鄰域的協同過濾主要分為兩類,基於用戶的協同過濾和基於物品的協同過濾。前者給用戶推薦和他興趣相似的其他用戶喜歡的物品,后者則是推薦和他之前喜歡過的物品相似的物品。

基於用戶的協同過濾算法

這里介紹基於用戶的協同過濾,從定義來說,可以分為以下兩步進行:

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

計算用戶相似度的基本算法:

(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步:

  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))

 參考:http://blog.csdn.net/sinat_33741547/article/details/52740010


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM