系列文章目录
Part1 推荐系统基础
Part2 Movienles介
Part3 协同过滤基础
Part4 协同过滤进阶
前言
本文编写了User-CF代码,通过用户相似度得到电影推荐,编写了Item-CF代码,通过物品相似度得到电影推荐,使用倒排索引实现上述计算。
提示:以下是本篇文章正文内容,下面案例可供参考
一、基于UserCF算法的电影推荐系统
代码解析:
1.数据载入
输入数据为ratings.dat,将文件按行读取,依据::分割,存入data中,以列表形式返回。
# 加载评分数据到data
def loadData(self):
print("加载数据...")
data=[]
for line in open(self.datafile):
userid,itemid,record,_ = line.split("::")
data.append((userid,itemid,int(record)))
return data
2.数据集切分
self.data中存储着loaddata得到的data列表,使用random.randint(0,M) 函数,函数的作用是,返回一个随机整型数,范围从低(包括)到高(不包括),即[0, M),因此最终测试集和训练集的数据量比是1:7。
def splitData(self,k,seed,M=8):
print("训练数据集与测试数据集切分...")
train,test = {},{}
random.seed(seed)
for user,item,record in self.data:
if random.randint(0,M) == k:
test.setdefault(user,{})
test[user][item] = record
else:
train.setdefault(user,{})
train[user][item] = record
return train,test
3.计算用户之间的相似度,惩罚热门商品,采用倒排表优化算法时间复杂度
原理上一篇系列文章已经介绍过,这里得到的相似度矩阵存储进user_sim.json文件保存。 json.dump(userSim, open(‘user_sim.json’, ‘w’))将userSim转换为json格式,以user_sim.json为名称存储于当前目录下。
def UserSimilarityBest(self):
print("开始计算用户之间的相似度 ...")
# 得到每个item被哪些user评价过
item_users = dict()
for u, items in self.trainData.items():
for i in items.keys():
item_users.setdefault(i,set())
if self.trainData[u][i] > 0:
item_users[i].add(u)
# 构建倒排表
count = dict()
user_item_count = dict()
for i, users in item_users.items():
for u in users:
user_item_count.setdefault(u,0)
user_item_count[u] += 1
count.setdefault(u,{})
for v in users:
count[u].setdefault(v, 0)
if u == v:
continue
count[u][v] += 1 / math.log(1+len(users))
# 构建相似度矩阵
userSim = dict()
for u, related_users in count.items():
userSim.setdefault(u,{})
for v, cuv in related_users.items():
if u==v:
continue
userSim[u].setdefault(v, 0.0)
userSim[u][v] = cuv / math.sqrt(user_item_count[u] * user_item_count[v])
json.dump(userSim, open('user_sim.json', 'w'))
return userSim
4.为指定用户进行物品推荐
have_score_items 中存储用户已经评价过的电影。对于指定用户,在上述步骤计算得到的user-sim矩阵中,找到和目标用户相似度最高的k个用户,v表示用户,wuv表示用户之间的相似度,i表示用户评分过的电影,rvi表示电影评分,result中记录的是推荐给目标用户的电影候选集。最后再对result进行倒序排序。
"""
user: 为用户user进行推荐
k: 选取k个近邻用户
nitems: 取nitems个物品
"""
def recommend(self, user, k=8, nitems=40):
result = dict()
have_score_items = self.trainData.get(user, {})
for v, wuv in sorted(self.users_sim[user].items(), key=lambda x: x[1], reverse=True)[0:k]:
for i, rvi in self.trainData[v].items():
if i in have_score_items:
continue
result.setdefault(i, 0)
result[i] += wuv * rvi
return dict(sorted(result.items(), key=lambda x: x[1], reverse=True)[0:nitems])
5.推荐结果准确率计算
上文中介绍到的数据集划分函数还没有使用,在这里将会使用到。precision函数将会对训练集中的每一个用户进行电影推荐,每次推荐,会得到一个rank推荐目录,之后,遍历rank推荐目录,如果在测试集中此用户对rank目录里的电影进行过评价,那么,推荐准确率会相应变大。
"""
k: 近邻用户数
nitems: 推荐的item个数
"""
def precision(self, k=8, nitems=10):
print("开始计算准确率 ...")
hit = 0
precision = 0
for user in self.trainData.keys():
tu = self.testData.get(user, {})
rank = self.recommend(user, k=k, nitems=nitems)
for item, rate in rank.items():
if item in tu:
hit += 1
precision += nitems
return hit / (precision * 1.0)
完整代码如下:
import random
import math
import json
import os
class UserCFRec:
def __init__(self,datafile):
self.datafile = datafile
self.data = self.loadData()
self.trainData,self.testData = self.splitData(3,47) # 训练集与数据集
self.users_sim = self.UserSimilarityBest()
# 加载评分数据到data
def loadData(self):
print("加载数据...")
data=[]
for line in open(self.datafile):
userid,itemid,record,_ = line.split("::")
data.append((userid,itemid,int(record)))
return data
"""
拆分数据集为训练集和测试集
k: 参数
seed: 生成随机数的种子
M: 随机数上限
"""
def splitData(self,k,seed,M=8):
print("训练数据集与测试数据集切分...")
train,test = {},{}
random.seed(seed)
for user,item,record in self.data:
if random.randint(0,M) == k:
test.setdefault(user,{})
test[user][item] = record
else:
train.setdefault(user,{})
train[user][item] = record
return train,test
# 计算用户之间的相似度,采用惩罚热门商品和优化算法复杂度的算法
def UserSimilarityBest(self):
print("开始计算用户之间的相似度 ...")
# 得到每个item被哪些user评价过
item_users = dict()
for u, items in self.trainData.items():
for i in items.keys():
item_users.setdefault(i,set())
if self.trainData[u][i] > 0:
item_users[i].add(u)
# 构建倒排表
count = dict()
user_item_count = dict()
for i, users in item_users.items():
for u in users:
user_item_count.setdefault(u,0)
user_item_count[u] += 1
count.setdefault(u,{})
for v in users:
count[u].setdefault(v, 0)
if u == v:
continue
count[u][v] += 1 / math.log(1+len(users))
# 构建相似度矩阵
userSim = dict()
for u, related_users in count.items():
userSim.setdefault(u,{})
for v, cuv in related_users.items():
if u==v:
continue
userSim[u].setdefault(v, 0.0)
userSim[u][v] = cuv / math.sqrt(user_item_count[u] * user_item_count[v])
json.dump(userSim, open('user_sim.json', 'w'))
return userSim
"""
为用户user进行物品推荐
user: 为用户user进行推荐
k: 选取k个近邻用户
nitems: 取nitems个物品
"""
def recommend(self, user, k=8, nitems=40):
result = dict()
have_score_items = self.trainData.get(user, {})
for v, wuv in sorted(self.users_sim[user].items(), key=lambda x: x[1], reverse=True)[0:k]:
for i, rvi in self.trainData[v].items():
if i in have_score_items:
continue
result.setdefault(i, 0)
result[i] += wuv * rvi
return dict(sorted(result.items(), key=lambda x: x[1], reverse=True)[0:nitems])
"""
计算准确率
k: 近邻用户数
nitems: 推荐的item个数
"""
def precision(self, k=8, nitems=10):
print("开始计算准确率 ...")
hit = 0
precision = 0
for user in self.trainData.keys():
tu = self.testData.get(user, {})
rank = self.recommend(user, k=k, nitems=nitems)
for item, rate in rank.items():
if item in tu:
hit += 1
precision += nitems
return hit / (precision * 1.0)
cf = UserCFRec("../input/movielens1m/ratings.dat")
result = cf.recommend("1")
print("user '1' recommend result is {} ".format(result))
precision = cf.precision()
print("precision is {}".format(precision))
二、基于ItemCF算法的电影推荐系统
和UserCF算法类似,这里就不做赘述。
完整代码如下:
import random
import math
import os
import json
class ItemCFRec:
def __init__(self,datafile,ratio):
# 原始数据路径文件
self.datafile = datafile
# 测试集与训练集的比例
self.ratio = ratio
self.data = self.loadData()
self.trainData,self.testData = self.splitData(3,47)
self.items_sim = self.ItemSimilarityBest()
# 加载评分数据到data
def loadData(self):
print("加载数据...")
data=[]
for line in open(self.datafile):
userid,itemid,record,_ = line.split("::")
data.append((userid,itemid,int(record)))
return data
"""
拆分数据集为训练集和测试集
k: 参数
seed: 生成随机数的种子
M: 随机数上限
"""
def splitData(self,k,seed,M=9):
print("训练数据集与测试数据集切分...")
train,test = {},{}
random.seed(seed)
for user,item,record in self.data:
if random.randint(0,M) == k:
test.setdefault(user,{})
test[user][item] = record
else:
train.setdefault(user,{})
train[user][item] = record
return train,test
# 计算物品之间的相似度
def ItemSimilarityBest(self):
print("开始计算物品之间的相似度")
if os.path.exists("data/item_sim.json"):
print("物品相似度从文件加载 ...")
itemSim = json.load(open("data/item_sim.json", "r"))
else:
itemSim = dict()
item_user_count = dict() # 得到每个物品有多少用户产生过行为
count = dict() # 共现矩阵
for user, item in self.trainData.items():
print("user is {}".format(user))
for i in item.keys():
item_user_count.setdefault(i, 0)
if self.trainData[str(user)][i] > 0.0:
item_user_count[i] += 1
for j in item.keys():
count.setdefault(i, {}).setdefault(j, 0)
if self.trainData[str(user)][i] > 0.0 and self.trainData[str(user)][j] > 0.0 and i != j:
count[i][j] += 1
# 共现矩阵 -> 相似度矩阵
for i, related_items in count.items():
itemSim.setdefault(i, dict())
for j, cuv in related_items.items():
itemSim[i].setdefault(j, 0)
itemSim[i][j] = cuv / math.sqrt(item_user_count[i] * item_user_count[j])
json.dump(itemSim, open('item_sim.json', 'w'))
return itemSim
"""
为用户进行推荐
user: 用户
k: k个临近物品
nitem: 总共返回n个物品
"""
def recommend(self, user, k=8, nitems=40):
result = dict()
u_items = self.trainData.get(user, {})
for i, pi in u_items.items():
for j, wj in sorted(self.items_sim[i].items(), key=lambda x: x[1], reverse=True)[0:k]:
if j in u_items:
continue
result.setdefault(j, 0)
result[j] += pi * wj
return dict(sorted(result.items(), key=lambda x: x[1], reverse=True)[0:nitems])
# 计算准确率
def precision(self, k=8,nitems=10):
print("开始计算准确率 ...")
hit = 0
precision = 0
for user in self.testData.keys():
u_items = self.testData.get(user, {})
result = self.recommend(user, k=k, nitems=nitems)
for item, rate in result.items():
if item in u_items:
hit += 1
precision += nitems
return hit / (precision * 1.0)
ib = ItemCFRec("../input/movielens1m/ratings.dat",[1,9])
print("用户1进行推荐的结果如下:{}".format(ib.recommend("1")))
print("准确率为: {}".format(ib.precision()))
三、其他
1.数据实验平台:
kaggle (比自己电脑的运行速度还是快很多滴)
2.数据集链接:
movielens
movielens数据集介绍
四、参考文章和书籍
《推荐系统开发实践》
搜索与推荐Wiki