推荐算法的Python实现——ItemCF(基于物品的协同过滤)

Posted 白水baishui

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了推荐算法的Python实现——ItemCF(基于物品的协同过滤)相关的知识,希望对你有一定的参考价值。

1. 数据集

本博客用Movielens-1m数据集的ratings.dat作为推荐数据来训练ItemCF推荐模型。第一列是用户id(user_id)、第二列是物品id(item_id)、第三列是用户对物品的评分(score)、第四列是时间戳(timestamp)。

在Movielens-1m的元素数据集中,ratings.dat是用::作为分隔符的。在本次的python实现中,提前将分隔符::替换为了,,文件名ratings.dat改为了ratings.csv

如果嫌麻烦不想改ratings.dat文件的内容,改代码也可以,主要替换以下两行:

userid, itemid, record, _ = line.split(",")
# 替换为
userid, itemid, record, _ = line.split("::")
ibcf = ItemBasedCF('ratings.csv')
# 替换为
ibcf = ItemBasedCF('ratings.dat')

2. 代码

import math

class ItemBasedCF:
    def __init__(self, datafile):
        self.datafile = datafile
        self.data = []
        self.trainData = {}
        self.itemSimMatrix = []

    def readData(self):
        """
        在Movielens数据集中读取数据
        """
        datalist = []
        for line in open(self.datafile):
            userid, itemid, record, _ = line.split(",") # 用逗号分割
            datalist.append((int(userid), int(itemid), int(record)))
        self.data = datalist

    def preprocessData(self):
        """
        把读入的数据转换为训练UCF模型需要的格式
        """
        traindata_list = {}
        for user, item, record in self.data:
            traindata_list.setdefault(user, {})
            traindata_list[user][item] = record
        self.trainData = traindata_list

    def itemSimilarity(self):
        """
        生成用户相似度矩阵
        """
        # 物品相似度矩阵
        self.itemSimMatrix = dict()
        # 物品-物品矩阵 格式:物品ID1:{物品ID2:同时给两件物品评分的人数}
        item_item_matrix = dict()
        # 物品-用户矩阵 格式:物品ID:给物品评分的人数
        item_user_matrix = dict()

        for user, items in self.trainData.items():
            for itemId, source in items.items():
                item_user_matrix.setdefault(itemId, 0) # 初始化item_user_matrix[itemId]
                item_user_matrix[itemId] += 1 # 给物品打分的人数+1
                item_item_matrix.setdefault(itemId, {})
                for i in items.keys():
                    if i == itemId:
                        continue
                    item_item_matrix[itemId].setdefault(i, 0)
                    # 计算同时给两个物品打分的人数,并对活跃用户进行惩罚
                    item_item_matrix[itemId][i] += 1 / math.log(1 + len(items) * 1.0)
        # 将物品-物品矩阵转换为物品相似度矩阵
        for itemId, relatedItems in item_item_matrix.items():
            self.itemSimMatrix.setdefault(itemId, dict()) # 初始化self.itemSimMatrix[itemId]
            for relatedItemId, count in relatedItems.items():
                self.itemSimMatrix[itemId][relatedItemId] = count / math.sqrt(item_user_matrix[itemId] * item_user_matrix[relatedItemId])
            # 归一化
            sim_max = max(self.itemSimMatrix[itemId].values())
            for item in self.itemSimMatrix[itemId].keys():
                self.itemSimMatrix[itemId][item] /= sim_max

    def recommend(self, user_id, k, N):
        """给用户推荐物品列表
            Args:
                userId:用户ID
                k:取和物品j最相似的K个物品
                N:推荐N个物品
            Return:
                推荐列表
            """
        rank = dict()
        interacted_items = self.trainData.get(user_id, {}) # 当前用户已经交互过的item
        # 遍历用户评分的物品列表
        for itemId, score in interacted_items.items(): # 取出每一个当前用户交互过的item
            # 取出与物品itemId最相似的K个物品及其评分
            for i, sim_ij in sorted(self.itemSimMatrix[itemId].items(), key=lambda x: x[1], reverse=True)[0:k]:
                # 如果这个物品j已经被用户评分了,舍弃
                if i in interacted_items.keys():
                    continue
                # 对物品ItemID的评分*物品itemId与j的相似度 之和
                rank.setdefault(i, 0)
                rank[i] += score * sim_ij
        # 堆排序,推荐权重前N的物品
        return dict(sorted(rank.items(), key=lambda x:x[1], reverse=True)[0:N])


if __name__ == "__main__":
    ibcf = ItemBasedCF('ratings.csv')
    ibcf.readData() # 读取数据
    ibcf.preprocessData() # 预处理数据
    ibcf.itemSimilarity() # 计算物品相似度矩阵

    # ------ 为用户 i 产生推荐 ------ #
    i = 1
    topN = ibcf.recommend(i, k=3, N=10)  # 输出格式item的id和评分
    topN_list = list(topN.keys())  # 只取对应的item的id

    print("------ i ------")
    print(i)
    print("------ topN_list ------")
    print(topN_list)

    # ------ 为全部用户产生推荐 ------ #
    # topN_list = {} # 存储为每一个用户推荐的列表
    # for each_user in ibcf.trainData:
    #     topN = ibcf.recommend(each_user, k=3, N=10) # item的id和评分
    #     topN_list[each_user] = list(topN.keys()) # 只取对应的item的id
    #
    #     print("------ topN ------")
    #     print(topN)
    #     print("------ topN_list[each_user] ------")
    #     print(topN_list[each_user])
    # print("------ topN_list ------")
    # print(topN_list)

对用户1(user_id=1)产生一次推荐的输出结果:

------ i ------
1
------ topN_list ------
[318, 1196, 364, 2858, 2096, 1302, 593, 1265, 1198, 1704]

以上是关于推荐算法的Python实现——ItemCF(基于物品的协同过滤)的主要内容,如果未能解决你的问题,请参考以下文章

推荐系统系列:不到百行代码实现基于Spark的ItemCF计算

基于上下文的推荐 -- 包括时间衰减算法和位置推荐算法(代码实现)

基于上下文的推荐 -- 包括时间衰减算法和位置推荐算法(代码实现)

推荐系统手写ItemCF/UserCF代码,你会吗?

ItemCF_基于物品的协同过滤_MapReduceJava代码实现思路

07-02 基于协同过滤的推荐算法