推荐系统之基于邻域的算法---协同过滤算法

基于邻域的协同过滤主要分为两类,基于用户的协同过滤和基于物品的协同过滤。前者给用户推荐和他兴趣类似的其余用户喜欢的物品,后者则是推荐和他以前喜欢过的物品类似的物品。算法

基于用户的协同过滤算法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))
复制代码
相关文章
相关标签/搜索