返回列表

2nd Place Solution (One model is all you need) - Team Ujjwal Pandey

619. Playground Series - Season 4, Episode 7 | playground-series-s4e7

开始: 2024-06-30 结束: 2024-07-31 保险科技 数据算法赛
第二名解决方案 - Ujjwal Pandey 团队

第二名解决方案(只需一个模型)- Ujjwal Pandey 团队

作者: Ujjwal Pandey
发布时间: 2024-08-01
竞赛排名: 第 2 名

大家好,

我想感谢 Kaggle 社区和各位参赛者带来了令人难以置信的 Playground Series episode。这是一个资源密集且耗时的挑战,需要极大的耐心才能取得胜利。公共 leaderboard 的频繁变动,尤其是在最后 2-3 天,反映了激烈的竞争。

以下是我分为几个迭代的旅程。

迭代 1

在最初的尝试中,我在配备 RTX 4070 的本地 PC 上训练了一组快速、轻微调优的模型(XGB、LGBM 和 SnapML)。

  • XGB 提交很顺利,公共 LB 得分为 0.88448,CV 得分为 0.8833
  • 我在 LightGBM CUDA 版本上遇到了问题,因为这个 bug [CUDA] illegal memory access。因此,我不得不在第一次迭代中放弃 LightGBM。
  • 我没有 pursue Random Forest 或任何 sklearn 库,因为公共讨论和笔记本表明它们不值得付出计算努力。
  • SnapML 即使在轻量级数据集上通常也需要很长时间,但我还是尝试了,因为它支持 GPU,它的 LB 得分也接近 0.8880

在这次迭代结束时,我的得分大约在 0.88。为了突破 0.89 大关,我需要进一步的改进。我还没有探索 CatBoost,因为公共论坛表明其默认设置可以产生超过 0.895 的得分。

迭代 2

决心调优 XGB、LGBM 和 SnapML 以匹配 CatBoost 的性能:

  • 我在四台机器上(2 x Kaggle GPU P100, 1 x Colab L4, 1 x 本地 RTX 4070)使用分布式 Optuna aggressively 调优了 XGB 和 SnapML。为了避免 HPO 期间的 OOM 错误,我没有将测试集加载到内存中。
  • 在这个阶段,我还合并了原始数据集,并根据公共笔记本创建了这四个额外的交互特征:
train_df['Insured_Vehicle_Damage']=  train_df['Previously_Insured'].astype(str) +  train_df['Vehicle_Damage'].astype(str)
train_df['Insured_Vehicle_Age'] =  train_df['Previously_Insured'].astype(str) +  train_df['Vehicle_Age'].astype(str)
train_df['Insured_License'] =  train_df['Previously_Insured'].astype(str) +  train_df['Driving_License'].astype(str)
train_df['Insured_License'] =  train_df['Previously_Insured'].astype(str) +  train_df['Driving_License'].astype(str)
train_df['Insured_Gender'] =  train_df['Previously_Insured'].astype(str) +  train_df['Gender'].astype(str)
  • 调优后的 XGBoost 公共 LB 得分为 0.89387,CV 得分为 0.89113
  • 我在 CPU 上使用三台机器(1 x Kaggle TPU 和 2 x Colab TPUs)调优了 LightGBM。该模型公共 LB 得分为 0.89344,CV 得分为 0.89302。在 Kaggle 上使用 TPU 的一个挑战是,当 TPU 未被消耗时,3 小时后会自动关闭,但分布式调优允许我快速恢复训练。
  • 接下来是 SnapML,遗憾的是,这个模型甚至无法在我的 CV 上突破 0.890,因此在这次迭代后我将其过滤掉了 ☹️。
  • 在看到公共讨论和笔记本中神经网络的性能后,我尝试了一下。有一个很棒的库 pytorch-tabular,我用它训练了 TABNETGANDALF,它们的 CV 得分也达到了 ~0.8910,但它们的训练成本非常高,并且与 GBDT 相比没有提供更好的性能。

在这个阶段结束时,我的 LB 得分约为 ~0.8930,所以下一阶段是尝试一些不同的技术来缩小差距。

迭代 3

  • 我尝试使用来自 pytorch-tabular CategoryEmbedding 模型的神经嵌入来提高 LightGBM 和 XGBoost 的得分。这是一个错误,因为嵌入维度接近 300 维,需要高 RAM。CV 得分最初飙升到 0.895 是由于种子不匹配导致的 bug,我在耗尽 Colab 积分后才发现这一点 ☹️。
  • 我在一定程度上尝试了目标编码,但即便如此也无法推动 XGB 或 LGBM 的得分 ☹️。

此时,我考虑跳过这一期,因为几乎耗尽了所有计算资源和时间,且没有成功的保证。在大约 30 次提交后,我决定最后尝试一次 CatBoost。

最终迭代

在这个阶段,我只调优了一个 CatBoost 模型(就一个),再次使用相同的策略,在 3 台机器上分布式 Optuna,接近 50 轮 HPO,耗时约 10 小时,因为不幸的是,与 XGB 和 LightGBM 相比,CatBoost GPU 不支持 pruner。以下是 HPO 调优后的参数:

'learning_rate': 0.11913236771124495,
'reg_lambda': 0.5423732686916918,
'max_depth': 6,
'subsample': 0.9996168133883909,  # 我在训练完整模型时将其改为 1.0。
'leaf_estimation_iterations': 10,
'log_max_bin': 15

以下是我用于 HPO 的固定参数:

"loss_function": "Logloss",
"eval_metric": "AUC",
"iterations": 3000,
"random_state": 56315,
"bootstrap_type": "Bernoulli",
"grow_policy": "SymmetricTree",
"task_type": "GPU",
"early_stopping_rounds": 100,
"leaf_estimation_method": "Newton",
"use_best_model": True,

上述参数在训练 5000 次迭代并进行一些调整时,公共 LB 得分为 0.89666,但这还不足以获胜。

通过在 Kaggle 笔记本上的小实验,我观察到了两件重要的事情:

  1. 我可以手动将学习率降低到 0.085 并将迭代次数增加到 10000
  2. 基于 Newton 的 score_function 优于基于 Gradient 的。我想这是大多数公共笔记本忽略的。一旦我切换到 NewtonCosineNewtonL2 并使用 12 次 leaf_estimation_iterations,我的 CV 本身就接近 0.8960

此外,我首先从原始数据中移除对比重复项,然后在合并后从训练集和原始数据中移除,正如本讨论中所讨论的:https://www.kaggle.com/competitions/playground-series-s4e7/discussion/520253。我还对 Age 和 Premium 特征进行了分箱,因为我发现这略微提高了我的验证得分。

在尝试了所有方法后,我将数据集分为 4 折。我无法一起训练所有折,甚至无法在 Kaggle 上训练单个折,因为 CatBoost 训练后需要接近 (48 GB) 的 RAM。

我用剩余的计算资源在 Colab 上训练了前 3 折,这次提交取得了 0.89720 的 LB 得分,使我进入前 10 名,但在按照 @paddykb 提到的技巧操作后,它提升到了 0.89780

为了最终确定得分,我在 Kaggle 上训练了具有相同参数但减少迭代次数的小型 CatBoost 版本,使用不同的 CV 分割和随机状态,并仅基于验证得分进行平均(因为此时我没有 OOF 折来 properly 混合),这使得我的最终得分在没有技巧的情况下为 0.89728,有技巧的情况下为 0.89788,这是获胜解决方案,私有 LB 得分为 0.89753

以下是没有任何技巧的折的最终 CV-LB 得分:

折 (Fold) CV 得分 LB 得分
Fold-0 0.8960367441 0.89625
Fold-1 0.8962229192 0.89629
Fold-2 0.8962465823 0.89632
Fold-3 0.8959594369 0.89620
**Fold-4 0.8958430433 -
**Fold-4 是我所有小型模型的平均值。

总结

关键收获

  1. 监控讨论和公共笔记本以节省实验和计算时间,允许用更少的提交做出计算决策。
  2. 对大数据集保持耐心并尝试不同的训练策略。错误是学习的机会。
  3. 分布式优化更快、更实惠,并且具有更好的错误容忍度,尤其是在此类竞赛中。
  4. 警惕泄露,即使更改随机状态也是如此,尤其是在神经嵌入中。

无效尝试

  1. 与 XGB 和 LGBM 一起使用目标编码。
  2. 神经嵌入带来的性能提升不足以抵消所需的计算量。
  3. 在我的案例中,更改 CatBoost 的默认 max_ctr_complexity 导致过拟合。

本可以做的改进

  1. 保存 OOF 折并进行组合本可以提高我的得分,但由于它们的大小太大,我未能做到。
  2. 更多地探索神经网络。
  3. 尝试不同的 CatBoost 风味(可能是 lossguide)。
同比赛其他方案