python实现基于物品的协同过滤算法

一、算法思想

在上篇文章中简单实现了基于用户的协同过滤算法(userCF),该算法存在一些缺点:

  1. 随着用户数量的增加,计算用户相似度矩阵的时间和空间复杂度增长接近 O ( N 2 ) O(N^2) O(N2)
  2. 相比基于物品的协同过滤算法,基于用户的协同过滤算法可解释性稍弱(很难让用户相信:给用户推荐某个商品,原因是有个和该用户兴趣很相似的用户也买了这个商品。用户可能会有疑问:你凭什么说他和我的兴趣很相似)

基于物品的协同过滤算法(itemCF)的思想是给用户推荐 和用户喜欢的商品相似的商品,这就涉及到如何计算两个商品的相似度了。itemCF 中并不是利用物品本身的内容属性来计算相似度(这是基于内容的推荐),而是通过分析用户行为记录(评分、购买、点击、浏览等行为)来计算两个物品的相似度,同时喜欢物品A和物品B的用户数越多,就认为物品A和物品B越相似。下面介绍该算法的实现过程。

二、实现思路

  1. 数据集
    本实现使用的数据集也是 MovieLens 提供的数据:
    MovieLens数据集
    下载地址:MovieLens数据集
    加载解压后为:
    数据集解压后
    我们使用 ml-latest-small 文件夹下的 ratings.csv 文件:
    ratings.csv
    该数据集中的数据格式为:
    数据集格式

userId : 用户 ID
movieId : 用户看过的电影 ID
rating : 用户对所看电影的评分
timestap : 用户看电影的时间戳

  1. 定义变量

N : 记录电影被多少用户看过,如: N[“1”] = 10 表示有10个用户看过ID 为 “1”的电影;
W : 相似矩阵,存储两个用户的相似度,如:W[“1”][“2”] = 0.66 表示ID 为 “1” 的电影和 ID 为 “2” 的电影相似度为 0.66 ;
train : 用户记录数据集中的数据, 格式为: train= { user : [[item1, rating1], [item2, rating2], …], …… }
k : 使用最相似的 k 个电影
n : 为用户推荐 n 部电影

  1. 加载数据
    从数据集中读出数据,将数据以 { user : [[item1, rating1], [item2, rating2], …],……} 的形式存入 train 中。
  2. 计算物品相似矩阵
    本实现中物品相似度的计算公式为:
    W i j = N ( i ) ⋂ N ( j ) N ( i ) ∗ N ( j ) W_{ij}=\frac{N(i)\bigcap N(j)}{\sqrt{N(i)*N(j)}} Wij=N(i)N(j) N(i)N(j)
    其中 N ( i ) N(i) N(i)表示电影 i 被看过的次数, N ( i ) ⋂ N ( j ) N(i)\bigcap N(j) N(i)N(j)表示同时看过电影 i 和电影 j 的用户数。

具体计算如下(可以结合下面的源代码看):
首先遍历 train 变量 { user : [[item1, rating1], [item2, rating2], …],……} ,取出用户看过的电影列表,使用变量 i 遍历该用户看过的电影 ,对看过该电影的用户数(即变量 N(i) )加一,同时使用变量 j 遍历该用户看过的电影,如果 i 和 j 不同,则 W[i][j] 加一(此时W[i][j]记录的是同时看过电影 i 和电影 j 的用户数)。
遍历 W 矩阵,对 W 中的每一个元素进行如下计算:
W [ i ] [ j ] = W [ i ] [ j ] N ( i ) ∗ N ( j ) W[i][j] = \frac{W[i][j]}{\sqrt{N(i) * N(j)}} W[i][j]=N(i)N(j) W[i][j]
得到的 W 矩阵就是物品之间的相似度矩阵。
5. 推荐
首先获得用户已经看过的电影列表,遍历该电影列表,对于用户看过的电影 i ,找出与电影 i 最相似的前 k 个电影(对 W[i] 按照相似度排序),计算这 k 个电影各自的加权评分(rank):
r a n k j = ∑ i ( 用 户 对 电 影 i 的 评 分 ∗ 电 影 i 和 电 影 j 的 相 似 度 ) rank_{j}=\sum_i(用户对电影 i 的评分 * 电影 i 和电影 j 的相似度) rankj=i(iij)
对rank按照评分倒序排序,取前 n 个推荐给用户即可。

三、源代码

"""
@description: a demo for item_based collaborative filtering
@author: Chengcheng Zhao
@date: 2020-11-11 21:19:00
"""
import random
import operator

class ItemBasedCF:
    def __init__(self):
        self.N = {} # number of item user have interacted
        self.W = {} # similarity matrix to store similarity of item i and item j

        self.train = {}

        # recommend n items from the k most similar to the items user have interacted 
        self.k = 30
        self.n = 10

    
    def get_data(self, file_path):
        """
        @description: load data from file
        @param file_path: path of file
        """
        print('start loading data from ', file_path)
        with open(file_path, "r") as f:
            for i, line in enumerate(f, 0):
                if i != 0:  # remove the first line that is title
                    line = line.strip('\r')
                    user, item, rating, timestamp = line.split(',')
                    self.train.setdefault(user, [])
                    self.train[user].append([item, rating])
        print('loading data successfully')
                

    def similarity(self):
        """
        @description: caculate similarity between item i and item j
        """
        print('start caculating similarity matrix ...')
        for user, item_ratings in self.train.items():
            items = [x[0] for x in item_ratings]    # items that user have interacted
            for i in items:
                self.N.setdefault(i, 0)
                self.N[i] += 1  # number of users who have interacted item i
                for j in items:
                    if i != j:
                        self.W.setdefault(i, {})
                        self.W[i].setdefault(j, 0)
                        self.W[i][j] += 1   # number of users who have interacted item i and item j
        for i, j_cnt in self.W.items():
            for j, cnt in j_cnt.items():
                self.W[i][j] = self.W[i][j] / (self.N[i] * self.N[j]) ** 0.5    # similarity between item i and item j
        print('caculating similarity matrix successfully')
      

    def recommendation(self, user):
        """
        @description: recommend n item for user
        @param user: recommended user
        @return items recommended for user
        """
        print('start recommending items for user whose userId is ', user)
        rank = {}
        watched_items = [x[0] for x in self.train[user]]
        for i in watched_items:
            for j, similarity in sorted(self.W[i].items(), key=operator.itemgetter(1), reverse=True)[0:self.k]:
                if j not in watched_items:
                    rank.setdefault(j, 0.)
                    rank[j] += float(self.train[user][watched_items.index(i)][1]) * similarity  # rating that user rate for item i * similarity between item i and item j
        return sorted(rank.items(), key=operator.itemgetter(1), reverse=True)[0:self.n]


if __name__ == "__main__":
    file_path = "C:\\Users\\DELL\\Desktop\\code\\python\\dataset\\ml-latest-small\\ratings.csv"
    itemBasedCF = ItemBasedCF()
    itemBasedCF.get_data(file_path)
    itemBasedCF.similarity()
    user = random.sample(list(itemBasedCF.train), 1)
    rec = itemBasedCF.recommendation(user[0])
    print('\nitems recommeded for user whose userId is', user[0], ':')
    print(rec)

四、代码运行结果

代码运行结果
源代码地址:基于物品的协同过滤算法
参考资料:《推荐系统实践》—— 项亮

  • 3
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值