入职半年:游戏 AI 强化学习的实践笔记
前言
入职到现在半年多,我的工作基本围绕着同一件事:在一套大规模分布式强化学习框架上,为射击类对战游戏训练 Bot。这半年我写过状态特征、设计过奖励、拉过很多训练,也踩了不少坑。趁着记忆还热,我想把这段时间的方法和教训整理成一篇笔记。文中不涉及任何具体的项目、产品、地图和内部实现,只保留可以公开讨论的通用经验。
一、上手一套陌生的训练框架
刚进组的时候,我面对的是一套已经跑了很久的强化学习训练框架:环境侧有游戏服务器和协议,算法侧有特征、奖励、网络、训练编排,工程侧还有模型管理、推理服务和评估链路。一开始我习惯性地想先读完代码再动手,后来发现这条路很慢,也很容易读完就忘。
真正让我建立起整体认知的,是完整地跑通一次链路:从游戏服务器发来的协议里取出一个字段,把它写成状态特征,接进网络,配上一条奖励,拉起一次训练,产出模型,再把模型送进评估里看胜率和行为。这一圈走下来,框架里每一层的职责和边界就都清楚了。后来再接新需求,我基本都是按这条链路去拆解:这件事影响的是环境数据、特征、奖励、网络、训练编排,还是评估口径?
所以如果要给刚接触大型 RL 工程的人一个建议,我会说:不要试图先理解全部,先让一个最小的改动端到端地生效。
二、零和奖励:从「我打得好」到「我比对手打得好」
对抗类游戏的奖励通常只统计己方行为:我造成了多少伤害、击杀了几个人、死了几次。问题在于,这种奖励描述的是绝对收益,而不是相对优势。双方都打得很凶和双方都苟着不动,模型感受到的信号差异并不一定符合对局的真实胜负逻辑。
我做的第一件比较完整的工作,就是设计并实现了一套零和奖励机制。核心思路不复杂:对伤害、击杀、死亡这类对抗结果型事件,把敌方队伍的人均事件值作为扣减项引进来,让奖励反映双方的相对差值;而对移动、利用掩体、团队协同这类行为塑形型事件,保持原有逻辑不动。实现上采用了逐事件白名单加跨队事件缓存的方式,只让白名单内的事件参与零和计算。
之所以要做这个区分,是因为塑形奖励本来就是人为加的引导项,如果也做零和处理,等于把引导信号变成了噪声。
结论比我预想的更耐人寻味。数百局的对照评估显示,零和奖励基本保持了模型强度,但明显改变了动作分布——某些位移动作的使用频率和时长都有显著提升,在回放里肉眼可辨。而我原本期待的「减少无效进攻、降低苟活倾向」并没有实现。
这件事让我意识到:**奖励改造更容易改变模型的行为风格,而不是它的绝对强度。**如果目标是提升强度,光调奖励往往是不够的。
三、纯自博弈与联盟训练
联盟训练的常规做法是维护一个模型池,从池里按分布采样对手。这种方式稳定,但模型容易长期适应固定的历史快照,形成对特定对手的过拟合。于是我做了一套纯自博弈的训练设定,让对手按比例直接使用当前正在训练的模型。
几个印象比较深的结论:
- 从零开始的纯自博弈,几个小时就能形成基础的对战能力,并且会自发出现角色分化和绕后一类的战术行为,完全没有人工引导;
- 多版本之间的胜率矩阵显示模型能力在训练过程中稳步提升,较优的自博弈模型对已有的基线模型能取得相当可观的胜率;
- 自博弈对多样化策略有一定泛化能力,但训练后期存在退化风险——模型可能通过降低命中率、拖长对局这类方式去刷累计奖励;
- 模型强度必须区分「基础能力」和「战术策略」,单一胜率数字是会骗人的:一个模型可能只是克制了某类对手,而不是真的更强。
在联盟训练这边,我维护过多条并行的训练主线,也完整走了一遍天梯评估、Elo、风格画像和能力雷达的流程。评估体系的价值在这里体现得很明显:没有一套多维度的评估,你很难判断一次训练到底是变强了,还是只是换了一种打法。
四、躲藏—追击:一次非对称交替训练
有一类场景对 Bot 特别困难:对手躲在建筑里不出来,需要主动搜索、上楼、跨楼层接敌。直接训练往往学不会,因为正反馈太稀疏。
我们设计了一组非对称的交替训练:hide 一方只保留存活的时间正奖励和掉血惩罚,seek 一方则面对一个持续变强的躲藏对手。两边轮流迭代。结果是 seek 模型确实学会了上楼搜寻并歼灭对手;更有意思的是,hide 模型虽然没有任何战斗相关的奖励,却涌现出了基础的还击行为。
这个实验里最有价值的其实是中间那次排障。开启团队奖赏之后,训练曲线上的 reward 一路增长,但胜率十几个小时纹丝不动。我用消融的方式定位问题:分别跑全量团队奖赏、筛选后的子集、以及全部置零三组实验,最后确认只保留必要的那部分团队奖赏,模型就能稳定上楼击败躲藏方,平局率压到了 1% 以下。原因是部分团队奖赏被模型过度利用了——它找到了一种既能拿到奖励、又不必真正取胜的活法。
所以有两条经验我现在会反复提醒自己:reward 曲线上涨不等于能力提升;定位奖励相关的退化,消融几乎是唯一可靠的手段。
顺带一提,我们还通过对照实验推翻了一个直觉假设:占据高处对人类玩家可能是优势,但对当前的 AI 不一定天然构成优势。以后再听到「这个战术对 AI 应该有用」,我都会先要一组对照数据。
五、支持一个新玩法:从 0 到 1 的工程账
后来组里的重心转向了一个新玩法——一种以争夺控制点为目标的多人对抗模式。我成了这个方向算法实验的主要负责人,也第一次体会到「支持一个新模式」远不只是改改奖励那么简单。这半程做的事情大致是:
- 环境与数据:打通游戏服务器到训练侧的协议链路,补齐地图的静态资产(障碍物轮廓、楼层与房间栅格、楼梯、高度图等);
- 状态特征:新增当前激活的控制点、点位归属状态、双方占领进度、全场比分、到点位边缘的距离、换点倒计时等特征;
- 团队共享信息:把「队友是否在点内」接进已有的队友注意力模块;
- 奖励风格:以 overlay 的方式实现了进攻抢点、占据制高、游走牵制、隐蔽偷点四种风格,并配套了贡献门控、争夺限时、归属落定即停发等防刷分设计;
- Episode 切分:把一整场对局拆成「点级」的 episode 并提前结算,消除跨点的信用分配问题,也顺手消灭了「领先之后拖时间刷奖励」这种退化激励;
- 胜负口径:改成按全场大比分判定,让训练目标和玩法目标对齐;
- 训练编排:支持同一支队伍挂载多套独立的策略网络、按槽位绑定不同风格、相同风格的槽位共享 Learner,并加了配置校验;
- 评估:支持按槽位指定对手、多模型同时 serving、按槽位出评估结果,建立了个人行为、团队点权、终态结果三类共二十余项指标。
算法上还独立做了一版多智能体全局 Critic(CTDE)的设计:在统一的全局表征上同时估计个体价值和团队价值,用加权后的优势去驱动策略更新,目的是让「为团队做贡献但个人收益不明显」的行为也能被正确评价。
训练层面,通过实验对四种风格做了筛选,最终形成了一套「多个抢点位 + 一个占制高」的阵容配置。这件事让我对「算法工作」的理解发生了变化:在真实业务里,能不能训出好模型,往往取决于环境数据是否干净、episode 定义是否合理、评估口径是否可信,而不只是取决于损失函数写得对不对。
六、一些比较通用的体会
- **先跑通链路,再谈算法。**一个最小改动端到端生效带来的认知,比读三天代码更多。
- **奖励要分类型。**结果型事件和塑形型事件的处理方式应该不同,混在一起改,很容易引入噪声。
- **不要只看训练曲线。**reward 上涨、熵下降都不等于变强,最终要靠对照评估和回放里的实际行为来判断。
- **消融是定位退化最可靠的工具。**怀疑某组奖励有问题时,与其反复微调系数,不如直接置零跑一组。
- **警惕人类直觉。**对人类有效的战术,对 AI 不一定成立,要用实验说话。
- **简化有时比增加更有效。**去掉一些引导和约束之后,模型反而能自己找到合理的行为。
七、需要改进的地方
写总结如果只写做成了什么,那基本等于没写。这半年我自己比较清楚的几个问题:
一是工程纪律不够好。赶进度的时候容易攒一大批改动一起提交,自测和 Review 都不充分,风险都压在了最后。后面要坚持小步提交、及时关联需求。
二是实验记录质量不高。很多实验是先跑起来再想怎么解释,事后回看常常说不清当时改了哪些变量。正确的做法应该是在开始之前就把变量、对照组、验收指标和停止条件写清楚,并统一记录配置和模型版本。
三是有时候过于关注结果。时间紧的时候会牺牲过程校验和可复现材料,短期看是快了,但一旦结论被质疑,就得重新跑一遍,反而更慢。
四是结论的量化支撑还不够。胜率之外,还应该结合 Elo、行为指标、训练稳定性和回放观察,才能形成别人可以复核的结论。
从「熟悉框架、执行训练」到「拆解问题、设计方案、组织实验、输出结论」,这半年的变化大概就在这里。强化学习这件事,越做越觉得它一半是算法,一半是工程,剩下一半是耐心——虽然加起来超过了一,但确实是这么回事。