需求
现在有用户-商品评分数据(基于用户行为,包括点击、收藏、转发、播放、浏览时间归一化为分数范围0-10 小数点后两位)
使用surprise库进行推荐
介绍
surprise 是一个用于构建和分析推荐系统的 Python 库,它提供了多种算法和工具来评估推荐系统的性能。这个库特别适用于那些想要快速开始构建和测试推荐系统原型的研究人员和开发者。以下是一些关于 surprise 库的基本介绍和常用使用方法。
安装
conda install -c conda-forge scikit-surprise
在 conda 命令中,-c 或 --channel 选项用于指定安装包的来源渠道(channel)。conda-forge 是一个由社区维护的 Conda 渠道,它提供了大量由社区贡献者构建和维护的开源软件包
在 surprise 中,推荐系统主要由以下几个部分组成:
- 数据集(Dataset):包含用户、物品和评分的数据。
- 读者(Reader):用于解析数据集的格式。
- 算法(Algorithm):用于生成推荐的算法,如协同过滤、基于内容的推荐等。
- 预测器(Predictor):用于生成预测评分的对象
在surprise库中,常用算法包括
- 协同过滤算法(物以类聚 人以群分)
基于用户的协同过滤(User-Based Collaborative Filtering):通过找到与目标用户兴趣相似的其他用户,然后根据这些相似用户的评分来预测目标用户对物品的评分。
基于物品的协同过滤(Item-Based Collaborative
Filtering):通过分析用户对不同物品的评分,找到物品之间的相似性,然后根据用户对相似物品的评分来预测用户对未评分物品的评分。- 矩阵分解算法 SVD(奇异值分解):通过分解用户-物品评分矩阵来发现潜在的用户和物品特征,然后利用这些特征来预测评分。 SVD++:SVD的扩展,考虑了隐式反馈(如浏览、点击等行为)对评分预测的影响。
PMF(概率矩阵分解):通过概率模型来分解评分矩阵,考虑了评分的随机性和不确定性。
NMF(非负矩阵分解):与SVD类似,但分解后的矩阵元素都是非负的,适用于某些特定场景。- 基于内容的推荐算法
注意:surprise库主要侧重于协同过滤和矩阵分解等算法,但它也支持通过结合物品的内容信息(如描述、标签等)来进行推荐。这通常不是通过surprise库直接提供的算法实现的,而是需要开发者自己结合外部数据源和内容处理技术来完成。- 其他算法
Slope One:一种简单的协同过滤算法,通过计算物品之间的评分差来预测用户对未评分物品的评分。
NormalPredictor:一种基于全局平均评分的简单预测器,不考虑用户或物品的个性化特征。
KNNBasic:基于K最近邻的协同过滤算法,可以配置不同的相似性度量和基线估计方法。
测试
from surprise import Dataset
from surprise import Reader
from surprise import SVD
from surprise.model_selection import train_test_split
from surprise import KNNBasic
# 定义评分范围
reader = Reader(rating_scale=(1, 10))
# 加载数据集 也可以使用其他方法加载数据
data = Dataset.load_from_file('ratings.csv', reader=reader, sep=',', header=True)
# 选择SVD算法
algo = SVD()
# 分割数据集为训练集和测试集
trainset, testset = train_test_split(data, test_size=.25)
# 训练模型
algo.fit(trainset)
# 预测未评分电影的评分
predictions = algo.test(testset)
# 生成推荐
def get_top_n(predictions, n=10):
top_n = {}
for uid, iid, true_r, est, _ in predictions:
if uid not in top_n:
top_n[uid] = []
top_n[uid].append((iid, est))
# 确保每个用户都有n个推荐
if len(top_n[uid]) > n:
top_n[uid] = sorted(top_n[uid], key=lambda x: x[1], reverse=True)[:n]
return top_n
top_n = get_top_n(predictions, n=10)
# 打印推荐结果
for uid, user_ratings in top_n.items():
print(uid, [iid for (iid, _) in user_ratings])
核心代码
trainset, testset = train_test_split(data, test_size=.25)
train_test_split: 这是Scikit-learn库中的一个函数,
用于将数据集随机分割成训练集和测试集。
data: 这是指已经准备好用于训练和测试的完整数据集。
test_size=.25: 这个参数指定了测试集所占的比例。
在这个例子中,25%的数据会被分配给测试集,剩下的75%将被用作训练集
algo.fit(trainset)
这一行代码是在训练模型。具体来说,它是在使用训练集 (trainset) 来训练推荐系统算法 (algo)。
fit 方法通常涉及到模型参数的学习过程,这可能包括损失函数的最小化和梯度下降等优化技术,但这取决于具体的算法实现。
predictions = algo.test(testset)
这一行代码是在使用测试集 (testset) 对模型进行测试,即使用测试集中的数据来生成预测评分。test 方法会返回一个包含模型预测的评分列表。
predictions 是一个列表,其中包含针对测试集中每一条评分记录的预测结果。每个预测结果都是一个元组,通常包含用户ID、项目ID、实际评分、预测评分等信息。
使用这些信息来计算一些常用的评价指标,例如均方根误差(RMSE)或平均绝对误差(MAE)。surprise库提供了方便的方法来计算这些指标。
from surprise import accuracy
# 假设已经有了预测结果
predictions = algo.test(testset)
# 计算RMSE均方根误差
rmse = accuracy.rmse(predictions)
print("Root Mean Squared Error:", rmse)
总结一下,algo.fit(trainset)是在训练模型,而predictions = algo.test(testset)则是在使用测试集生成预测评分。可以使用predictions来比较预测值与实际值,并计算评价指标如RMSE或MAE等。
注意:此处不需要手动迭代
调用algo.fit(trainset)时,该方法会根据所选的算法(例如SVD、KNNBasic等)进行训练,
并且在训练过程中会尝试找到能够最小化某种损失函数(例如均方误差MSE)的最佳参数。
这个过程通常是自动完成的,不需要手动递归调用fit方法。
一旦fit方法完成,模型就被认为是“训练好的”,并且输出的结果就是基于训练数据得到的最佳模型。
如果想改进可以用一下方法
调整超参数:可以通过交叉验证等方法来调整算法的超参数,寻找最佳的超参数组合。
特征工程:可以添加额外的特征或对现有特征进行转换,以提高模型的表现。
集成学习:可以通过组合多个模型的预测结果来创建一个更强大的预测模型。
调整超参数
在surprise库中,可以使用GridSearchCV类来寻找最佳的超参数组合
from surprise import SVD
from surprise.model_selection import GridSearchCV
from surprise import Dataset
from surprise import Reader
# 定义评分范围
reader = Reader(rating_scale=(1, 5))
# 加载数据集
data = Dataset.load_from_df(ratings_df[['uid', 'mediaid', 'score']], reader)
# 定义超参数网格
param_grid = {'n_factors': [50, 100, 200],
'lr_all': [0.002, 0.005],
'reg_all': [0.4, 0.6]}
# 创建GridSearchCV对象
gs = GridSearchCV(SVD, param_grid, measures=['rmse', 'mae'], cv=5)
# 执行交叉验证
gs.fit(data)
# 获取最佳参数
best_params = gs.best_params['mae'] # 也可以使用 'rmse'
print("Best MAE parameters:", best_params)
# 使用最佳参数重新训练模型
algo = SVD(n_factors=best_params['n_factors'],
lr_all=best_params['lr_all'],
reg_all=best_params['reg_all'])
algo.fit(data.build_full_trainset())
在surprise库中,SVD算法的一些关键参数包括n_factors、lr_all和reg_all。这些参数分别代表:
n_factors:表示潜在因子的数量。在矩阵分解中,每个用户和每个项目都会被表示为一个向量,这些向量的维度就是n_factors。较高的n_factors通常可以捕捉到更多的细节,但也可能导致过拟合。
lr_all:表示学习率。这是在使用梯度下降或其他优化算法时,更新模型参数的速度。较高的学习率会导致模型参数更新更快,但可能会跳过最优解;较低的学习率会使模型收敛更慢,但可能找到更精确的解。
reg_all:表示正则化项的强度。正则化是为了防止过拟合而添加的惩罚项。较高的正则化强度可以减少过拟合的风险,但可能会导致模型过于简单,无法很好地拟合数据。
mae:均方绝对误差(Mean Absolute
Error)是一种评价指标,它衡量的是预测值与实际值之间的平均绝对差值。mae越低,说明模型的预测效果越好。
gs.fit(data)中的data是什么? gs.fit(data)中的data是指经过surprise库处理的数据集。
特征工程
假设数据汇总有一些额外的特征,比如用户年龄和性别,也可以将这些特征加入到模型中。首先,需要将这些特征编码到surprise可以理解的形式。
# 数据DataFrame `users_df` 包含用户ID、年龄和性别
# users_df = pd.DataFrame({'uid': [1, 2, 3, ...], 'age': [20, 30, 40, ...], 'gender': ['M', 'F', 'M', ...]})
# 添加用户年龄和性别到评分数据
ratings_df_with_features = ratings_df.merge(users_df, on='uid')
# 使用合并后的数据集训练模型
data_with_features =
Dataset.load_from_df(ratings_df_with_features[['uid', 'mediaid', 'score', 'age', 'gender']], reader)
# 选择一个可以处理额外特征的算法,比如KNNWithZScore
from surprise import KNNWithZScore
algo = KNNWithZScore(user_based=False, sim_options={'user_based': False, 'min_support': 1, 'name': 'cosine'})
algo.fit(data_with_features.build_full_trainset())
数据合并
评分数据 (ratings_df)
uid mediaid score
1 10 4.5
1 11 3.0
2 10 5.0
3 12 2.5
3 13 4.0
用户数据 (users_df)
uid age gender
1 25 M
2 30 F
合并后
uid mediaid score age gender
1 10 4.5 25 M
1 11 3.0 25 M
2 10 5.0 30 F
3 12 2.5 22 M
3 13 4.0 22 M
import pandas as pd
from surprise import Dataset
from surprise import Reader
# 创建评分数据
ratings_df = pd.DataFrame({
'uid': [1, 1, 2, 3, 3],
'mediaid': [10, 11, 10, 12, 13],
'score': [4.5, 3.0, 5.0, 2.5, 4.0]
})
# 创建用户数据
users_df = pd.DataFrame({
'uid': [1, 2, 3],
'age': [25, 30, 22],
'gender': ['M', 'F', 'M']
})
# 合并数据
ratings_df_with_features = ratings_df.merge(users_df, on='uid')
# 显示合并后的数据
print(ratings_df_with_features)
# 定义评分范围
reader = Reader(rating_scale=(1, 5))
# 加载数据集
data_with_features = Dataset.load_from_df(ratings_df_with_features[['uid', 'mediaid', 'score', 'age', 'gender']], reader)
# 打印数据集信息
print(data_with_features.raw_ratings)
混合模型
可以通过组合多个模型的预测结果来创建一个更强大的预测模型。例如,使用SVD和KNNBasic算法的预测结果来创建一个加权平均预测:
from surprise import KNNBasic
# 使用SVD算法
algo_svd = SVD()
algo_svd.fit(trainset)
# 使用KNNBasic算法
algo_knn = KNNBasic()
algo_knn.fit(trainset)
# 预测
predictions_svd = algo_svd.test(testset)
predictions_knn = algo_knn.test(testset)
# 计算加权平均预测
predictions_ensemble = []
for pred_svd, pred_knn in zip(predictions_svd, predictions_knn):
# 假设SVD和KNNBasic的权重相同
ensemble_pred = (pred_svd.est + pred_knn.est) / 2
predictions_ensemble.append((pred_svd.uid, pred_svd.iid, pred_svd.r_ui, ensemble_pred))
# 计算集成模型的RMSE
rmse = accuracy.rmse(predictions_ensemble)
print("Ensemble RMSE:", rmse)
加载数据的其他方法
data_surprise = Dataset.load_from_df(ratings_df[['uid', 'mediaid', 'score']], reader)
这一行代码的作用是从一个Pandas DataFrame (ratings_df) 中加载数据到surprise的数据集对象中。
这里的数据帧应该包含至少三列:用户ID (uid)、项目ID (mediaid) 和评分 (score)。
ratings_df[[‘uid’, ‘mediaid’, ‘score’]]:
这是一个DataFrame切片操作,选择出用户ID、项目ID和评分这三列作为输入数据。
reader: 这是之前创建的Reader对象,它指定了评分数据的格式,比如评分的范围等。
结果是将DataFrame转换成了surprise可以理解的数据集格式。