664. Playground Series - Season 5, Episode 8 | playground-series-s5e8
本月的 Playground 竞赛非常有趣!CV(交叉验证)与 LB(排行榜)的关系很稳固,而且聚集了一群非常棒的竞争对手,使得从月初到月末整个过程都充满乐趣。
我想向 Kaggle 组织者以及所有贡献了有用讨论和 Notebook 的人表示最深切的感谢。
特别是 @tilii7, @mahoganybuttstrings, @optimistix, @cdeotte, 和 @siukeitin 的帖子和评论极具信息量和启发性。非常感谢大家。
在竞赛的前 20 天,我致力于实验。我会生成预测,保存 OOF 和测试预测,然后不加太多深思地将其输入到 XGBoost 模型中。我重复这个循环,直到我的 Public Score 达到 0.9781 左右。
在剩下 10 天时,我决定重新评估我的方法。我对我好奇的领域进行了实验(例如,折平均是否比重拟合更好)。在最后 7 天,我确定了最终策略,进行了一些微调,并将分数从 0.97808 提高到了 0.97828。
我想用最小的努力创建尽可能多的多样化模型,所以我转向了 AutoGluon。正如官方 AutoGluon 文档中提到的,花时间进行特征工程 (FE) 是使用该库最有效的方法。我创建了许多特征集的变体,并在每个变体上训练了一个 AutoGluon 实例,这让我能够轻松生成大量的 OOF 预测。这些模型的 CV 分数范围在 0.95 到 0.9758 之间。
由于 AutoGluon 主要专注于集成而不是参数调整,我还单独使用了 FLAML 为每个特征集创建优化的 LGBM 模型。其中一个 Notebook 在这里。除此之外,我还创建了两三个更多样化的 LGBM 模型来生成 OOF。这些模型的 CV 分数在 0.971 到 0.976 之间。
此外,正如 @mahoganybuttstrings 的优秀 Notebook 所展示的那样,创建 2 路交互特征并应用目标/频率编码 (Target/Frequency Encoding) 非常有效。我想将其扩展到 3 路或更高阶的交互,但在 Kaggle Notebook 环境中遇到了内存问题。
为了克服这个问题,我使用 Optuna 搜索了多达 150 个有效的 2 路到 6 路特征组合。由于 12 小时的运行时间限制,无法进行完整的搜索,所以我多次运行搜索过程(大约 5 次)作为补偿。这种方法产生的 CV 分数在 0.973 到 0.9755 之间。
我还尝试了其他一些非常规模型,主要是为了好玩。这些包括 '1D-CNNs' 以及像 'NN-SVC_Head' 和 'NN-XGB_Head' 这样的模型,它们是在从基础神经网络提取的特征上训练的。这些模型是在 2 路 TE/CE 特征上训练的,都达到了约 0.974 的 CV。然而,这次它们并没有给最终混合带来显著提升。
我觉得有帮助的讨论:
最终,我实施了能想到的每一个想法,包括过去尝试过的东西以及在其他竞赛中看到的有趣解决方案。对于所有 L1 模型,我使用了每个折内多个种子的预测的折平均 (fold-average),而不是在整个数据集上重拟合。
我使用 L1 中生成的 200+ 个 OOF 预测训练了 XGBoost 和神经网络模型。
在这个阶段,我添加了来自测试集的伪标记数据,条件是 test_pred > 0.99 或 test_pred < 0.01。用于此伪标记的预测来自所有 L1 模型的加权平均,权重通过爬山算法 (Hill Climb) 计算。我考虑过使用最终模型的测试预测,但我选择了加权 L1 平均,以降低过拟合和高模型偏差的风险,希望它能更好地吸收个别模型的怪癖。
我以两种不同的方式使用了这些伪数据:
我试图为此创建一个图表,但效果不佳。关于 k-NN 列方法,请参考这个 Notebook。
使用伪数据的想法来自 @cdeotte 的这篇总结:https://www.kaggle.com/competitions/playground-series-s5e6/writeups/chris-deotte-1st-place-fast-gpu-experimentation-wi。
| 模型 | CV | Public LB | Private LB |
|---|---|---|---|
| L2-pseudoColXGB | 0.977431 | 0.97816 | 0.97774 |
| L2-pseudoRowXGB | 0.977418 | - | 0.97767 |
| L2-pseudoColNN | 0.977494 | - | 0.97762 |
| L2-pseudoRowNN | 0.977471 | 0.97802 | 0.97757 |
对于最后一层,我对 L2 模型的四个 OOF 预测进行了加权平均。权重是使用爬山算法计算的。
最终权重为:
pcol_XGB*0.1 + prow_XGB*0.2 + pcol_NN*0.55 + prow_NN*0.15
在模型类型层面上,这简化为 XGB*0.3 + NN*0.7。
| 模型 | CV | Public LB | Private LB |
|---|---|---|---|
| L3-HC | 0.977594 | 0.97828 | 0.97790 |
我相信很多人都在争论是使用折预测的平均值,还是使用在所有训练数据上重拟合的模型的预测。我最终为所有模型选择了折平均,但我做了一些实验来做出这个决定。
我将训练数据按 80:20 分割。20% 的分割作为验证集保留。我使用 80% 的部分进行交叉验证和重拟合,然后在保留的 20% 上比较两种方法的性能。
在重拟合时,我尝试使用 folds 中平均 best_iteration 的 1.2x 和 2.0x,但在所有情况下,折平均方法都产生了更好且更稳定的结果。
查看 Chris Deotte 的实验,他似乎从重拟合中看到了不错的提升。我个人推测这可能是由于模型数量的增加(重拟合 + 多种子),而不仅仅是训练数据的增加。也就是说,refit + multi-seed average > fold-average > refit (1 seed)。由于为每个折训练多个种子(这也增加了模型数量)给我的性能与 refit + multi-seed 非常相似,所以我采用了这种方法。
当我在广泛的特征工程后使用 AutoGluon 时,经常遇到"disk space exceeded"(磁盘空间超出)错误。为了管理这个问题,我通过使用 excluded_model_types 或 included_model_types 限制了模型数量。我还设置了 num_stack_levels=0 以确保我只获取 L1 基础模型。
predictor = TabularPredictor(label=label, eval_metric=metric, groups='fold').fit(
train_data,
excluded_model_types=['XGB'],
# included_model_types=['GBM', 'FASTAI'],
time_limit=time_limit,
presets='extreme',
num_cpus=4,
num_gpus=2,
num_stack_levels=0,
)
通过将模型限制为较轻量的模型,如 included_model_types=['GBM', 'FASTAI'],甚至可以在环境中训练到 L3。
关于如何检索 OOF 预测的信息,官方文档和 @ravaghi 的 Notebook 非常有帮助:https://www.kaggle.com/code/ravaghi/s05e05-calorie-expenditure-prediction-automl。
我很乐意在评论中听到任何反馈或改进建议。