【推荐系统】协同过滤之基于用户的最近邻推荐

标签: 推荐系统 协同过滤 用户 | 发表时间:2014-05-24 00:22 | 作者:lifehack
出处:http://blog.csdn.net

1.算法简介


协同过滤(collaborative filtering)的核心思想:利用其他用户的行为来预测当前用户。协同过滤算法是推荐系统中最基本的,同时在业界广为使用。根据使用的方法不同,可以分为基于用户(user-based)、基于物品(item-based)的最近邻推荐。


基于用户的最近邻推荐的主要思想与 kNN有点相似:对于一个给定的评分集,找出与当前用户u口味相近的k个用户;然后,对于用户u没有见过的物品p,利用k个近邻对p进行预测评分。由此引出了两个问题,一是如何度量用户与用户间的相似性(涉及到相似性度量),二是如何进行预测。


相似性度量


常见的用于推荐的相似性度量:Pearson相关系数、余弦相似度、改进的余弦相似度。pearson相关系数用来描诉两组向量一同变化的趋势,取值从+1(强正相关)到-1(强负相关)。用户a和用户b的相似度


其中, 表示用户a对物品p的评分, 表示用户a的平均评分。Pearson相关系数越接近于1,则说明用户a、b越相似。但Pearson相关系数存在着下列缺陷:

  • 未考虑重叠物品项的数量对相似度的影响

比如:①用户a与用户b有2个重叠项;②用户a与用户c有10个重叠项,且Pearson相关系数小于①。但这并不能说明用户b比用户c更相似于用户b,因为用户的口味(或者说是偏好)是通过一定数量的物品评分反映出来的。过少的重叠项不能用于计算相似度。

  • 如果只有一个重叠项,或重叠项的评分相等,则无法计算Pearson相关系数

在此种情况下,方差为0;由于Pearson相关系数的分母为方差相乘,即为0,0除0无法计算。


余弦相似度定义为向量之间夹角的余弦

基本的余弦方法没有考虑用户的平均评分。针对这个问题,改进余弦相似度


取值在-1到+1之间,与Pearson相关系数一样。


预测评分


先选取k个用户的近邻,利用这k个近邻的评分数据来做预测。对物品p,具体做法是对近邻中给物品p的评分做加权平均;预测用户a对物品p的评分:


其中,N为近邻的集合。


2. 实战


本文采用GoupLens研究组的电影评分 MovieLens数据集,MovieLens数据集共有3种,本文选的是ml-100k。


为了便于统计哪些用户对同一部电影进行了评分,我们建立电影-用户的倒排表:输入电影id,输出给过评分的用户。扫描倒排表,即可得到用户间的交互行为。


读取文件,建立词典

import scipy.spatial.distance as ssd
from texttable import Texttable

def getMovieList(item_file):
    """读取u.item文件,获取电影列表: movies[mid]=mtitle
    """

    movies = dict()
    f = open(item_file)
    for raw_item in f.readlines():
        item = raw_item.split('|')
        movies[item[0]] = item[1]
    return movies

def getRatingsInfo(data_file):
    """读取u.data文件,获取三个词典
       用户-电影的评价信息: ratings[uid][mid]=(rating)
       用户的平均评分: avgrating[mid]=average_rating
       电影-用户的倒排表: movieTouser[mid]=(uid)
    """

    ratings = dict()
    avgratings = dict()
    movieTouser = dict()
    f=open(data_file)
    for raw_data in f.readlines():
        data = raw_data.split('\t')
        #用户-电影词典
        if data[0] not in ratings:
            ratings[data[0]] = {}
        ratings[data[0]][data[1]] = int (data[2])
        
        #电影-用户的倒排表
        if data[1] not in movieTouser:
            movieTouser[data[1]] = []
        movieTouser[data[1]].append(data[0])
    
    #用户的平均评分
    for u, movies in ratings.items():
        avgratings[u] = 0
        for mid, rating in movies.items():
            avgratings[u] += rating
        avgratings[u] /= float (len(ratings[u]))

    return ratings, movieTouser, avgratings

计算用户间的相似度,因为Pearson相关系数容易出现分母为0的情况,这里采用cosine作为相似度度量。考虑到重叠项数量的影响,如果重叠项少于5个,则相似度取接近于0

def calcSimilarity(ratings, u1, u2, args = 5):
    """计算用户u1 u2的相似度"""

    ratings_u1=[]; ratings_u2=[]
    for mid1, rating1 in ratings[u1].items():
        for mid2, rating2 in ratings[u2].items():
            if mid1 == mid2:
                ratings_u1.append(rating1)
                ratings_u2.append(rating2)
    
    if len(ratings_u1) < args:   #如果重叠项少于5个,相似度取接近于0
        return 0.001
    return ssd.cosine(ratings_u1, ratings_u2)

寻找用户u的k近邻。为了避免依次扫描每个用户,只把与用户u发生过交互行为的用户列为可能的近邻(所谓交互行为,即给过同一部电影评分);计算出与用户最相似的k个用户。

def kNearestNeighbor(ratings, movieTouser, user, k):
    """寻找用户user的k个近邻"""

    neighbors = []
    #如果用户neighbor与用户user都看过一部电影
    #那么将用户neighbor看作可能的近邻
    for mid, rating in ratings[user].items():
        for neighbor in movieTouser[mid]:
            if neighbor != user and neighbor not in neighbors:
                neighbors.append(neighbor)

    dis = dict()
    for neighbor in neighbors:
        dis[neighbor] = calcSimilarity(ratings, user, neighbor)
    
    return sorted(dis.items(), key = lambda dic: dic[1], reverse = True)[:k]

协同过滤。用户u对电影p是否感兴趣,需要有一个度量来衡量。本文做了简化处理:如果对电影p的评分高于用户u的平均评分,则说明用户u对电影p感兴趣。

def predictRating(user, mid, sorted_dis, ratings, avgratings):
    """预测用户user对电影mid的评分"""

    pred = avgratings[user]
    numerator = 0.0; denominator = 0.0
    for neighbor, dis in sorted_dis:
        if mid in ratings[neighbor]:
            numerator += dis*(ratings[neighbor][mid] - avgratings[neighbor])
            denominator += dis
    pred += numerator/denominator
    return pred

def userBasedCF(movies, ratings, avgratings, movieTouser, user, k = 20):
    """基于用户的协同过滤"""

    recommendList = []; usedList = []
    sorted_dis = kNearestNeighbor(ratings, movieTouser, user, k)
    for neighbor, dis in sorted_dis:
        for mid, rating in ratings[neighbor].items():
            #剪枝:用户看过的电影,或已经推荐过的电影
            if mid not in ratings[user].items() and mid not in usedList:
                pred = predictRating(user, mid, sorted_dis, ratings, avgratings)
                if pred > avgratings[user]:
                    recommendList.append((mid,round(pred,2)))
                    usedList.append(mid)

    recommendList.sort(key = lambda x:x[1], reverse = True)
    return recommendList


测试与结果可视化。输入用户‘2’,返回预测评分最高的15部电影。

"""测试与结果可视化"""
if __name__ == "__main__":
    #读取数据集,生成字典
    movies = getMovieList("u.item")
    ratings, movieTouser, avgratings = getRatingsInfo("u.data")
    
    recomMovies = userBasedCF(movies, ratings, avgratings, movieTouser, '2')[:15]

    table = Texttable()
    table.set_deco(Texttable.HEADER)
    table.set_cols_dtype(['t',   # text 
                          't'])  # float (decimal)
    table.set_cols_align(["l", "l"])
    rows=[]
    rows.append([u"recommended movie",u"predicted rating"])
    for mid, pred in recomMovies:
        rows.append([movies[mid], pred])
    table.add_rows(rows)
    print table.draw()



代码参考了[2],存在着问题:预测的评分大于5,在数据集中最高评分==5。


3.Referrence


[1] Dietmar Jannach, et al., Recommender systems : an introduction.

[2] ygrx, [推荐算法]基于用户的协同过滤算法.

[3]  Pearson 相关系数--最佳理解及相关应用.



作者:lifehack 发表于2014-5-23 16:22:32 原文链接
阅读:71 评论:0 查看评论

相关 [推荐系统 协同过滤 用户] 推荐:

【推荐系统】协同过滤之基于用户的最近邻推荐

- - CSDN博客互联网推荐文章
协同过滤(collaborative filtering)的核心思想:利用其他用户的行为来预测当前用户. 协同过滤算法是推荐系统中最基本的,同时在业界广为使用. 根据使用的方法不同,可以分为基于用户(user-based)、基于物品(item-based)的最近邻推荐. 基于用户的最近邻推荐的主要思想与 kNN有点相似:对于一个给定的评分集,找出与当前用户u口味相近的k个用户;然后,对于用户u没有见过的物品p,利用k个近邻对p进行预测评分.

协同过滤推荐系统的那些不足点

- - 标点符
类目(种类)推荐杂很多情境下行不通,因为有太多的产品属性,而每个属性(比如价钱,颜色,风格,面料,等等)在不同的时候对于消费者的重要程度都是不一样的. 但是协同过滤推荐系统也不是那么完美无缺,他或多或少的会有那么一些问题~. 协同过滤的精度主要取决于用户数据的多少. 如果一个系统有很多用户的历史数据,他就能更好的对用户的喜欢做出预测.

为豆瓣电影实现User-based协同过滤的推荐系统

- - 鸟窝
协同过滤(Collaborative Filtering),简单来说是利用某兴趣相投、拥有共同经验之群体的喜好来推荐使用者感兴趣的信息,个人透过合作的机制给予信息相当程度的反馈(如评分)并记录下来以达到过滤的目的进而帮助别人筛选信息,反馈不一定局限于特别感兴趣的,特别不感兴趣信息的纪录也相当重要,比如浏览信息,收藏,分享,点击等.

为豆瓣电影实现Item-based协同过滤的推荐系统

- - 鸟窝
前面的两篇文章分别使用Spark mllib ALS实现了Model-based协同过滤推荐系统和使用Mahout实现了User-based的协同过滤推荐系统. 我们再来回顾一下item-base CF算法的特点:. 物品数明显小于用户数的场合,否则物品相似度矩阵计算代价很大. 适合长尾物品丰富,用户个性化需求强的领域.

基于Spark MLlib平台的协同过滤算法---电影推荐系统

- - zzm
又好一阵子没有写文章了,阿弥陀佛...最近项目中要做理财推荐,所以,回过头来回顾一下协同过滤算法在推荐系统中的应用.     说到推荐系统,大家可能立马会想到协同过滤算法. 本文基于Spark MLlib平台实现一个向用户推荐电影的简单应用. 基于模型的协同过滤应用---电影推荐.     一、协同过滤算法概述.

[原]Spark MLlib系列(二):基于协同过滤的电影推荐系统

- -
随着大数据时代的到来,数据当中挖取金子的工作越来越有吸引力. 利用Spark在内存迭代运算、机器学习领域强悍性能的优势,使用spark处理数据挖掘问题就显得很有实际价值. 这篇文章给大家分享一个spark MLlib 的推荐实战例子. 我将会分享怎样用spark MLlib做一个电影评分的推荐系统.

用Hadoop流实现mapreduce版推荐系统基于物品的协同过滤算法

- - CSDN博客推荐文章
       以个性化新闻推荐为例,整个过程分成两个mapreduce阶段,由于hadoop流不支持多个mapreduce过程的自动化,所以所有mapreduce过程命令必须人工一个一个的执行. 1、首先需要将原始数据处理成如下形式的两个文件. 文件一:Item_user_score.txt. 如下图中第一行,物品100655565被用户1634974浏览过,则将分数记为1.

推荐算法之基于用户的协同过滤算法

- - CSDN博客综合推荐文章
协同过滤是推荐算法中最基本的算法,主要分为基于用户的协同过滤算法和基于物品的协同过滤算法. 这篇文章主要介绍基于用户的协同过滤算法,简单来说,要给用户u作推荐,那么只要找出那些和u之前的行为类似的用户,即和u比较像的用户,把他们的行为推荐给用户u即可. 所以基于用户的系统过滤算法包括两个步骤:1)找到和目标用户兴趣相似的用户集合  2)找到这个集合中的用户喜欢的,且目标用户没有听说过的物品推荐给目标用户.

协同过滤算法

- - CSDN博客推荐文章
今天要讲的主要内容是 协同过滤,即Collaborative Filtering,简称 CF.    关于协同过滤的一个最经典的例子就是看电影,有时候不知道哪一部电影是我们喜欢的或者评分比较高的,那.    么通常的做法就是问问周围的朋友,看看最近有什么好的电影推荐. 在问的时候,都习惯于问跟自己口味差不.