- 机器学习
- 人工智能
【免费下载链接】Surprise
A Python scikit for building and analyzing recommender systems
Surprise 是一个用于构建和分析推荐系统的 Python scikit。本文聚焦其核心文档 prediction_algorithms.rst 所讲解的主题——预测算法的统一基类体系,以及决定算法精度的两大配置模块:Baseline 估计(baseline estimates)与相似度度量(similarity measure)。读完本文,你将掌握如何通过bsl_options与sim_options两个字典参数精确控制 ALS/SGD 基线偏差估计和 cosine/MSD/pearson/pearson_baseline 相似度计算,并能结合源码理解每个配置项的底层作用。
预测算法的统一基类与全局命名空间
Surprise 提供了一批内置预测算法,它们都派生自AlgoBase基类(定义于 surprise/prediction_algorithms/algo_base.py)。基类实现了所有算法共享的关键方法:
fit(trainset):在给定训练集上训练算法,初始化内部结构并保存self.trainset,返回self以支持链式调用(algo.fit(trainset).test(testset))。predict(uid, iid, r_ui=None, clip=True, verbose=False):将原始用户/物品 id 转换为内部 id 后调用子类实现的estimate方法;若预测不可能(用户或物品未知),则回退到default_prediction()(默认返回训练集全局评分均值trainset.global_mean);clip=True时会把估计值裁剪回评分区间[lower_bound, higher_bound]。返回的Prediction对象包含原始 uid、iid、真实评分、估计评分及附加细节。test(testset, verbose=False):对测试集中的全部评分逐一调用predict,返回Prediction列表。compute_baselines():按bsl_options计算用户偏差bu与物品偏差bi(见下文)。compute_similarities():按sim_options构建相似度矩阵(见下文)。get_neighbors(iid, k):返回与指定内部 id 最相似的 k 个近邻,k-NN 算法可直接调用。
全部内置算法都直接暴露在 Surprise 的全局命名空间中,无需深入子包导入,例如:
from surprise import KNNBasic algo = KNNBasic()可用算法的完整清单(见 surprise/prediction_algorithms/init.py)包括:NormalPredictor、BaselineOnly、KNNBasic、KNNWithMeans、KNNWithZScore、KNNBaseline、SVD、SVDpp、NMF、SlopeOne、CoClustering,另有Prediction、PredictionImpossible两个辅助类型。各算法的具体公式与参数可查阅 prediction_algorithms_package.rst 下挂载的 basic_algorithms.rst、knn_inspired.rst、matrix_factorization.rst、slope_one.rst、co_clustering.rst 等页面。
部分算法会用到baseline 估计,部分会用到相似度度量,下面分别讲解如何配置。
Baseline 估计配置(bsl_options)
适用前提
本节配置只适用于尝试最小化如下正则化平方误差(或其等价形式)的算法或相似度度量:
$$\sum_{r_{ui} \in R_{train}} \left(r_{ui} - (\mu + b_u + b_i)\right)^2 + \lambda \left(b_u^2 + b_i^2 \right)$$
其中 $\mu$ 为全局均值,$b_u$、$b_i$ 分别为用户、物品偏差。对于在其他目标函数中使用 baseline 的算法(例如SVD),baseline 的配置方式不同且与具体算法相关,需参阅各自文档。典型适用对象是BaselineOnly算法以及使用pearson_baseline相似度的 k-NN 算法。若不想自定义,直接使用默认参数即可——bsl_options完全可选。
Baseline 可以用两种方式估计:
- 随机梯度下降(SGD)
- 交替最小二乘(ALS)
通过算法构造时传入的bsl_options字典配置,其中键'method'指定方法,可选值为'als'(默认)与'sgd'。两种方法下用户/物品偏差($b_u$、$b_i$)均初始化为零。
ALS 参数
当'method': 'als'时可用以下键:
| 参数 | 含义 | 对应文献记号 | 默认值 |
|---|---|---|---|
reg_i | 物品的正则化参数 | $\lambda_2$ | 10 |
reg_u | 用户的正则化参数 | $\lambda_3$ | 15 |
n_epochs | ALS 过程的迭代轮数(注意 Koren 2010 原文描述的是单轮ALS) | — | 10 |
SGD 参数
当'method': 'sgd'时可用以下键:
| 参数 | 含义 | 对应文献记号 | 默认值 |
|---|---|---|---|
reg | 被优化代价函数的正则化参数 | $\lambda_1$ | 0.02 |
learning_rate | SGD 的学习率 | $\gamma$ | 0.005 |
n_epochs | SGD 过程的迭代轮数 | — | 20 |
完整示例
以下代码来自 examples/baselines_conf.py,先加载 MovieLens-100k 内置数据集,然后分别演示 ALS 与 SGD 配置(可直接运行):
from surprise import BaselineOnly, Dataset, KNNBasic from surprise.model_selection import cross_validate # Load the movielens-100k dataset. data = Dataset.load_builtin("ml-100k") # Example using ALS print("Using ALS") bsl_options = {"method": "als", "n_epochs": 5, "reg_u": 12, "reg_i": 5} algo = BaselineOnly(bsl_options=bsl_options) cross_validate(algo, data, verbose=True) # Example using SGD print("Using SGD") bsl_options = { "method": "sgd", "learning_rate": 0.00005, } algo = BaselineOnly(bsl_options=bsl_options) cross_validate(algo, data, verbose=True)源码级原理:ALS 与 SGD 究竟在做什么
Baseline 的两种优化实现在 surprise/prediction_algorithms/optimize_baselines.pyx(Cython 源码)中:
baseline_als:按'n_epochs'(默认 10)、'reg_u'(默认 15)、'reg_i'(默认 10)读取参数,先固定用户偏差迭代更新物品偏差bi[i] = dev_i / (reg_i + len(ir[i])),再固定物品偏差更新用户偏差bu[u] = dev_u / (reg_u + len(ur[u])),即标准的坐标式交替最小二乘。baseline_sgd:按'n_epochs'(默认 20)、'reg'(默认 0.02)、'learning_rate'(默认 0.005)读取参数,对每个评分样本执行bu[u] += lr * (err - reg * bu[u])、bi[i] += lr * (err - reg * bi[i]),即带正则化项的随机梯度下降。
AlgoBase.compute_baselines内部通过字典{"als": baseline_als, "sgd": baseline_sgd}分发调用,并会检查self.bu是否已计算以避免重复计算——例如pearson_baseline相似度在构建相似矩阵时会复用同一组 baseline。若传入非法'method'值会抛出ValueError,提示可选值为als和sgd。该逻辑可由 tests/test_algorithms.py 中的 sanity check 佐证:BaselineOnly()在 ml-100k 上期望 RMSE 为1.0268524031297395。
相似度度量中复用 Baseline
部分相似度度量(如pearson_baseline)也会使用 baseline 估计。无论 baseline 是否参与最终的评分预测 $\hat{r}_{ui}$,配置方式完全一致,例如:
# Some similarity measures may use baselines. It works just the same. print("Using ALS with pearson_baseline similarity") bsl_options = { "method": "als", "n_epochs": 20, } sim_options = {"name": "pearson_baseline"} algo = KNNBasic(bsl_options=bsl_options, sim_options=sim_options) cross_validate(algo, data, verbose=True)这正是 examples/baselines_conf.py 第三段示例的完整内容。由于KNNBasic通过**kwargs透传bsl_options到AlgoBase,任何使用相似度的 k-NN 算法都可以这样组合配置。
相似度度量配置(sim_options)
许多算法依赖相似度度量来估计评分,其配置方式与 baseline 类似:在算法构造时传入sim_options字典,包含以下(全部可选)键:
| 参数 | 含义 | 默认值 | ||
|---|---|---|---|---|
name | 要使用的相似度名称,定义于 surprise/similarities.pyx 模块 | 'MSD' | ||
user_based | 相似度计算在用户间还是物品间进行(True为用户间,False为物品间)。对预测算法性能影响巨大 | True | ||
min_support | 用户间相似度所需的最少共同物品数(user_based=True时),或物品间相似度所需的最少共同用户数(user_based=False时)。若 $ | I_{uv} | < \text{min_support}$,则 $\text{sim}(u, v) = 0$,物品同理 | 1(由AlgoBase.compute_similarities中get("min_support", 1)给出) |
shrinkage | 收缩参数,仅对pearson_baseline相似度有效 | 100 |
四种内置相似度
surprise/similarities.pyx 提供四种度量(AlgoBase.compute_similarities中的分发字典{"cosine", "msd", "pearson", "pearson_baseline"}确认了合法值):
cosine:余弦相似度,仅统计共同评分项: $$\text{cosine_sim}(u, v) = \frac{\sum_{i \in I_{uv}} r_{ui} \cdot r_{vi}}{\sqrt{\sum_{i \in I_{uv}} r_{ui}^2} \cdot \sqrt{\sum_{i \in I_{uv}} r_{vi}^2}}$$msd(Mean Squared Difference):先算平均平方差 $\text{msd}(u, v) = \frac{1}{|I_{uv}|}\sum_{i \in I_{uv}}(r_{ui} - r_{vi})^2$,再取 $\text{msd_sim} = \frac{1}{\text{msd} + 1}$(+1防止除零)。pearson:皮尔逊相关系数,可视为均值中心化的余弦相似度;无共同评分的对象对相似度为 0(而非 -1)。pearson_baseline:用 baseline 估计($b_{ui} = \mu + b_u + b_i$)代替均值进行中心化的收缩皮尔逊系数: $$\hat{\rho}{uv} = \frac{\sum{i \in I_{uv}}(r_{ui} - b_{ui})(r_{vi} - b_{vi})}{\sqrt{\sum_{i \in I_{uv}}(r_{ui} - b_{ui})^2}\sqrt{\sum_{i \in I_{uv}}(r_{vi} - b_{vi})^2}}$$ 收缩版本为 $\frac{|I_{uv}| - 1}{|I_{uv}| - 1 + \text{shrinkage}} \cdot \hat{\rho}_{uv}$;shrinkage=0即不收缩。实现中还强制min_sprt = max(2, min_sprt),因为支撑数为 1 时皮尔逊系数必然为 0。
所有相似度实现均只统计共同评分项,并在共同数量低于min_support时将该对相似度置 0;相似矩阵为对称矩阵。
使用示例
以下代码来自 examples/similarity_conf.py:
from surprise import Dataset, KNNBasic from surprise.model_selection import cross_validate # Load the movielens-100k dataset. data = Dataset.load_builtin("ml-100k") # Example using cosine similarity sim_options = { "name": "cosine", "user_based": False, # compute similarities between items } algo = KNNBasic(sim_options=sim_options) cross_validate(algo, data, verbose=True) # Example using pearson_baseline similarity sim_options = {"name": "pearson_baseline", "shrinkage": 0} # no shrinkage algo = KNNBasic(sim_options=sim_options) cross_validate(algo, data, verbose=True)第一个示例把相似度切换为物品间(item-item)余弦;第二个示例使用pearson_baseline并显式关闭收缩。
源码级原理:相似矩阵是如何构建的
在 surprise/prediction_algorithms/algo_base.py 的compute_similarities中:
- 根据
user_based选择维度:用户间相似时n_x = n_users且用trainset.ir(每个物品的评分列表)做 y 轴遍历;物品间相似时n_x = n_items且用trainset.ur。 - 读取
min_support(默认 1)与name(默认"msd",大小写不敏感)。 - 仅当
name == "pearson_baseline"时才额外读取shrinkage(默认 100)、调用compute_baselines()获取偏差并按 user/item 方向交换bu/bi传入相似度函数。 - 非法相似度名会抛出
NameError,列出合法值。
k-NN 算法(surprise/prediction_algorithms/knns.py)在fit中调用compute_similarities()生成self.sim矩阵,estimate阶段对目标 y 的评分列表按相似度取前k个邻居(heapq.nlargest)做加权聚合,并统计actual_k;若actual_k < min_k,KNNBasic直接抛PredictionImpossible(预测回退为全局均值),而KNNWithMeans/KNNBaseline/KNNWithZScore则将聚合项置 0、退回各自的均值或 baseline。这也解释了为什么文档强调user_based对算法性能影响巨大——用户间与物品间相似度的维度、邻居来源与聚合公式都完全不同。tests/test_algorithms.py 中的test_nearest_neighbors验证了同一算法在user_based=True与False下get_neighbors(0, k=10)的结果确实不同;而test_sanity_checks给出KNNBasic在四种相似度(cosine/MSD/pearson/pearson_baseline)下 ml-100k 的期望 RMSE,可作为复现基线(如KNNBasic(sim_options={"name": "cosine"})→ 1.1495、MSD → 1.1337、pearson → 1.1219、pearson_baseline → 1.1242)。
调参建议与注意事项
- 默认值即可用:
bsl_options与sim_options均完全可选,不传时使用默认参数(ALS +reg_i=10, reg_u=15, n_epochs=10;MSD 相似度 +user_based=True+min_support=1)。 - ALS vs SGD 的选择:ALS 迭代轮数默认 10、SGD 默认 20;文献中 Koren 的 ALS 是单轮描述,仓库实现做了多轮扩展。二者对
reg/reg_u/reg_i与learning_rate的敏感度不同,建议通过cross_validate在小数据集上先做粗调。 pearson_baseline必须搭配bsl_options:该相似度内部会调用compute_baselines(),因此会额外引入 baseline 计算开销;shrinkage默认 100 用于避免仅有少量共同评分时过拟合,设为 0 即关闭收缩。min_support与冷启动:min_support越大,稀疏数据集上的相似对越多被置 0,可能导致actual_k < min_k而回退到全局均值或 baseline,需与 k-NN 的k、min_k参数配合权衡。- 非法配置会直接报错:baseline 方法名非法抛
ValueError,相似度名非法抛NameError,且错误信息中均列出了合法取值,便于快速修正。
小结
Surprise 把预测算法的配置收敛为两个字典:bsl_options控制 baseline 的估计方式(ALS/SGD 及其正则化、迭代参数),sim_options控制相似度度量的选择(cosine/MSD/pearson/pearson_baseline)、计算方向(user_based)、最小支撑(min_support)与收缩(shrinkage)。所有配置均通过AlgoBase统一注入,与具体算法解耦。掌握这两个字典的每个键及其源码层面的作用,即可在 examples/baselines_conf.py 与 examples/similarity_conf.py 的基础上,针对自己的数据集快速组合出高精度的基线方案与 k-NN 变体。
- 机器学习
- 人工智能
【免费下载链接】Surprise
A Python scikit for building and analyzing recommender systems
相关推荐
Surprise 自定义预测算法开发指南:从 estimate 到 baselines 与相似度的完整实现
Surprise 自定义预测算法开发指南:从 estimate 到 baselines 与相似度的完整实现 Surprise 是一个基于 Python 的推荐系
机器学习人工智能HanLP文本相似度计算:语义匹配与相似度评估完整指南
HanLP文本相似度计算:语义匹配与相似度评估完整指南 在当今信息爆炸的时代,如何从海量文本中快速找到相似内容成为了重要课题。HanLP作为一款强大的中文自然语
人工智能NLP深度学习如何在10分钟内快速搭建传奇游戏服务器:OpenMir2终极完整指南
如何在10分钟内快速搭建传奇游戏服务器:OpenMir2终极完整指南 想象一下,你只需要10分钟就能拥有一个完全属于自己的传奇游戏服务器,重温1.76经典版本的
游戏开发后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考