美文网首页数据分析程序员读书
提供推荐(集体智慧) —— 使用MovieLens数据为用户推荐

提供推荐(集体智慧) —— 使用MovieLens数据为用户推荐

作者: _简书 | 来源:发表于2016-07-20 19:48 被阅读2400次

    0x00 下载MovieLens数据

    1) 从网站http://grouplens.org/datasets/movielens/下载数据
    2) u.item文件包含两列数据,为电影id和电影名称的对应关系;u.data文件包含四列数据,为用户id,电影id,评价,时间

    2016-07-20 21:25:24 的屏幕截图.png
    3)读入数据
    <code>
    movie_list = {} #id:title
    with open("./u.item") as f:
        for line in f.readlines():
            (mid, title) = line.split('|')[0:2]
            movie_list[mid] = title
    pref_by_people = {}
    with open("./u.data") as f:
        for line in f.readlines():
            (uid, mid, rating) = line.split('\t')[0:3]
            if not uid in pref_by_people.keys():
                pref_by_people[uid] = {}
            pref_by_people[uid][movie_list[mid]] = int(rating)
    

    </code>
    4)数据类型转换 {people:{movie:1}} -->> {movie:{people:1}}
    <code>

    def TransfromPref(pref):
        re_pref = {}
        for k1, v1 in pref.items():
            for k2, v2 in v1.items():
                if not k2 in re_pref.keys():
                    re_pref[k2] = {}
                re_pref[k2][k1] = v2
        return re_pref
    

    pref_by_movie = TransfromPref(pref_by_people)

    0x01 协同过滤两种方式

    协同过滤:常常被用于分辨某位特定顾客可能感兴趣的东西,这些结论来自于对其他相似顾客对哪些产品感兴趣的分析。

    欧几里德距离评价

    1)以人们对事物的评价作为坐标轴,比较两人在坐标空间的距离,来判断人们偏好的相近程度
    2)代码
    <code>

    def edclidean(prefs, person1, person2):
        #找出两人均做出评价的事物
        both_list = [item for item in prefs[person1].keys() if item in prefs[person2].keys()] 
        #计算距离
        dis = math.sqrt(sum([(prefs[person1][item] - perfs[person2][item]) ** 2 for item in both_list])) 
        #以距离的倒数为返回值,两人偏好程度越就近,该值越大,假设不存在完全相同的两人,不考虑dis为0的情况
        return 1/dis 
    

    </code>
    prefs中以python字典嵌套字典的方式存储了每个人对于某时候的评价
    prefs = {person:{item:1...}...},下同

    皮尔逊相关系数评价

    1)考虑到人们对于事物的评价标准不同,即有些人倾向打出更高的分数,而其他人更倾向于打出较低的分数,这是单纯的考虑逻辑空间距离就不能很好的得出相近的偏好程度(例:person1对item1和item2打分分别为6和8,person2对item1和item2打分分别为8和10,两人分数不相同,但两人的评价有相同的趋势,此时也认为两人偏好相同)
    2)皮尔逊相关系数计算

    2016-07-20 20:04:24 的屏幕截图.png

    3)代码
    <code>
    通过每个电影人们的评价计算出电影之间的相关系数,将上例中通过中每个人对电影的评价计算人们之间的相关系数同理

    def Pearson(pref , movie1, movie2):
    #找出对两部电影都评论的人
        people_list = [person for person in pref[movie1].keys() if person in pref[movie2].keys()]
        n = len(people_list)
        if n == 0:
            return 0
        #计算评价和
        sum1 = sum([pref[movie1][person] for person in people_list])
        sum2 = sum([pref[movie2][person] for person in people_list])
        #计算评价平方和
        sumSq1 = sum([pref[movie1][person] ** 2 for person in people_list])
        sumSq2 = sum([pref[movie2][person] ** 2 for person in people_list])
        #计算评价成绩和
        psum = sum([pref[movie1][person] * pref[movie2][person] for person in people_list])
        # 皮尔逊相关系数计算
        num = psum - sum1 * sum2 / n
        den = sqrt((sumSq1 - (sum1 ** 2) / n) * (sumSq2 - (sum2 ** 2) / n))
    
        if den == 0:
            return 0
        return num / den
    

    </code>

    0x02 根据电影推荐相关的电影

    1)通过皮尔逊相关系数计算出每个电影的最相近的电影
    <code>

    def TopMatch(pref, movie, n = 5):
        #计算给电影和每部电影的皮尔逊相关系数
        scores = [(Pearson(pref_by_movie, movie, mov), mov) for mov in pref_by_movie.keys() if mov != movie]
        #根据系数进行排序,并由大到小排序
        scores.sort(key = lambda x:x[0], reverse = True)
        return scores[0:n]
    

    </code>
    2)对每部电影计算最接近电影,得到match_list
    <code>

    def CreateMatchList(pref = pref_by_movie):
        match_list = {}
        for movie in pref.keys():
            match_list[movie] = TopMatch(pref, movie, 5)
        return match_list
    
    match_list = CreateMatchList()
    

    </code>

    0x03 为用户推荐电影

    1)找到我们已经看过的电影和评价,从match_list中找出那些和我们看过的影片相近程度更高的没看过的电影,用相关系数作为权值,计算每部电影的评分的加权平均值。

    2016-07-20 20:41:09 的屏幕截图.png
    例:每一横列出我们看过的电影第一列是我们对它的评分,之后的没两列分别是新电影的相关系数和系数乘评分的值,总计记录的相关系数的和与相关系数与评分的乘机的和,最后一横做除法得到评分的加权平均数,即推荐得分,分数越高的越值得推荐
    2)代码
    <code>
    def get_recommanded_items(pref = pref_by_people, match_list = match_list, user = '1'):
        try:
            user_ratings = pref[user] #找出用户看过的电影与评价
        except KeyError:
            print("no user")
            return 0
        scores = {} #记录加权和
        totalsim = {} #记录评分和
    
        for movie, rating in user_ratings.items(): #遍历当前用户评分电影
            for sim, sim_movie in match_list[movie]: #遍历当前电影相近电影
                if sim_movie in user_ratings.keys(): #如果用户看过该电影,跳出本次循环
                    continue
                 #记录加权和与评分和
                if not sim_movie in scores.keys():
                    scores[sim_movie] = sim * rating
                    totalsim[sim_movie] = sim 
                scores[sim_movie] += sim * rating
                totalsim[sim_movie] += sim
    
        rankings = [(scores[sim_movie]/totalsim[sim_movie], sim_movie) for sim_movie in scores.keys() if totalsim[sim_movie] != 0]
        #排序并取前5
        rankings.sort(key=lambda x:x[0], reverse=True)
        return rankings[0:5]
    

    </code>

    0x04 基于用户进行过滤还是基于物品进行过滤

    1)本文采用了基于物品方式的过滤,即对每样物品计算和它相似的物品。当为某位用户推荐时,我们找到他过去评分靠前的物品,再找出这些物品相似的物品,用相关系数作为权值,计算每个物品的评分的加权平均值
    2)基于用户的推荐即对每位用户求出和他取向相近的人,找出这些人中评分靠前的物品,用相关系数作为权值,计算每个物品的评分的加权平均值
    3)物品间相似度的变化不会向人之间变化那么快,即相关系数计算不用那么频繁,可以事先计算好,不用在每次推荐时都计算
    4)针对大量数据推荐列表时,基于物品的过滤明显比基于用户的过滤快,不过需要额外的存储空间开销。
    5)对于稀疏数据集,基于物品的推荐明显优于基于用户的推荐;对于密集数据集,两者效果类似

    0x05 测试

    2016-07-20 21:11:26 的屏幕截图.png
    输入用户id得到推荐电影的前5位
    第一次推荐由于需要计算match_list,花费较长时间
    代码下载:链接: http://pan.baidu.com/s/1jILdXtk 密码: xyzd

    相关文章

      网友评论

      • dcc7fbfee185:代码链接失效了,能不能重发一下,谢谢

      本文标题:提供推荐(集体智慧) —— 使用MovieLens数据为用户推荐

      本文链接:https://www.haomeiwen.com/subject/tfovjttx.html