在Surprise中从预定义折加载数据时如何构建完整训练集?
解决Surprise中
DatasetUserFolds无法调用build_full_trainset()的问题 我刚遇到过类似的情况,当用PredefinedKFold加载预拆分数据时,得到的DatasetUserFolds对象确实没有build_full_trainset()方法——因为它本质是多个训练/测试折的集合,不是单一的数据集。下面给你两种可行的解决方案,都能帮你构建完整训练集并计算预测覆盖率:
方法1:在Surprise框架内合并所有训练折
你可以手动遍历预定义的所有训练折,把它们的原始数据合并成一个完整数据集,再重新构建Surprise可用的数据集对象:
# 加载预拆分数据(复用你原有的load_splits函数) data = load_splits() pkf = PredefinedKFold() # 收集所有训练折的原始评分数据 all_train_ratings = [] for trainset, _ in pkf.split(data): # 遍历当前训练折的所有评分,转换为原始用户ID、物品ID、评分的三元组 for inner_uid, inner_iid, rating in trainset.all_ratings(): raw_uid = trainset.to_raw_uid(inner_uid) raw_iid = trainset.to_raw_iid(inner_iid) all_train_ratings.append((raw_uid, raw_iid, rating)) # 转换为DataFrame,重新构建Surprise数据集 full_train_df = pd.DataFrame(all_train_ratings, columns=["user", "item", "rating"]) reader = Reader(line_format='user item rating', sep=' ', rating_scale=(1, 5)) full_dataset = Dataset.load_from_df(full_train_df[["user", "item", "rating"]], reader) # 现在可以正常构建完整训练集了 full_trainset = full_dataset.build_full_trainset()
接下来用这个完整训练集训练模型,再遍历所有用户-物品组合计算覆盖率:
# 用GridSearch得到的最佳模型训练 algo = gs.best_estimator['rmse'] algo.fit(full_trainset) # 获取所有用户和物品的原始ID all_raw_users = [full_trainset.to_raw_uid(uid) for uid in full_trainset.all_users()] all_raw_items = [full_trainset.to_raw_iid(iid) for iid in full_trainset.all_items()] # 统计无法预测的组合数,计算覆盖率 unpredictable = 0 total_combinations = len(all_raw_users) * len(all_raw_items) for uid in all_raw_users: for iid in all_raw_items: try: # 执行预测,这里可以把结果保存到列表或文件中 _ = algo.predict(uid, iid) except Exception: unpredictable += 1 coverage = (total_combinations - unpredictable) / total_combinations print(f"预测覆盖率: {coverage:.4f}")
方法2:直接合并原始拆分文件(更高效)
如果你的预拆分训练文件(u1.base到u5.base)是独立存储的,你可以直接在文件层面合并所有训练数据,再加载成完整数据集:
import os files_dir = 'data/recommenders/splits/' full_train_file = os.path.join(files_dir, 'u_full.base') # 合并所有base训练文件 with open(full_train_file, 'w') as out_f: for fold_num in range(1, 6): fold_file = os.path.join(files_dir, f'u{fold_num}.base') with open(fold_file, 'r') as in_f: out_f.write(in_f.read()) # 加载合并后的完整数据集 reader = Reader(line_format='user item rating', sep=' ', skip_lines=0, rating_scale=(1,5)) full_dataset = Dataset.load_from_file(full_train_file, reader=reader) full_trainset = full_dataset.build_full_trainset()
这种方法不需要处理Surprise内部的编码转换,更适合你已经有独立拆分文件的场景,代码更简洁高效。
为什么原方法会报错?
当你用Dataset.load_from_folds()加载预拆分数据时,返回的DatasetUserFolds是一个包含多组(train, test)对的集合,它的设计目标是用于交叉验证流程,而非作为单一数据集使用,因此没有提供build_full_trainset()方法。上面两种方法都是把分散的训练数据重新合并为单一数据集,自然就能正常调用该方法了。
内容的提问来源于stack exchange,提问作者user2529589
相关产品推荐
相关产品推荐

