ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Surprise Trainset 类深度解析:从原始评分数据到推荐算法训练集的完整指南

Surprise Trainset 类深度解析:从原始评分数据到推荐算法训练集的完整指南 机器学习人工智能【免费下载链接】SurpriseA Python scikit for building and analyzing recommender systems项目地址https://gitcode.com/gh_mirrors/su/Surprise点击查看免费下载导读在 Surprise 推荐系统库中Trainset是连接原始评分数据与预测算法之间的核心枢纽每一个预测算法如 SVD、KNN、BaselineOnly的fit()方法接收的都是Trainset对象而不是原始的Dataset。本文以 surprise/trainset.py 的实现为主线系统讲解 Trainset 的数据结构、raw/inner id 双重映射机制、全部成员方法与属性并结合 surprise/dataset.py 的构造流程、surprise/model_selection/split.py 的交叉验证切分以及 tests/test_dataset.py 的测试用例给出可直接运行的实战代码。读完本文你将能够独立完成构建 trainset、在 trainset 上测试算法、构造反测试集进行全量召回评测以及理解算法内部为何统一使用整数内 id。一、Trainset 是什么Dataset 与 Trainset 的分工Surprise 官方文档对 Trainset 的定义如下见 surprise/trainset.pyA trainset contains all useful data that constitute a training set. It is used by thefit()method of every prediction algorithm. You should not try to build such an object on your own but rather use theDataset.folds()method or theDatasetAutoFolds.build_full_trainset()method.这段话揭示了两个关键事实Trainset 是算法的直接输入。AlgoBase.fit()的签名是fit(self, trainset)它做的事情只是把 trainset 保存为self.trainset并重置基线参数见 surprise/prediction_algorithms/algo_base.py。用户不应该手动实例化 Trainset而应通过Dataset的构造方法间接获得。关于第二个事实可以这样理解两者的关系Dataset 是原始数据层它持有从文件、DataFrame 或内置数据集读出的原始评分四元组(user_raw_id, item_raw_id, rating, timestamp)即raw_ratings列表。Trainset 是高层数据层它对原始评分做了 id 归一化、建立了用户-评分/物品-评分索引并提供global_mean、build_testset()、build_anti_testset()等实用方法。一个 Dataset 可以派生出多个 Trainset。交叉验证时每个 fold 都会调用construct_trainset()生成一个独立的 Trainset源码注释中明确写道a Dataset may be comprised of multiple Trainsets (e.g. when doing cross validation)。二、Trainset 的核心数据结构与属性Trainset.__init__接收 8 个参数见 surprise/trainset.py其公开属性如下属性类型含义urdefaultdictoflist用户评分字典键为用户内 id值为(item_inner_id, rating)元组列表irdefaultdictoflist物品评分字典键为物品内 id值为(user_inner_id, rating)元组列表n_usersint用户总数 |U|n_itemsint物品总数 |I|n_ratingsint训练评分总数 |R_train|rating_scaletuple评分区间(min, max)如(1, 5)global_meanfloat全部评分的均值 μ通过property懒计算值得注意的是_raw2inner_id_users、_raw2inner_id_items两个字典是私有的下划线前缀而反向映射_inner2raw_id_users、_inner2raw_id_items在初始化时被置为None直到第一次调用to_raw_uid()/to_raw_iid()时才惰性构建。源码注释解释了这一设计动机surprise/trainset.pyinner2raw dicts could be built right now (or even before) but they are not always useful so we wait until we need them.即反向映射并非所有算法都需要延迟构建可以省去不必要的内存与时间开销。2.1 Trainset 是如何被构建出来的以最常用的build_full_trainset()为例调用链为DatasetAutoFolds.build_full_trainset()→Dataset.construct_trainset(raw_ratings)→Trainset(...)在 surprise/dataset.py 的construct_trainset中逐条遍历原始评分完成了三类关键工作raw → inner 映射为每个首次出现的用户/物品分配从 0 开始递增的整数内 id构建 ur / ir 索引ur[uid].append((iid, r))、ir[iid].append((uid, r))统计规模n_users len(ur)、n_items len(ir)、n_ratings len(raw_trainset)。交叉验证场景下surprise/model_selection/split.py 中的KFold.split()、ShuffleSplit.split()、LeaveOneOut.split()等迭代器都会在每折内先切出raw_trainset再调用data.construct_trainset(raw_trainset)生成该折的 Trainset最后yield (trainset, testset)。因此你写的for trainset, testset in kf.split(data)中的 trainset 正是由这套流程产生的。三、raw id 与 inner idSurprise 的双 id 机制这是使用 Trainset 时最容易踩坑、也最需要理解的概念。文档 doc/source/FAQ.rst 有专门一节进行说明raw id原始 id评分文件或 DataFrame 中的原始标识可以是字符串也可以是数字。注意从文件读取的评分其 raw id 一律是字符串即使看起来是数字。因此调用predict(uid, iid)这类接收 raw id 的方法时务必传入字符串否则可能匹配失败。inner id内 idtrainset 构建时为每个 raw id 分配的唯一整数从 0 开始连续编号更适合库内部的高效运算与矩阵索引。由于内 id 是每个 Trainset 各自独立分配的同一个用户在不同折的 Trainset 中可能拥有不同的内 id——这也是为什么测试集testset中保存的永远是 raw id见construct_testset的实现surprise/dataset.py。为什么用整数内 id因为所有算法尤其是矩阵分解与近邻类算法都需要把用户/物品索引到稠密矩阵或数组的整数下标上。整数内 id 使X[u][i]这样的访问成为可能而 raw id 是任意类型无法直接做数组索引。3.1 四个转换方法Trainset 提供了两对双向转换方法全部定义于 surprise/trainset.py方法方向说明异常to_inner_uid(ruid)用户 raw → inner将用户 raw id 转为内 id用户不在 trainset 时抛ValueErrorto_raw_uid(iuid)用户 inner → raw将用户内 id 转为 raw id首次调用触发反向字典构建非合法内 id 时抛ValueErrorto_inner_iid(riid)物品 raw → inner将物品 raw id 转为内 id物品不在 trainset 时抛ValueErrorto_raw_iid(iiid)物品 inner → raw将物品内 id 转为 raw id首次调用触发反向字典构建非合法内 id 时抛ValueErrorto_inner_*的实现是直接查_raw2inner_id_*字典并捕获KeyError转成ValueErrorto_raw_*则在_inner2raw_id_*为None时先用字典推导式{inner: raw for (raw, inner) in ...}构建反向映射再查询。四、Trainset 的查询与遍历方法4.1 成员检查knows_user 与 knows_itemknows_user(uid)判断用户是否在 trainset 中实现为return uid in self.ur即该用户是否至少有一条评分surprise/trainset.pyknows_item(iid)判断物品是否在 trainset 中实现为return iid in self.ir即该物品是否被至少评分过一次surprise/trainset.py。注意这两个方法的参数都是内 id。4.2 遍历器all_ratings / all_users / all_itemsall_ratings()生成器依次产出(uid, iid, rating)三元组id 均为内 idsurprise/trainset.py。它是global_mean计算的基础也是build_testset()的数据源all_users()返回range(self.n_users)产出全部用户内 idsurprise/trainset.pyall_items()返回range(self.n_items)产出全部物品内 idsurprise/trainset.py。all_users()与all_items()直接使用range而非字典键正是得益于内 id 的连续整数编号设计——这也是build_anti_testset()能高效枚举所有用户 × 所有物品组合的前提。4.3 global_mean懒计算的全局均值global_mean是一个propertysurprise/trainset.py第一次访问时用 numpy 对all_ratings()的所有评分求均值并缓存到_global_mean之后直接返回缓存值。它是build_anti_testset(fillNone)时的默认填充值也是许多基线算法的初始估计。五、构建测试集build_testset 与 build_anti_testset5.1 build_testset()在训练集上自测有偏评估build_testset()返回一个可直接传给algo.test()的测试集内容就是训练集自身的全部评分但以 raw id 形式给出surprise/trainset.pyreturn [ (self.to_raw_uid(u), self.to_raw_iid(i), r) for (u, i, r) in self.all_ratings() ]用途是在训练集上评估算法得到的是有偏乐观的指标。仓库示例 examples/evaluate_on_trainset.py 给出了完整用法from surprise import accuracy, Dataset, SVD from surprise.model_selection import KFold data Dataset.load_builtin(ml-100k) algo SVD() trainset data.build_full_trainset() algo.fit(trainset) testset trainset.build_testset() predictions algo.test(testset) # RMSE should be low as we are biased accuracy.rmse(predictions, verboseTrue) # ~ 0.68 (which is low)示例还演示了在交叉验证循环内同时评估测试集与训练集的做法kf KFold(n_splits3) for i, (trainset_cv, testset_cv) in enumerate(kf.split(data)): print(fold number, i 1) algo.fit(trainset_cv) print(On testset,, end ) predictions algo.test(testset_cv) accuracy.rmse(predictions, verboseTrue) print(On trainset,, end ) predictions algo.test(trainset_cv.build_testset()) accuracy.rmse(predictions, verboseTrue)5.2 build_anti_testset(fillNone)构造反测试集全量负样本build_anti_testset()返回所有不在训练集中的评分组合即用户 u 已知、物品 i 已知、但(u, i)没有被评过分。由于真实评分未知未知评分用fill参数填充fill self.global_mean if fill is None else float(fill)具体逻辑surprise/trainset.py对每个用户先取出其已评分物品集合user_items再遍历all_items()把未评分的(u, i)以(raw_uid, raw_iid, fill)形式加入结果。因此fillNone默认未知评分填全局均值global_meanfill某个数值未知评分填该数值如fill0常用于 Top-N 推荐评测。测试 tests/test_dataset.py 验证了其正确性for fillvalue in (0, 42.0, -1): anti trainset.build_anti_testset(fillfillvalue) for (u, i, r) in anti: assert r fillvalue anti trainset.build_anti_testset(fillNone) for (u, i, r) in anti: assert r trainset.global_mean expect trainset.n_users * trainset.n_items assert trainset.n_ratings len(anti) expect最后一个断言非常精妙训练评分数 反测试集大小 用户数 × 物品数即反测试集恰好补全了完整的用户-物品笛卡尔积二者互斥且互补。这正是做全量召回 / Top-N 推荐评估的标准数据集构造方式。六、实战演练一个完整的 Trainset 使用流程下面结合build_full_trainset()与四个转换方法演示从 DataFrame 构造 Trainset 并做基础查询思路与 tests/test_dataset.py 的测试一致import pandas as pd from surprise import Dataset, Reader ratings_dict { itemID: [1, 1, 1, 2, 2], userID: [9, 32, 2, 45, 10000], rating: [3, 2, 4, 3, 1], } df pd.DataFrame(ratings_dict) reader Reader(rating_scale(1, 5)) data Dataset.load_from_df(df[[userID, itemID, rating]], reader) trainset data.build_full_trainset() print(trainset.n_users, trainset.n_items, trainset.n_ratings) # 5 2 5 print(trainset.rating_scale) # (1, 5) print(trainset.global_mean) # 2.6 # raw - inner print(trainset.to_inner_uid(9)) # 用户 raw id9 的内 id print(trainset.to_inner_iid(2)) # 物品 raw id2 的内 id # inner - raw首次调用会触发反向映射构建 print(trainset.to_raw_uid(trainset.to_inner_uid(9))) print(trainset.to_raw_iid(trainset.to_inner_iid(2))) # 成员检查 print(trainset.knows_user(trainset.to_inner_uid(9))) # True print(trainset.knows_item(trainset.to_inner_iid(2))) # True # 遍历评分 for uid, iid, r in trainset.all_ratings(): print(uid, iid, r)再结合预测接口演示内 id 的使用边界。AlgoBase.predict()接收的是raw id其内部先调用to_inner_uid/to_inner_iid做转换若用户或物品不在 trainset 中则内 id 会被替换为UKN__ str(uid)这样的占位符并最终走default_prediction()兜底见 surprise/prediction_algorithms/algo_base.py。而自定义算法中重写的estimate(uid, iid)方法接收的则是inner id详见 doc/source/building_custom_algo.rst 的说明。把握住对外 raw、对内 inner这条分界线就能避免绝大多数 id 混用导致的错误。七、深入原理从测试与源码印证 Trainset 的行为7.1 惰性反向映射的可观察行为tests/test_dataset.py 精确验证了惰性构建机制assert trainset._inner2raw_id_users is None assert trainset._inner2raw_id_items is None for i in range(4): assert trainset.to_raw_uid(i) user str(i) for i in range(2): assert trainset.to_raw_iid(i) item str(i) assert trainset._inner2raw_id_users is not None assert trainset._inner2raw_id_items is not None在调用任何to_raw_*之前两个反向字典均为None一旦调用它们被填充后续访问直接命中缓存。7.2 未知 id 的异常行为同一测试文件tests/test_dataset.py还验证了to_inner_uid/to_inner_iid对未知 id 抛出ValueErrorwith pytest.raises(ValueError): trainset.to_inner_uid(unknown_user) with pytest.raises(ValueError): trainset.to_inner_iid(unknown_item)7.3 ur / ir 的结构细节测试 tests/test_dataset.py 展示了 ur / ir 的确切形态ur[0] [(0, 4)]表示内 id 为 0 的用户对物品 0 打了 4 分ir[0] [(0, 4), (1, 4), (2, 1)]表示物品 0 被三个用户分别打了 4、4、1 分。注意ur[40]、ir[20000]等不存在的键会返回空列表——这正是defaultdict(list)的特性意味着不要用len(trainset.ur)的键数量去推断用户数以外的信息也不要依赖键的存在性判断而应使用knows_user/knows_item。测试 tests/test_zero_ratings.py 也提醒直接访问trainset.ur可能因 defaultdict 的自动创建而产生幽灵键遍历时应优先使用all_ratings()以保证数据可靠。7.4 为什么不建议手动构造 TrainsetTrainset.__init__需要同时提供 ur、ir、四个统计量、rating_scale 以及两套映射字典任何一处不一致都会导致算法行为异常例如 ur 与 n_users 不匹配。源码 docstring 与 doc/source/trainset.rst 都明确建议通过Dataset.folds()或DatasetAutoFolds.build_full_trainset()获取这正是Dataset.construct_trainset()内部统一封装全部逻辑的原因——它保证了映射、索引、统计量的原子性一致。八、总结Trainset 在 Surprise 中的位置一句话概括Dataset 管读Trainset 管算。无论数据来自内置的 ml-100k、ml-1m、jester还是自定义文件、DataFrame最终都会经过construct_trainset()归一化成一个或多个 Trainset供各类算法fit()使用。掌握 Trainset 的六大核心能力——ur/ir索引、内 id 转换、成员检查、遍历器、build_testset()有偏自评、build_anti_testset()全量反测试——就等于掌握了 Surprise 从数据到模型的最后一块拼图。相关代码与测试均位于 surprise/trainset.py、surprise/dataset.py、surprise/model_selection/split.py 与 tests/test_dataset.py读者可继续深入研读。赞分享机器学习人工智能【免费下载链接】SurpriseA Python scikit for building and analyzing recommender systems项目地址https://gitcode.com/gh_mirrors/su/Surprise点击查看免费下载相关推荐AlphaFold 3实战指南从零开始掌握生物分子结构预测的完整流程AlphaFold 3实战指南从零开始掌握生物分子结构预测的完整流程 AlphaFold 3作为DeepMind最新一代的生物分子结构预测工具在蛋白质、核酸人工智能基础模型深度学习生物信息学科学计算情感分析与 IMDb 数据集处理d2l-en 从原始影评到可训练数据迭代器的完整实战指南情感分析与 IMDb 数据集处理d2l en 从原始影评到可训练数据迭代器的完整实战指南 情感分析Sentiment Analysis是自然语言处理中的经文档教程人工智能深度学习NLP计算机视觉强化学习MiniMind训练策略深度解析从算法选择到参数调优的完整指南MiniMind训练策略深度解析从算法选择到参数调优的完整指南 在深度学习模型训练中选择合适的训练策略往往比盲目调参更重要。MiniMind框架提供了多种训人工智能大模型预训练微调强化学习模型蒸馏AI Agent工具调用模型推理服务模型评测上一篇3种免费方法解锁加密音乐Unlock Music完整教程指南下一篇为什么选择NativeOverleaf桌面LaTeX写作的5大优势创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表