返回列表

9th Place Solution: Various Augmentations + A lot of Modeling tricks

631. UM - Game-Playing Strength of MCTS Variants | um-game-playing-strength-of-mcts-variants

开始: 2024-09-05 结束: 2024-12-02 游戏AI 数据算法赛
第 9 名解决方案:各种增强 + 大量建模技巧
作者: Mohamed Eltayeb (Master)
发布日期: 2024-12-03
团队: Cody_Null, HZM, Gaurav Rawat
竞赛排名: 第 9 名

第 9 名解决方案:各种数据增强 + 大量建模技巧

感谢主办方和 Kaggle 带来如此精彩的体验。我非常享受这次比赛,尤其是与我优秀的队友 @cody11null@leehann@gauravbrills 一起合作。
终于在竞争了 3 年后,我获得了金牌并晋升到了 Master 层级!!!!

总结:

我们的解决方案包括修改后的 AdvantageP1 特征、CatBoost 和两个在不同数据/特征上训练的 LGBM Dart 模型之间的集成。
我们成功选择了我们在私有榜上表现第 2 好的提交,它在公共榜上也是第 2 名,并且交叉验证(CV)得分最高之一。
Leaderboard Screenshot
我们使用 8 折 GroupKFold 验证模型。令人惊讶的是,使用 8 折而不是 5 折或 10 折使得 CV 更加稳定,并且比任何其他策略的相关性更高。

数据预处理:

1. 数据增强:

我们探索了几种类型的关系,包括以下内容:
翻转 (Flip): Agent1=a, Agent2=b ==> Agent1=b, Agent2=a
我们翻转代理,将 AdvantageP1 改为 1-AdvantageP1,并将 utility_agent1 乘以 -1。
这是最有用的方法。它在训练和作为推理时的测试时增强(TTA)都效果很好。特别是如果你分别为原始数据训练一个模型,为增强样本训练一个模型然后集成。这在 CV(+~0.01)和 LB(+~0.003)上都给出了巨大的提升,而不是将它们合并到同一个模型中(+~0.001 LB)。
但在最后,经过许多实验,我们发现对其进行训练会使模型变得不稳定,因此我们从训练中 drop 掉了它,只使用了原始数据。事实证明这是正确的,因为我们最终没有基于增强数据训练的子模型在私有榜上表现更好。
自我对弈 (Self-play): Agent1=a, Agent2=b ==> Agent1=a, Agent2=a 和 Agent1=b, Agent2=b
我们通过让代理与自己对抗来创建新样本,将 Advp1 设为 0.5,utility_agent1 设为 0。这对 CV 帮助很大,但对 LB 没有帮助。
传递性 (Transitivity): Agent1=a, Agent2=b 和 Agent1=b, Agent2=c ==> Agent1=a, Agent2=c
很难为这个设置 AdvantageP1 和 utility_agent1 值。我采用了这种方法:

  • 初始化新样本的 wins/draws/losses:
# 从第一个样本分配 num_wins_agent1
new_row['num_wins_agent1'] = row1['num_wins_agent1']

# 从第二个样本分配 num_losses_agent1
new_row['num_losses_agent1'] = row2['num_losses_agent1']

# 求和两个样本的 draws
new_row['num_draws_agent1'] = (row1['num_draws_agent1'] + row2['num_draws_agent1'])
  • 定义“技能差异 (Skill Difference)",指的是代理 1 和 3 之间的技能差异:
diff = row1['num_losses_agent1'] - row2['num_wins_agent1']

因为如果关系中的中间代理(连接两个样本的代理)对第一个和第三个代理有相同的胜率,那么我们可以假设代理 1 和 3 没有技能差异,因此我们取第一个的胜率和第三个的胜率作为我们的新胜负数。但是如果有差异怎么办?我们应用这个:

# 计算技能差异
diff = row1['num_losses_agent1'] - row2['num_wins_agent1']
if diff < 0:
      new_row['num_wins_agent1'] += abs(diff)
elif diff > 0:
      new_row['num_losses_agent1'] += abs(diff)

我们将相同的技能差异思想应用于 AdvantageP1:

new_row['AdvantageP1'] = row1['AdvantageP1']
new_row['AdvantageP2'] = row2['AdvantageP2']
diff_adv = row1['AdvantageP2'] - row2['AdvantageP1']
clip_val = 1 - (new_row['AdvantageP1'] + new_row['AdvantageP2'])
if diff_adv < 0:
      new_row['AdvantageP1'] += np.clip(abs(diff_adv), 0, clip_val)
elif diff_adv > 0:
      new_row['AdvantageP2'] += np.clip(abs(diff_adv), 0, clip_val)

这实际上增加了整整 20 万个新样本。此外,经过上述修改,目标分布与原始训练数据非常相似:
Target Distribution
但在最后,这个传递性想法在 CV 上没有显著差异,我也没有针对 LB 尝试它(在这个过拟合派对之后我应该做的😅)。我认为它需要更多的工作。
最终,我们决定从训练中 drop 掉所有的增强想法,因为它们使结果变得非常不稳定(我们只使用原始数据进行训练),仅在推理期间使用翻转增强进行 TTA。
例如,我们将训练数据和增强数据结合在 X_Train 中,并针对原始数据 X_Test 进行验证(以确保正确的比较),CV 从 0.405 跳到了 0.395。100% 没有泄漏。但 LB 从 0.416 到了 0.421。在那一点上,我们相信很容易在 CV 上过拟合,所以我们尝试只使用能同时改善 CV 和 LB 的想法,结果证明这是最后最好的主意。
无论如何,这些想法对于从现有样本生成新样本可能仍然有效,但我想需要一些改进。

2. 建模:

我和 @cody11null 基于上述想法构建了 CatBoost 和 Dart 模型。我们没有使用任何特征工程或选择,因为它们没有帮助。
@leehann@gauravbrills 基于相同的想法构建了 Dart 模型,但进行了特征工程和选择,基于相关性、方差和置换重要性选取前 300 个特征,这对他们的 Dart 模型有效。
第一个 CB 和 Dart 的 CV 为 0.412,LB 为 0.419。第二个 Dart 的 CV 为 0.416,LB 为 0.421。
结合两者给了我们 CV 0.406 和 LB 0.416。
@leehann@gauravbrills 尝试了一些 tabnet、xgboost、聚类、嵌入的实验,但都没有起作用。
对于我和 @cody11null,我们采用了几种技巧:

  • 从目标中减去 AdvantageP1:
    我们正在预测以下内容:
    Target Formula
    AdvantageP1 与目标相关性很高对吧?所以,让我们改为预测 df["utility_agent1"] - df["AdvantageP1"],这给出了以下分布:
    Modified Target Distribution
    然后在预测时通过再次添加 AdvP1 来反转此操作。这实际上给我的 CV 带来了巨大的提升 ~0.02,LB 提升较小 <0.000xx。
  • 在最终集成中使用 AdvantageP1:
    AdvantageP1 代表“获胜概率”对吧?但我们预测的不是获胜概率。它是 胜 - 负。所以,很容易将 AdvantageP1 替换为:
train_df["AdvantageP1"] = (train_df["AdvantageP1"] - (1 - train_df["AdvantageP1"]))

使其匹配目标的构建方式。这给出了以下分布,与目标相似:
AdvantageP1 Distribution
这作为模型中的特征没有帮助,但将其添加到最终集成预测中将 CV 提高到 0.404,LB 提高到 0.415。

  • 使用 Wins/Losses 预测:
    我们没有直接预测 utility_agent1,而是尝试预测 wins/n 和 losses/n 然后减去它们。这也提供了巨大的 CV 提升 ~0.01,但 LB 更差为 0.421。我认为 AdvantageP1 可能是这个巨大提升的原因,因为(类似于最后一点)这里我们预测的是胜负,而 AdvantageP1 代表获胜概率。所以也许因为它们的含义和分布匹配,模型过于依赖它?实际上这是我得到最后一个改变原始 advp1 为差异的想法的地方,它最后起作用了,但这个 wins/losses 不同模型的想法没有起作用。
  • 使用 MultiRMSE:
    在 catboost 中使用 objective="MultiRMSE" 然后拟合:
y = pd.concat([y, -y], axis=1)

然后预测使用:

pr = model.predict(test)
pred = (pr[:, 0] - pr[:, 1]) / 2

它在 CV 上提升了 ~0.002,但在 LB 上结果稍差为 0.416。我们选择这个作为我们的第二个提交,它在私有榜上也给了 0.424。

  • 每个代理一个模型 (Model Per Agent):
    尝试为每个代理训练一个模型,CV 提升 ~0.002,但 LB 相同为 0.416。私有榜更差为 0.425。
  • 全量拟合 (Full fit):
    我发现对数据进行全量拟合给我的 LB 带来了 +0.001 的提升,这也转化到了私有榜。
  • 训练分类器:
    减少目标的唯一值,然后使用分类器,期望预测能得到连续值。对 CV 或 LB 没有帮助。
  • 伪标签 (Pseudo Labeling):
    在训练数据上训练我的 Dart,然后在测试集中每 100 个样本的集成预测上继续训练,并做出另一个新预测。对 CV 或 LB 没有帮助。
  • 优化集成权重:
    我们对所有模型的 a*(pred)+b 进行了优化,最后我们也优化了 clip 值 np.clip(pred,a,b)。这两者都是我们最终解决方案的一部分,在 CV 和 LB 上都给出了提升。
同比赛其他方案