返回列表

8th Place Solution - Imitation Learning

645. NeurIPS 2024 - Lux AI Season 3 | lux-ai-season-3

开始: 2024-12-09 结束: 2025-03-24 游戏AI AI大模型赛
第 8 名解决方案 - 模仿学习

第 8 名解决方案 - 模仿学习

作者: Gregor Lied
发布时间: 2025-04-21
竞赛排名: 第 8 名

引言

首先,我要感谢 Lux AI 和 Kaggle 团队组织了这次比赛。我特别喜欢新功能,例如动态环境和战争迷雾 (FoW) 的存在。我期待在(希望有的)即将到来的 Lux AI 第四季中与大家一起竞争!!

源代码 available here: https://github.com/gregorlied/lux-s3/tree/main

概述

我的解决方案是一种基于模仿学习 (IL) 的方法,使用了来自 Flat Neurons 和 Frog Parade 团队的比赛片段。

与 aDg4b 和 YumeNeko 团队类似,我采用了两个不同的 UNet 模型:

  • 动作模型 (Action Model): 一个地图级别的 UNet,同时预测所有单位的动作。
  • 吸取模型 (Sap Model): 一个单位级别的 UNet,预测单个单位在执行吸取动作时的目标。

模型是在包含来自 10 个不同代理的 11681 个比赛片段的数据集上训练的。

然而,不同的代理可能有不同的动作分布,这在训练过程中引入了不稳定性。此外,模型架构本身没有利用地图对称性,导致数据使用效率低下。为了解决这些问题,我在训练和推理过程中应用了两个技巧:

  • 代理 ID 条件化 (Agent ID Conditioning): 模型在 UNet 的瓶颈层和预测头处都基于代理 ID 进行条件化。在推理过程中,模型被条件化为模仿 Frog Parade 团队中最好的代理。
  • 游戏状态归一化 (Game State Normalization): 如果游戏是以玩家 1 的视角进行的,游戏状态会沿主对角线镜像,以确保游戏始终以玩家 0 的视角进行。

由于存在战争迷雾 (FoW) 和隐藏的奖励节点,维护全局游戏状态至关重要。该状态在每一回合用观察到的局部信息进行迭代更新。对于奖励节点检测,我使用了 Relicbound 中提出的算法。

数据选择

我策划了一个包含 10 个不同代理的 11681 个比赛片段的数据集,仅选择了代理赢得游戏的片段。对于这些片段,我只保留了代理获胜的比赛。

由于 Boey 和 aDg4b 团队的代理一旦游戏达到决定状态就会停止操作,我从数据集中移除了这些比赛。在这些情况下,动作分布与未决游戏中的动作分布显著不同,可能会降低模型性能。

地图级别特征

基于维护的全局游戏状态,我构建了 18 个地图级别特征。

特征 可能值 备注
my_unit_positions {0, 1} 是否有我方单位位于该单元格 (1 是,0 否)
my_unit_energy [0, 1] 每个单位的归一化能量水平
my_unit_sap_range_map {0, 1} 该单元格是否在我方单位的吸取范围内 (1 是,0 否)
opp_unit_positions {0, 1} 是否有对方单位位于该单元格 (1 是,0 否)
opp_unit_energy [0, 1] 每个单位的归一化能量水平
is_visible {0, 1} 该单元格是否对我方单位可见 (1 是,0 否)
energy -1 或 [0, 1] 单元格的归一化能量水平 (未知单元格为 -1)
curr_tile_type_empty {-1, 0, 1} 该单元格是否为空地 (1 是,0 否,-1 未知)
curr_tile_type_nebula {-1, 0, 1} 该单元格是否为星云地块 (1 是,0 否,-1 未知)
curr_tile_type_asteroid {-1, 0, 1} 该单元格是否为小行星地块 (1 是,0 否,-1 未知)
next_tile_type_empty {-1, 0, 1} 漂移更新后该单元格是否为空地 (1 是,0 否,-1 未知漂移/单元格)
next_tile_type_nebula {-1, 0, 1} 漂移更新后该单元格是否为星云地块 (1 是,0 否,-1 未知漂移/单元格)
next_tile_type_asteroid {-1, 0, 1} 漂移更新后该单元格是否为小行星地块 (1 是,0 否,-1 未知漂移/单元格)
dist_from_center_x [0, 1] 相对于 x 轴距中心的距离
dist_from_center_y [0, 1] 相对于 y 轴距中心的距离
relic_nodes {-1, 0, 1} 该单元格是否为遗迹节点 (1 是,0 否,-1 未知)
reward_nodes {-1, 0, 1} 该单元格是否为奖励节点 (1 是,0 否,-1 未知)
reward_map [0, 1] 归一化奖励地图
特征地图展示

全局特征

基于维护的全局游戏状态,我构建了 32 个全局特征。

特征 可能值 备注
steps [0, 1] 归一化游戏步数
match_steps [0, 1] 归一化比赛步数
next_map_update -1 或 [0, 1] 距下次地图更新的归一化步数 (-1 未知漂移)
unit_move_cost [0, 1] 归一化移动消耗
unit_sap_cost [0, 1] 归一化吸取消耗
unit_sap_range [0, 1] 归一化吸取范围
unit_sensor_range [0, 1] 归一化传感器范围
all_relics_found {0, 1} 是否所有遗迹节点都已发现 (1 是,0 否)
all_rewards_found {0, 1} 是否所有奖励节点都已发现 (1 是,0 否)
gained_reward_last_X {0, 1} 我方代理在过去 X 回合是否获得奖励点数,X = 1, 3, 5, 10 (1 是,0 否)
reward_last_X [0, 1] 我方代理在过去 X 回合获得的奖励点数百分比,X = 1, 3, 5, 10
team_points [0..] 归一化队伍分数 (除以 500)
opp_team_points [0..] 归一化对方队伍分数 (除以 500)
nebula_tile_drift_speed_X {0, 1} 漂移速度是否为类型 X (1 是,0 否)
agent_id_X {0, 1} 代理 ID 是否为 X (1 是,0 否)

网络架构

UNet 架构 adopted from the 第一季第 6 名解决方案

由于不同的代理可能有不同的动作分布,我为每个代理使用了不同的预测头以稳定训练,这个想法最初是在 第一季第 4 名解决方案 中引入的。

在比赛的最后一周,我还尝试了类似于 Diffusion Transformer 架构的单一预测头。每个 DiT 块都基于代理 ID 进行条件化。

我的最终提交包含这两种网络架构的集成。

地图级别动作模型

  • 输入: 形状为 18x24x24 的体积
  • 输出: 形状为 6x24x24 的体积 (类别:中心,北,东,南,西,吸取)
  • 损失: 交叉熵损失 (Cross Entropy Loss)
  • 掩码: 训练和推理期间掩蔽所有没有单位的单元格
  • 超参数: 训练轮次 (Epochs) 8, 学习率 1e-3, 批次大小 128

单位级别吸取模型

  • 输入: 形状为 18x24x24 的体积
  • 输出: 形状为 1x24x24 的体积
  • 损失: 加权 Dice 损失 (Weighted Dice Loss)
  • 掩码: 训练和推理期间掩蔽所有超出吸取范围的单元格
  • 超参数: 训练轮次 (Epochs) 4, 学习率 1e-3, 批次大小 128

提交版本 1 – v73a (最终排名:2035.6)

你可以在这里找到此提交:https://github.com/gregorlied/lux-s3/releases/tag/v73a

  • 动作模型: UNet + 独立预测头,UNet + DiT 预测头
  • 吸取模型: UNet + 独立预测头

提交版本 2 – v73b (最终排名:2030.3)

你可以在这里找到此提交:https://github.com/gregorlied/lux-s3/releases/tag/v73b

  • 动作模型: UNet + 独立预测头,UNet + DiT 预测头
  • 吸取模型: UNet + 独立预测头,UNet + 独立预测头
同比赛其他方案