surprise是scikit系列中的一个,简单易用,同时支持多种推荐算法:基础算法、协同过滤算法、矩阵分解(隐语义模型)。
surprise文档: https://surprise.readthedocs.io/en/stable/getting_started.html
数据集:movielens-100k数据集
数据集下载地址:http://files.grouplens.org/datasets/movielens
1.基于邻域的方法(协同过滤)(collaborative filtering): user-based, item-based。
import os, io, collections
import pandas as pd
from surprise import Dataset, KNNBaseline, SVD, accuracy, Reader
from surprise.model_selection import cross_validate, train_test_split
# 协同过滤方法
# 载入movielens-100k数据集,一个经典的公开推荐系统数据集,有选项提示是否下载。
data = Dataset.load_builtin('ml-100k')
# 或载入本地数据集# 数据集路径path to dataset filefile_path = os.path.expanduser('~/.surprise_data/ml-100k/ml-100k/u.data')# 使用Reader指定文本格式,参数line_format指定特征(列名),参数sep指定分隔符reader = Reader(line_format='user item rating timestamp', sep='\t')# 加载数据集data = Dataset.load_from_file(file_path, reader=reader)
data_df = pd.read_csv(file_path, sep='\t', header=None, names=['user','item','rating','timestamp'])
item_df = pd.read_csv(os.path.expanduser('~/.surprise_data/ml-100k/ml-100k/u.item'), sep='|', encoding='ISO-8859-1', header=None, names=['mid','mtitle']+[x for x in range(22)])
# 每列都转换为字符串类型
data_df = data_df.astype(str)
item_df = item_df.astype(str)
# 电影id到电影标题的映射
item_dict = { item_df.loc[x, 'mid']: item_df.loc[x, 'mtitle'] for x in range(len(item_df)) }
1.1基于用户的协同过滤算法:
# 使用协同过滤算法时的相似性度量配置
# user-based
user_based_sim_option = {'name': 'pearson_baseline', 'user_based': True}
# item-based
item_based_sim_option = {'name': 'pearson_baseline', 'user_based': False}
# 为用户推荐n部电影,基于用户的协同过滤算法,先获取10个相似度最高的用户,把这些用户评分高的电影加入推荐列表。
def get_similar_users_recommendations(uid, n=10):
# 获取训练集,这里取数据集全部数据
trainset = data.build_full_trainset()
# 考虑基线评级的协同过滤算法
algo = KNNBaseline(sim_option = user_based_sim_option)
# 拟合训练集
algo.fit(trainset)
# 将原始id转换为内部id
inner_id = algo.trainset.to_inner_uid(uid)
# 使用get_neighbors方法得到10个最相似的用户
neighbors = algo.get_neighbors(inner_id, k=10)
neighbors_uid = ( algo.trainset.to_raw_uid(x) for x in neighbors )
recommendations = set()
#把评分为5的电影加入推荐列表
for user in neighbors_uid:
if len(recommendations) > n:
break
item = data_df[data_df['user']==user]
item = item[item['rating']=='5']['item']
for i in item:
recommendations.add(item_dict[i])
print('\nrecommendations for user %s:')
for i, j in enumerate(list(recommendations)):
if i >= 10:
break
print(j)
测试,给id为1的用户推荐10部电影:
get_similar_users_recommendations('1', 10)
输出结果如下:
1.2基于物品的协同过滤算法:
# 与某电影相似度最高的n部电影,基于物品的协同过滤算法。
def get_similar_items(iid, n = 10):
trainset = data.build_full_trainset()
algo = KNNBaseline(sim_option = item_based_sim_option)
algo.fit(trainset)
inner_id = algo.trainset.to_inner_iid(iid)
# 使用get_neighbors方法得到n个最相似的电影
neighbors = algo.get_neighbors(inner_id, k=n)
neighbors_iid = ( algo.trainset.to_raw_iid(x) for x in neighbors )
recommendations = [ item_dict[x] for x in neighbors_iid ]
print('\nten movies most similar to the %s:' % item_dict[iid])
for i in recommendations:
print(i)
给id为2的电影(GoldenEye (1995))相似度最高的十部电影:
get_similar_items('2')
推荐结果如下:
2.基于隐语义的方法(矩阵分解):SVD。
# SVD算法,预测所有用户的电影的评分,把每个用户评分最高的n部电影加入字典。
def get_recommendations_dict(n = 10):
trainset = data.build_full_trainset()
# 测试集,所有未评分的值
testset = trainset.build_anti_testset()
# 使用SVD算法
algo = SVD()
algo.fit(trainset)
# 预测
predictions = algo.test(testset)
# 均方根误差
print("RMSE: %s" % accuracy.rmse(predictions))
# 字典保存每个用户评分最高的十部电影
user_recommendations = collections.defaultdict(list)
for uid, iid, r_ui, est, details in predictions:
user_recommendations[uid].append((iid, est))
for uid, user_ratings in user_recommendations.items():
user_ratings.sort(key = lambda x: x[1], reverse=True)
user_recommendations[uid] = user_ratings[:n]
return user_recommendations
# 获取每个用户评分最高的10部电影
user_recommendations = get_recommendations_dict(10)
# 显示为用户推荐的电影名
def rec_for_user(uid):
print("recommendations for user %s:" % uid)
#[ item_dict[x[0]] for x in user_recommendations[uid] ]
for i in user_recommendations[uid]:
print(item_dict[i[0]])
给id为1的用户推荐10部电影:
rec_for_user('1')
输出结果如下: