zoukankan      html  css  js  c++  java
  • 推荐系统实践—UserCF实现

    参考:https://github.com/Lockvictor/MovieLens-RecSys/blob/master/usercf.py#L169

    数据集

    本文使用了MovieLens中的ml-100k小数据集,数据集的地址为:传送门
    该数据集中包含了943个独立用户对1682部电影做的10000次评分。

    首先看一下数据:

    data = pd.read_csv('u.data', sep='	', names=['user_id', 'item_id', 'rating', 'timestamp'])
    print(data)

    完整代码

      1 import numpy as np
      2 import pandas as pd
      3 import math
      4 from collections import defaultdict
      5 from operator import itemgetter
      6 
      7 np.random.seed(1)
      8 
      9 
     10 class UserCF(object):
     11 
     12     def __init__(self):
     13         self.train_set = {}
     14         self.test_set = {}
     15         self.movie_popularity = {}
     16 
     17         self.tot_movie = 0
     18         self.W = {}   # 相似度矩阵
     19 
     20         self.K = 20   # 最接近的K个用户
     21         self.M = 10   # 推荐电影数
     22 
     23     def split_data(self, data, ratio):
     24         ''' 按ratio的比例分成训练集和测试集 '''
     25         for line in data.itertuples():
     26             user, movie, rating = line[1], line[2], line[3]
     27             if np.random.random() < ratio:
     28                 self.train_set.setdefault(user, {})
     29                 self.train_set[user][movie] = int(rating)
     30             else:
     31                 self.test_set.setdefault(user, {})
     32                 self.test_set[user][movie] = int(rating)
     33         print('数据预处理完成')
     34 
     35     def user_similarity(self):
     36         ''' 计算用户相似度 '''
     37         movie_users = {}
     38         for user, items in self.train_set.items():
     39             for movie in items.keys():
     40                 if movie not in movie_users:
     41                     movie_users[movie] = set()
     42                 movie_users[movie].add(user)
     43                 if movie not in self.movie_popularity:   # 用于后面计算新颖度
     44                     self.movie_popularity[movie] = 0
     45                 self.movie_popularity[movie] += 1
     46         print('倒排表完成')
     47         self.tot_movie = len(movie_users)  # 用于计算覆盖率
     48 
     49         C, N = {}, {}    # C记录u,v之间给相同电影打分的数量, N记录用户打分的电影数量
     50         for movie, users in movie_users.items():
     51             for u in users:
     52                 C.setdefault(u, defaultdict(int))
     53                 N.setdefault(u, 0)
     54                 N[u] += 1
     55                 for v in users:
     56                     if u == v:
     57                         continue
     58                 C[u][v] += 1
     59 
     60         train_user_num = len(self.train_set)  # 训练集用户数
     61         count = 1
     62         for u, related_users in C.items():
     63             print('
    相似度计算进度:{:.2f}%'.format(count * 100 / train_user_num), end='')
     64             count += 1
     65             self.W.setdefault(u, {})
     66             for v, cuv in related_users.items():
     67                 self.W[u][v] = float(cuv) / math.sqrt(N[u] * N[v])
     68         print('
    相似度计算完成')
     69 
     70     def recommend(self, u):
     71         ''' 通过与u最相似的K个用户推荐M部电影 '''
     72         rank = {}
     73         user_movies = self.train_set[u]
     74         for v, similarity in sorted(self.W[u].items(), key=itemgetter(1), reverse=True)[0:self.K]:
     75             for movie, rating in self.train_set[v].items():
     76                 if movie in user_movies:
     77                     continue
     78                 rank.setdefault(movie, 0)
     79                 rank[movie] += similarity * rating
     80         return sorted(rank.items(), key=itemgetter(1), reverse=True)[0:self.M]
     81 
     82     def evaluate(self):
     83         ''' 评测算法 '''
     84         hit = 0
     85         ret = 0
     86         rec_tot = 0
     87         pre_tot = 0
     88         tot_rec_movies = set()  # 推荐电影
     89         for user in self.train_set:
     90             test_movies = self.test_set.get(user, {})
     91             rec_movies = self.recommend(user)
     92             for movie, pui in rec_movies:
     93                 if movie in test_movies.keys():
     94                     hit += 1
     95                 tot_rec_movies.add(movie)
     96                 ret += math.log(1+self.movie_popularity[movie])
     97             pre_tot += self.M
     98             rec_tot += len(test_movies)
     99         precision = hit / (1.0 * pre_tot)
    100         recall = hit / (1.0 * rec_tot)
    101         coverage = len(tot_rec_movies) / (1.0 * self.tot_movie)
    102         ret /= 1.0 * pre_tot
    103         print('precision=%.4f' % precision)
    104         print('recall=%.4f' % recall)
    105         print('coverage=%.4f' % coverage)
    106         print('popularity=%.4f' % ret)
    107 
    108 
    109 if __name__ == '__main__':
    110     data = pd.read_csv('u.data', sep='	', names=['user_id', 'item_id', 'rating', 'timestamp'])
    111     usercf = UserCF()
    112     usercf.split_data(data, 0.7)
    113     usercf.user_similarity()
    114     usercf.evaluate()

    结果

    在不同的K值下运行的结果

    相似度计算的改进

    在现实中,很多人因为电影热门而去看它,此时也许这并不是他的兴趣所在,如果两个人同时看了相同的冷门电影,那么也许他们更有可能有更高的相似度。

    对此,可以适当降低热门电影的加成比例,提高冷门电影的加成比例。

    因此,只需对上述代码做此修改

    C[u][v] += 1 / math.log(1 + len(users))

    再重新进行评测,发现修改后在各项性能上都有所提高。

  • 相关阅读:
    extel操作
    postman 使用post方式提交参数值
    json_encode 转化数组时,中文不转义出现乱码的解决方法
    csnd 不好使,所以我来博客园了
    Nodejs 之Ajax的一个实例(sql单条件查询&并显示在Browser端界面上)
    JS 之JSON
    Nodejs sql模块及模块化思想
    Nodejs之Ajax
    Nodejs 之express框架的应用---简单的登录界面
    Nodejs 入门 12-28
  • 原文地址:https://www.cnblogs.com/zyb993963526/p/12815426.html
Copyright © 2011-2022 走看看