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

一、算法思想

基于用户的协同过滤算法的思想是有相似兴趣的用户(user)可能会喜欢相同的物品(item)。因此,计算用户的相似度成为该算法的关键步骤。本文实现过程中使用的相似度公式如下:
ω u v = N ( u ) ⋂ N ( v ) N ( u ) ∗ N ( v ) \omega_{uv}=\frac{N(u)\bigcap N(v)}{\sqrt{N(u)*N(v)}} ωuv=N(u)N(v) N(u)N(v)
其中 N(u) 表示用户 u 看过的电影个数。

二、实现思路

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

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

  1. 定义变量

N : 记录用户看过的电影数量,如: N[“1”] = 10 表示用户 ID 为 “1” 的用户看过 10 部电影;
W : 相似矩阵,存储两个用户的相似度,如:W[“1”][“2”] = 0.66 表示用户 ID 为 “1” 的用户和用户 ID 为 “2” 的用户相似度为 0.66 ;
train : 用户记录数据集中的数据, 格式为: train= { user : [[item1, rating1], [item2, rating2], …], …… }
item_users : 将数据集中的数据转换为 物品_用户 的倒排表,这样做的原因是在计算用户相似度的时候,可以只计算看过相同电影的用户之间的相似度(没看过相同电影的用户相似度默认为 0 ),倒排表的形式为: item_users = { item : [user1, user2, …], ……}
k : 使用最相似的 k 个用户作推荐
n : 为用户推荐 n 部电影

  1. 加载数据
    从数据集中读出数据,将数据以 { user : [[item1, rating1], [item2, rating2], …],……} 的形式存入 train 中,并整理成形如 { item : [user1, user2, …], ……} 的倒排表,存入 item_users 中。
  2. 计算相似度矩阵
    遍历 item_users ,统计每个用户看过的电影 item 的次数,同时遍历 item_users 中的 users ,先计算用户 u 和用户 v 看过相同电影的个数存在 W 中, 然后遍历 W ,使用上述公式计算用户 u 和用户 v 的相似度。
  3. 推荐
    最后为用户 u 推荐,首先从 item_users 中获得用户 u 已经看过的电影,然后将用户按相似度排序,并取前 k 个用户,对这 k 个用户看过但用户 u 没有看过的电影计算加权评分和,计算公式为:用户相似度 * 用户对电影的评分。

三、源代码

"""
@description: a demo for user-based collaborative filtering
@author: Chengcheng Zhao
@date: 2020-11-15 15:21:13
"""
import random
import operator


class UserBasedCF:
    def __init__(self):
        self.N = {}  # number of items user interacted, N[u] = the number of items user u interacted
        self.W = {}  # similarity of user u and user v

        self.train = {} # train = { user : [[item1, rating1], [item2, rating2], …], …… }
        self.item_users = {}    # item_users = { item : [user1, user2, …], …… }

        # recommend n items from the k most similar users
        self.k = 30
        self.n = 10


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

                    self.item_users.setdefault(item, [])
                    self.item_users[item].append(user) 


    def similarity(self):
        """
        @description: calculate similarity between user u and user v
        """
        for item, users in self.item_users.items():
            for u in users:
                self.N.setdefault(u, 0)
                self.N[u] += 1
                for v in users:
                    if u != v:
                        self.W.setdefault(u, {})
                        self.W[u].setdefault(v, 0)
                        self.W[u][v] += 1   # number of items which both user u and user v have interacted
        for u, user_cnts in self.W.items():
            for v, cnt in user_cnts.items():
                self.W[u][v] = self.W[u][v] / (self.N[u] * self.N[v]) ** 0.5    # similarity between user u and user v
 

    def recommendation(self, user):
        """
        @description: recommend items for user
        @param user : the user who is recommended, we call this user u
        @return : items recommended for user u
        """
        watched = [i[0] for i in self.train[user]]  # items that user have interacted
        rank = {}
        for v, similar in sorted(self.W[user].items(), key=operator.itemgetter(1), reverse=True)[0:self.k]: # order user v by similarity between user v and user u
            for item_rating in self.train[v]:   # items user v have interacted
                if item_rating[0] not in watched:   # item user hvae not interacted
                    rank.setdefault(item_rating[0], 0.)
                    rank[item_rating[0]] += similar * float(item_rating[1])
        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"
    userBasedCF = UserBasedCF()
    userBasedCF.get_data(file_path)
    userBasedCF.similarity()
    user = random.sample(list(userBasedCF.train), 1)
    rec = userBasedCF.recommendation(user[0])
    print(rec)


四、代码运行结果

程序随机选择一位用户,根据和他最相似的 30 个用户,为其推荐 10 部电影,打印出推荐电影的 ID 和加权评分 [[电影ID, 加权评分],[电影ID,加权评分],……]。
代码运行结果
源代码地址:基于用户的协同过滤算法的 python 实现

参考资料:《推荐系统实践》——项亮

  • 1
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,基于用户协同过滤算法可以分为以下几个步骤: 1. 数据处理,将用户和物品转化为数字标识,如1、2、3、4等。 2. 构建用户-物品评分矩阵,即将用户对物品的评分存储到一个矩阵中。 3. 计算用户之间的相似度,可以使用余弦相似度或皮尔逊相关系数等。 4. 预测用户对未评分物品的评分,计算方法为:对于用户u和物品i,找出与u最相似的k个用户,计算这k个用户对i的评分的加权平均值,权重为相似度。 5. 推荐物品,根据预测的评分值对用户推荐物品。 下面是一个简单的基于用户协同过滤算法实现: ```python import numpy as np # 原始评分矩阵 R = np.array([ [5, 3, 0, 1], [4, 0, 0, 1], [1, 1, 0, 5], [1, 0, 0, 4], [0, 1, 5, 4], ]) # 用户相似度矩阵 sim_matrix = np.zeros((R.shape[0], R.shape[0])) for i in range(R.shape[0]): for j in range(i+1, R.shape[0]): # 计算余弦相似度 sim_matrix[i, j] = np.dot(R[i], R[j]) / (np.linalg.norm(R[i]) * np.linalg.norm(R[j])) sim_matrix[j, i] = sim_matrix[i, j] # 预测用户对未评分物品的评分 def predict(u, i, k=3): # 找出与u最相似的k个用户 sim_users = np.argsort(-sim_matrix[u])[:k] # 计算加权平均值 rating = np.dot(sim_matrix[u][sim_users], R[sim_users, i]) / np.sum(sim_matrix[u][sim_users]) return rating # 推荐物品 def recommend(u, k=3): # 找出用户u未评分的物品 unrated_items = np.where(R[u] == 0)[0] # 预测用户对未评分物品的评分 ratings = [predict(u, i, k) for i in unrated_items] # 对预测评分排序,推荐评分最高的物品 recommended_item = unrated_items[np.argmax(ratings)] return recommended_item # 测试推荐结果 print(recommend(0, 3)) ``` 这里的数据是一个5x4的评分矩阵,其中5个用户对4个物品进行评分。首先计算用户之间的相似度,然后对于每个用户,预测他对未评分物品的评分,最后推荐评分最高的物品。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值