推荐算法的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计算
基于上下文的推荐 -- 包括时间衰减算法和位置推荐算法(代码实现)
基于上下文的推荐 -- 包括时间衰减算法和位置推荐算法(代码实现)