基于用户的协同过滤算法的Python实现

基于用户的协同过滤算法也被称为最近邻协同过滤或KNN (K.Nearest-Neighbor,K最近邻算法)。其核心思想就是,首先根据相似度计算出目标用户的邻居集合,然后用邻居用户评分的加权组合来为目标用户作推荐。

通常这些算法都可以总结成三步:

  1. 首先,使用用户已有的评分来计算用户之间的相似度;
  2. 然后,选择与目标用户相似度最高的K个用户,通常把这些用户称为邻居;
  3. 最后,通过对邻居用户的评分的加权平均来预测目标用户的评分。为了方便说明,我们把系统中用户的集合记为U。物品的集合记为I,用户u,v∈U,物品i,j∈I, 是用户对物品的评分,而用户u和v之间的相似度记为 ,用一个m×n的矩阵来表示所个用户对玎个物品的评分情况。
用来衡量用户之间的相似性方法有很多,最常见的有两种:Pearson相关系数、余弦相似度以及调整余弦相似度。

Pearson相关系数将两个用户共同评分的n个项目看做一组向量,计算两个用户在这n个项目上评分的相关性,减去用户平均评分是基于用户评分尺度的考量,公式如下:

基于用户的协同过滤算法的Python实现_第1张图片

其中是用户u和v都评过分的项目的集合,是用户u所有评分的平均分。

余弦相似度则是把用户的评分(包括所有项目,未评过分的项目分数则为0)看作是一个向量,通过计算两个向量夹角的余弦来衡量用户之间的相似性,其定义如公式如下:


得到用户相似度后,接下来的工作就是对近邻用户下载过的应用进行评分预测,公式如下:


其中得到的就是用户u对物品i的评分的预测,K是邻居的集合也就是和用户u最相似的用户的集合。

Python代码如下:

'''
数据集包括 
    * 943个用户对1682部电影的100,000 评分 (1-5) . 
    * 每个用户至少评分20个电影.  
	*每部电影的信息
u.data     -- 943个用户对1682部电影的100,000 评分(1-5). 
              每个用户至少评分20个电影. 用户和电影的编号都从1开始. 数据随机排列. 每行的信息为:
              user id | item id | rating | timestamp. 
              time stamps是从1/1/1970 UTC起的unix 秒数  
u.item     -- 所有电影的信息,每行记录的信息有: 
              movie id | movie title | release date | video release date | 
              IMDb URL | unknown | Action | Adventure | Animation | 
              Children's | Comedy | Crime | Documentary | Drama | Fantasy | 
              Film-Noir | Horror | Musical | Mystery | Romance | Sci-Fi | 
              Thriller | War | Western | 
              最后19个属性是电影的类型,是该类型该属性值为1,否者为0,一个电影可以同时为多种类型
u1.base    -- The data sets u1.base and u1.test through u5.base and u5.test
u1.test       are 80%/20% splits of the u data into training and test data.
u2.base       Each of u1, ..., u5 have disjoint test sets; this if for
u2.test       5 fold cross validation (where you repeat your experiment
u3.base       with each training and test set and average the results).
u3.test       These data sets can be generated from u.data by mku.sh.
u4.base
u4.test
u5.base
u5.test
'''
from math import sqrt


def loadData():
    trainSet = {}
    testSet = {}
    movieUser = {}
    u2u = {}
    

    TrainFile = 'ml-100k/u1.base'   #指定训练集 
    TestFile = 'ml-100k/u1.test'    #指定测试集
    #加载训练集
    for line in open(TrainFile):
        (userId, itemId, rating, timestamp) = line.strip().split('\t')   
        trainSet.setdefault(userId,{})
        trainSet[userId].setdefault(itemId,float(rating))

        movieUser.setdefault(itemId,[])
        movieUser[itemId].append(userId.strip())
    #加载测试集
    for line in open(TestFile): 
        (userId, itemId, rating, timestamp) = line.strip().split('\t')   
        testSet.setdefault(userId,{})
        testSet[userId].setdefault(itemId,float(rating))

    #生成用户用户共有电影矩阵
    for m in movieUser.keys():
        for u in movieUser[m]:
            u2u.setdefault(u,{})
            for n in movieUser[m]:
                if u!=n:
                    u2u[u].setdefault(n,[])
                    u2u[u][n].append(m)
    return trainSet,testSet,u2u
      
  

#计算一个用户的平均评分  
def getAverageRating(user):  
    average = (sum(trainSet[user].values())*1.0) / len(trainSet[user].keys())  
    return average

#计算用户相似度  
def getUserSim(u2u,trainSet):
    userSim = {}
    # 计算用户的用户相似度  
    for u in u2u.keys(): #对每个用户u
        userSim.setdefault(u,{})  #将用户u加入userSim中设为key,该用户对应一个字典
        average_u_rate = getAverageRating(u)  #获取用户u对电影的平均评分
        for n in u2u[u].keys():  #对与用户u相关的每个用户n             
            userSim[u].setdefault(n,0)  #将用户n加入用户u的字典中

            average_n_rate = getAverageRating(n)  #获取用户n对电影的平均评分
              
            part1 = 0  #皮尔逊相关系数的分子部分
            part2 = 0  #皮尔逊相关系数的分母的一部分
            part3 = 0  #皮尔逊相关系数的分母的一部分
            for m in u2u[u][n]:  #对用户u和用户n的共有的每个电影  
                part1 += (trainSet[u][m]-average_u_rate)*(trainSet[n][m]-average_n_rate)*1.0  
                part2 += pow(trainSet[u][m]-average_u_rate, 2)*1.0  
                part3 += pow(trainSet[n][m]-average_n_rate, 2)*1.0  
                  
            part2 = sqrt(part2)  
            part3 = sqrt(part3)  
            if part2 == 0 or part3 == 0:  #若分母为0,相似度为0
                userSim[u][n] = 0
            else:
                userSim[u][n] = part1 / (part2 * part3)
    return userSim
  

#寻找用户最近邻并生成推荐结果
def getRecommendations(N,trainSet,userSim):
    pred = {}
    for user in trainSet.keys():    #对每个用户
        pred.setdefault(user,{})    #生成预测空列表
        interacted_items = trainSet[user].keys() #获取该用户评过分的电影  
        average_u_rate = getAverageRating(user)  #获取该用户的评分平均分
        userSimSum = 0
        simUser = sorted(userSim[user].items(),key = lambda x : x[1],reverse = True)[0:N]
        for n, sim in simUser:  
            average_n_rate = getAverageRating(n)
            userSimSum += sim   #对该用户近邻用户相似度求和
            for m, nrating in trainSet[n].items():  
                if m in interacted_items:  
                    continue  
                else:
                    pred[user].setdefault(m,0)
                    pred[user][m] += (sim * (nrating - average_n_rate))
        for m in pred[user].keys():  
                pred[user][m] = average_u_rate + (pred[user][m]*1.0) / userSimSum
    return pred

#计算预测分析准确度
def getMAE(testSet,pred):
    MAE = 0
    rSum = 0
    setSum = 0

    for user in pred.keys():    #对每一个用户
        for movie, rating in pred[user].items():    #对该用户预测的每一个电影    
            if user in testSet.keys() and movie in testSet[user].keys() :   #如果用户为该电影评过分
                setSum = setSum + 1     #预测准确数量+1
                rSum = rSum + abs(testSet[user][movie]-rating)      #累计预测评分误差
    MAE = rSum / setSum
    return MAE


if __name__ == '__main__':
    
    print u'正在加载数据...'
    trainSet,testSet,u2u = loadData()

    print u'正在计算用户间相似度...'
    userSim = getUserSim(u2u,trainSet)
    
    print u'正在寻找最近邻...'
    for N in (5,10,20,30,40,50,60,70,80,90,100):            #对不同的近邻数
        pred = getRecommendations(N,trainSet,userSim)   #获得推荐
        mae = getMAE(testSet,pred)  #计算MAE
        print u'邻居数为:N= %d 时 预测评分准确度为:MAE=%f'%(N,mae)

    raw_input('按任意键继续...')

结果如下:

基于用户的协同过滤算法的Python实现_第2张图片
基于用户的协同过滤算法的Python实现_第3张图片


你可能感兴趣的:(基于用户的协同过滤算法的Python实现)