完整代码
def get_similar_users_recommendations(uid, dataset=dataset,n=10): # 获取训练集,这里取数据集全部数据 trainset = dataset.build_full_trainset() # 考虑基线评级的协同过滤算法(使用基于用户的算法) algo = KNNBaseline(sim_option = user_based_sim_option) # 拟合训练集 algo.fit(trainset) # 将原始id转换为内部id try: inner_id = algo.trainset.to_inner_uid(uid) except: return algo,set() # 使用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[(data['用户ID']==user)&(data['产品评分']==5)]["产品名称"] for i in item.values: recommendations.add(i) print(f'\n推荐用户的ID是"{uid}":') # print(recommendations) for i, j in enumerate(list(recommendations)): # print(i) if i >= 10: break print(j) return algo,recommendations algo,recom = get_similar_users_recommendations(2019443818,dataset, 10)
为什么要推荐这些,首先是这些产品是基于用户的相似性得到的一个大的产品范围,然后我们再用surprise库进行训练得到10个推荐产品,这些产品我们是可以预测它的评分的。
预测推荐评分
for item in recom: algo.predict(2019443818,item,r_ui=5,verbose=True)
从上面我们得知,最好的一个推荐产品已经产生了,首先它是评分为5的产品,其次预测出的评分也是十分接近的!但是作为一个推荐系统,我们需要的是,我虽然很喜欢这个产品,但是第二次消费的时候,总不能总是推荐我上一次消费的产品吧,这样就容易让用户产生无趣感!
所以我们可以去给我们的推荐系统增加一个筛选功能,当用户进入推荐系统,可以自定义选择是否要延续第一次的消费产品,如果不需要,那么就需要将这类产品在推荐目录下去除!
这个时候,我们又发现一个产品,推荐分和本身相似用户的评分就不错的!
此外我们还可以对预测评分进行一个排序,便于用户在选择的时候,不仅可以参考名称也可以参考预测评分!
for group_label,group_df in alldata.groupby("产品分类"): data = group_df[["用户ID","产品名称","产品评分"]] reader = Reader(line_format="user item rating") dataset = Dataset.load_from_df(data,reader=reader) print(group_label.center(50,'#')) get_similar_users_recommendations(2019443818,dataset, 10)
增加预测分排序功能
d={} users=[] ranks=[] ests=[] items=[] for item in recom: user=algo.predict(2019443818,item,r_ui=5,verbose=True).uid rank=algo.predict(2019443818,item,r_ui=5,verbose=True).r_ui items.append(item) est=algo.predict(2019443818,item,r_ui=5,verbose=True).est users.append(user) ranks.append(rank) ests.append(est) d={"用户ID":users,"推荐产品名称":items,"产品评分":ranks,"预测分":ests} df=pd.DataFrame(d) df.sort_values(by=['预测分'],ascending=False)
基于surprise系统进行推荐,采用基于用户的算法计算用户相似度,寻找最相似的10个用户集群,通过他们的产品评分得到一个最佳的产品推荐数据集,利用KNN进行预测产品推荐分,然后在按照预测分进行排序,最后我们就可以按照其推荐结果选择最佳的产品进行消费了!!
可能细心的伙伴会发现,为什么没有景区的推荐,因为在原始的数据里面就没有对景区发生行为,所以就没有推荐,这就是surprise模块的特点,代码量也很少,但是就可以完成推荐系统的实现!
当然利用surprise做推荐系统比原生算法代码进行推荐的好处是,surprise有多种算法的选择,其次还可以进行优化参数来达到我们模型的最佳效果。
附加知识
surprise可使用的模型
music_data=dataset ### 使用NormalPredictor算法,基于训练集的评分矩阵来预测空白评分的。 from surprise.model_selection import cross_validate,train_test_split algo = NormalPredictor() perf = cross_validate(algo, music_data, measures=['RMSE', 'MAE'],verbose=True) print(perf) ### 使用BaselineOnly from surprise import BaselineOnly algo = BaselineOnly() perf = cross_validate(algo, music_data, measures=['RMSE', 'MAE'],verbose=True) print(perf) ### 使用基础版协同过滤 from surprise import KNNBasic algo = KNNBasic() perf = cross_validate(algo, music_data, measures=['RMSE', 'MAE'],verbose=True) print(perf) ### 使用均值协同过滤 from surprise import KNNWithMeans algo = KNNWithMeans() perf = cross_validate(algo, music_data, measures=['RMSE', 'MAE'],verbose=True) print(perf) ### 使用协同过滤baseline from surprise import KNNBaseline algo = KNNBaseline() perf = cross_validate(algo, music_data, measures=['RMSE', 'MAE'],verbose=True) print(perf) ### 使用SVD from surprise import SVD algo = SVD() perf = cross_validate(algo, music_data, measures=['RMSE', 'MAE'],verbose=True) print(perf) ### 使用SVD++ from surprise import SVDpp algo = SVDpp() perf = cross_validate(algo, music_data, measures=['RMSE', 'MAE'],verbose=True) print(perf) ### 使用NMF from surprise import NMF algo = NMF() perf = cross_validate(algo, music_data, measures=['RMSE', 'MAE'],verbose=True) print(perf)
surprise如何优化参数(网格调参)
类似于我们的机器学习,sklearn中的网格搜索(暴力搜索),寻找最佳参数!
# 度量准则:pearson距离,协同过滤:基于item sim_options = {'name': 'pearson_baseline', 'user_based': False}
在寻找最佳参数的时候,我们需要定义最佳参数的范围应该是属于哪一个范围,其次在利用网格搜索进行最佳参数的搜索,那么涉及到你应该查询文献知识或者去官网查看最佳参数的范围!
from surprise.model_selection import GridSearchCV # 定义好需要优选的参数网格 param_grid = {'n_epochs': np.arange(4,10,1), 'lr_all': np.arange(0.002,0.01,0.001), 'reg_all': np.arange(0.4,0.8,0.1)} # 使用网格搜索交叉验证 grid_search = GridSearchCV(SVD, param_grid, measures=['RMSE', 'MAE']) # 在数据集上找到最好的参数 grid_search.fit(dataset) # 输出调优的参数组 # 输出最好的RMSE结果 # 输出最好的RMSE结果 print(grid_search.best_score['rmse']) # 输出对应最好的RMSE结果的参数 print(grid_search.best_params['rmse']) # 输出最好的MAE结果 print(grid_search.best_score['mae']) # 输出对应最好的MAE结果的参数 print(grid_search.best_params['mae'])
基于物品的surprise案例
根据一个item取回相似度最高的item,主要是用到algo.get_neighbors()这个函数
import io import os from surprise import Dataset from surprise import KNNBaseline def read_item_names(): """ 获取电影名到电影id 和 电影id到电影名的映射 构建映射字典 :return: """ filename = (os.path.expanduser('~/.surprise_data/ml-100k/ml-100k/u.item')) rid_to_name = {} name_to_rid = {} with io.open(filename, 'r', encoding='ISO-8859-1') as f: for line in f: line = line.split('|') rid_to_name[line[0]] = line[1] name_to_rid[line[1]] = line[0] return rid_to_name, name_to_rid # 首先,用算法计算相互间的相似度 data = Dataset.load_builtin('ml-100k') # 使用协同过滤必须有这行,将我们的算法运用于整个数据集,而不进行交叉验证,构建了新的矩阵 trainset = data.build_full_trainset() # 度量准则:pearson距离,协同过滤:基于item sim_options = {'name': 'pearson_baseline', 'user_based': False} algo = KNNBaseline(sim_options=sim_options) algo.fit(trainset=trainset) rid_to_name, name_to_rid = read_item_names() # 拿出来Toy Story这部电影对应的item id toy_story_raw_id = name_to_rid['Toy Story (1995)'] # 转换为内部id toy_story_inner_id = algo.trainset.to_inner_iid(toy_story_raw_id) # 根据内部id找到最近的10个邻居 toy_story_neighbors = algo.get_neighbors(toy_story_inner_id, k=10) # 将10个邻居的内部id转换为item id也就是raw toy_story_neighbors_rids = (algo.trainset.to_raw_iid(inner_id) for inner_id in toy_story_neighbors) # 将10个邻居的item id 转为name toy_story_neighbors_names = (rid_to_name[raw_id] for raw_id in toy_story_neighbors_rids) # 打印结果 print('----------The 10 nearest neighbors of Toy Story---------------') for movie in toy_story_neighbors_names: print(movie)
总结
在使用surprise模块进行推荐系统的实现的时候,大致的思路就是,导入数据,转换数据,选择模型,至于如何选择模型,在初期的时候我们利用多种模型进行测试,其次确定好模型之后,我们需要对参数进行调优,如何选择参数,我们需要继续网格搜索,然后确定好最佳的参数之后,需要确定我们是基于物品还是基于用户的推荐,一些相似度度量应该选择那些,最终我们通过评估模型来验证我们的数据集,然后进行推荐!