🀄 Mortal Online 训练参数辩论庭
麻将 AI Mortal 的在线自对弈训练一共二十来个关键参数。这一页不直接给"标准答案",而是让 🔺 加大派 和 🔻 收紧派 就每个参数当庭对质,最后由 ⚖️ 裁决 给出单卡 RTX 4090 上的取值、设置原因、关联因素,以及调低 / 调高分别会发生什么。所有结论均对照 train.py / server.py / client.py 源码行为。
Online 训练闭环 —— 参数都作用在哪个环节?
辩论之前先把"法庭现场"看清楚。Online 阶段是三个进程通过 TCP(默认 127.0.0.1:5000)协作的闭环,每个参数都挂在闭环的某一段上——知道它挂在哪里,才知道它调错了会卡住谁。
🗄️ server.py (参数服务器 + 样本缓冲,纯 CPU)
- 保管最新网络参数(带
param_version) buffer_dir攒 client 回传的牌谱- 缓冲达 capacity → 拒发参数(反压)
- trainer 来 drain → 整仓搬到
drain_dir
🎮 client.py (自对弈采样,可多开)
- 轮询拿最新参数(拿不到 sleep 3s)
- trainee(Boltzmann 探索) vs 3×baseline(贪心)
- 每轮打 games 场,牌谱打包回传
- 打印最近 history_window 轮滑动战绩
🏋️ train.py (online=true,占大头显存)
- drain 到新牌谱 → 只学
trainee视角样本 - DQN MSE + 辅助任务(无 CQL,BN 冻结)
- 每 submit_every 步推新参数给 server
- 每 save_every 存档 / test_every 评估
train.py 的主进程只负责拉起训练子进程。每次 test_play 评估结束后子进程会 sys.exit(0)(规避一个官方注释里写明的卡死 bug),父进程 3 秒后自动重启并从 checkpoint 续训。周期性重启是正常现象,不是崩溃。best.pth 同时作为 online 起点和 baseline 对手;② 三个进程同挤一张 24GB 的 4090;③ 麻将是超高方差环境——很多参数的裁决最终都会回到这一点。训练节奏五参数 —— batch、累积、存档、推送、评估
这五个参数决定 trainer 的"心跳"。它们之间有两条硬性断言锁死:save_every % opt_step_every == 0 且 test_every % save_every == 0,改任何一个之前先想想另外两个。
offline 我们用 512 甚至 1024 都没问题,4090 有 24GB!大 batch 梯度更稳,40 层 ResNet 的算力利用率也更高。online 数据本来就是自对弈产生的噪声数据,更大的 batch 能平掉一部分方差,凭什么要砍半?
因为这张卡不是你一个人的。同一张 4090 上还跑着 client 的推理(trainee + baseline 两套网络)和每 test_every 步一次的 test_play。峰值撞到一起就是 OOM,而且 online 一轮 drain 只有约 capacity=1600 场牌谱、几十万个样本,大 batch 意味着一轮没几个 step,参数推送频率跟着变稀。
收紧派赢在"同卡三进程"这个硬约束上。取 256,同时用 opt_step_every=2 做梯度累积,等效 batch 512——梯度平滑度不输 offline,显存峰值却砍半,给 client 留出 3–4GB。这是"想要大 batch 的效果、付小 batch 的显存"的标准解法。若把 client 挪去第二台机器,这里可以直接回 512。
设成 4 甚至 8,等效 batch 上千,梯度质量拉满,还能进一步把单步 batch_size 压到 128 省显存。
累积不是免费的:optimizer.step() 变稀疏后,同样 400 步 save_every 里实际参数更新只有 400/N 次。online 的意义就是"边采边学",参数更新太稀,client 拿到的永远是几乎没动过的网络,闭环退化成昂贵的空转。而且 scheduler.step() 是每个 batch 都走的,累积过深还会让 lr 调度与真实更新错位感变强。
只为配合 batch_size=256 凑出等效 512 而设,够用就停。注意断言 save_every % opt_step_every == 0:save_every=400 能被 2 整除,合法。若显存宽裕(client 上了别的机器),首选做法是直接加 batch_size 而不是加深累积。
存 checkpoint 要序列化整套模型 + AdamW 动量,几百 MB 往磁盘写,太频繁纯属浪费 IO。设 2000 步存一次足够。
你忘了 online trainer 每次评估后会 sys.exit(0) 重启?重启是从最后一个 checkpoint 恢复的,save_every 就是崩溃 / 重启时最多丢多少进度。而且 TensorBoard 的 loss、Q 值直方图也是这个粒度才写一次,2000 步一个点根本没法盯训练健康度。
沿用官方示例值。400 步 × 256 batch ≈ 10 万样本存一次档,IO 占比可忽略,而重启丢失窗口和监控粒度都在舒适区。它还是 submit_every 的锚:源码在 steps % save_every == 0 时若与 submit 不同步会补推一次参数,两者取相同值最省心。
这可是 online 训练的灵魂参数!推送越频繁,client 采样用的策略越接近 trainer 正在优化的策略,off-policy 偏差越小。理论上每次 optimizer.step() 后都该推,设 100 步推一次不过分吧。
每次推送要把 40 层 ResNet + DQN 的 state_dict 全量序列化走一遍 TCP,server 端还要在锁里做 torch.save。更关键的是:client 一轮自对弈打 800 场要好几分钟,你 100 步推一次,client 一轮牌还没打完参数已经换了好几版——推了也没人用,纯烧 CPU。推送频率超过 client 的消费频率就是无效功。
推送节奏应当匹配 client 打完一轮的耗时,而不是越快越好。单卡上 trainer 跑 400 步与 client 打完一轮 800 场的耗时量级接近,一轮一版参数刚好衔接。另外源码保证 save 时若错过 submit 会补推一次,二者同值可以让"存档的模型"和"client 在用的模型"始终一致,排查问题时少一个变量。
online 训练最怕悄悄变弱(自对弈会放大策略的自我偏食)。评估间隔太长,模型走偏了几万步才发现,白烧几天电。2000 步一评,早发现早止损。
一次评估 = 3000 场对局的纯推理 + 完事后进程重启重建 DataLoader,在 4090 上是十几分钟量级的纯停训时间。2000 步一评,评估耗时能占到总墙钟的两三成。而且 400 步内策略根本没变多少,评了也是噪声——麻将 3000 场的置信区间本来就不小。
评估的价值在于"策略变化量足够大,胜率差异能盖过 3000 场的统计噪声"。按 submit_every=400 算,8000 步意味着 client 已经用了约 20 版参数、贡献了几万场新对局——这时候的评估才测得出真实进步。折中于官方示例 offline 用的 20000 和评得勤派的 2000 之间,因为 online 阶段确实更需要盯退化。
探索策略 —— trainee 该多"浪"?
client 里 trainee 用 Boltzmann 采样探索,三个 baseline 永远贪心。探索参数是 online 阶段数据质量的总开关:探索不足学不到新东西,探索过头牌谱全是昏招。这些参数放在 [train_play.<profile>] 段,可用环境变量 TRAIN_PLAY_PROFILE 让不同 client 用不同探索档位。
RL 没有探索就是自嗨。ε 才 0.5%?DQN 传统 ε-greedy 都从 1.0 退火到 0.05 呢。不探索,trainee 永远在自己 Q 值的舒适区里打转,那些"offline 数据里人类不打、但其实更优"的招永远发现不了。
麻将不是 Atari。一局 East-South 战每个玩家约有上百个决策点,ε=0.05 意味着每局平均好几次故意打偏——切错一张牌就可能直接放铳 12000 点,整局的 MC 回报全被这次探索污染,而这个被污染的回报会回传给这局所有正常决策的动作。稀疏大惩罚环境里,探索的代价是全局性的。另外我们不是从零训练:offline 已经给了一个人类水平的先验,需要的是精修不是撒网。
取 0.005 ≈ 每局在1 次以下的探索动作,加上 Boltzmann 本身按 Q 值加权(不是均匀乱选),偏差动作大概率也是"次优而非昏招"。这是微创探索:既保留发现新招的通道,又把回报污染控制在单局可承受范围。想要更多探索,正确姿势是多开一个 TRAIN_PLAY_PROFILE=explore 的 client 专门产高探索数据,而不是把主力 client 调浪。
T 太低的话 softmax(Q/T) 几乎还是 argmax,所谓探索就是走个形式。T=1 才能让次优动作真正有出场机会。
看看 Q 值的量纲:回报是 pts=[6,4,2,0] 尺度下的期望 pt 增量,动作间的 Q 差常在 0.1 以下。T=1 时这点差异被 softmax 抹平,等于在合法动作里近似均匀乱抽——包括那些 Q 值垫底的自杀切牌。探索应该在"好动作的邻域"里,不是掀桌子。
低温派对量纲的观察是决定性的:T=0.05 把 0.1 的 Q 差放大成 softmax 里 e² ≈ 7 倍的概率比,效果是"在前几名动作之间摇摆"——恰好是我们要的"探人类先验附近的未开发区"。T 与 ε 要一起看:ε 定频率、T 定幅度,两个都激进 = 数据报废;一收一放才是可控探索。
加一道保险:top_p=0.9 把概率长尾里的极端昏招直接排除,探索永远不会打出 Q 值垫底那几张牌,牌谱质量有下限保证。
T=0.05 的低温 softmax 已经让尾部动作概率指数级衰减到近乎为零,再加 top_p 是双重保险叠罗汉,实际行为几乎不变,还多一个要解释的超参。真出问题时你分不清是 T 的锅还是 p 的锅。
低温已提供实质截断,保持 1.0 少一个自由度。它的真正用武之地是高温 profile:如果你专门开一个 T=0.2 的高探索 client,配 top_p=0.8 能在"探得野"和"不自杀"之间画出安全区——届时再动它。
批量推理才有 GPU 效率,一轮多打点,牌谱管够,trainer 永远不会饿着等数据。而且 800 场的战绩统计比 200 场稳得多。
一轮打得越久,这批数据的"策略年龄"越老——第 800 场还在用轮初拿的参数,而 trainer 那边可能已经推了两版新的。games 本质上是数据新鲜度和采样效率的兑换率。
800 场 = 200 个种子 × 4 座轮换(duplicate 制,同一副牌四个座位各坐一遍,砍掉配牌运气的方差)。在 4090 上一轮耗时与 trainer 跑 400 步(= submit_every)大致同步,闭环节奏最顺。多开 client 或多机采样时,可以把单 client 的 games 减半,让参数消费更细粒度。
同一副牌用不同版本的参数各打一遍,天然的对照实验!还能让模型把一副牌"打透",类似课程学习。
训练数据的多样性是 RL 的生命线。反复喂同一批配牌,模型会记住"这副牌该这么打"而不是学会普适牌理;何况天凤种子空间近乎无限,没有理由省着用。
默认不重复。repeats 的合理场景是调试(固定种子对比两版参数的行为差异)或算力极度受限时摊薄种子生成成本——正常训练保持 1,让每轮都是新牌局。
数据管道与反压 —— server 怎么当好中间商
server 是闭环的流量阀门。这一章的参数决定"采样→训练"的耦合松紧:太松 = trainer 学陈年旧账,太紧 = 两边互相罚站。
缓冲当然越大越好:client 永远不被反压卡住,GPU 采样端满负荷运转,trainer 每次 drain 都有充足的大批量数据,吞吐拉满。
capacity 的真实身份是数据陈旧度的上限。trainer 一次 drain 拿走整仓,仓越大,这批数据里最老的牌谱离当前参数越远。Mortal 用的是 MC 回报 + 当前策略拟合,吃太陈的数据就是在拟合一个已经不存在的策略的价值。反压不是 bug,是让采样端"等一等学习端"的设计。
取一轮自对弈产量(800)的两倍:允许 client 在 trainer 消化时先打完下一轮不被卡住(管道并行),但攒不满第三轮——数据年龄被压在"两版参数以内"。扩多 client / 多机采样时按 2 × 所有 client 的单轮总产量 等比放大。
true 才是教科书式的同步 RL:每批数据都严格由"上一版训练完成的参数"产生,零陈旧、零版本混杂,实验完全可复现,调参归因清晰。
代价是什么?trainer 训练时 client 罚站,client 采样时 trainer 罚站——同一张 4090 上两个角色轮流干瞪眼,等效算力直接砍半。我们已经用 capacity=2×games 把陈旧度压到两版参数以内了,这点软陈旧换一倍吞吐,稳赚。
单卡训练时间是最贵的资源,选流水线并行。什么时候切 true?做严格的消融实验(比如对比两组探索参数的收敛曲线)时,串行模式消除了"数据年龄"这个混杂变量,结论更干净——跑正式训练再切回 false。
两个免辩论的速裁参数
| 参数 | 裁决 | 理由与后果 |
|---|---|---|
| [online].history_window | 50 | 只影响 client 日志里滑动平均战绩的窗口,完全不参与训练。50 轮 × 800 场 = 4 万场的滑动窗口,平均顺位的噪声约 ±0.02,足以判断趋势。设太小(5)曲线抖成心电图看不出涨跌;设太大(500)对"最近变强了吗"反应迟钝。纯监控参数,按口味调。 |
| [dataset].num_workers | 1(online 阶段) | offline 我们开 4–8 个 worker 喂饱 GPU,但 online 每轮 drain 只有约 1600 场牌谱,几个 worker 分不到几个文件,反而多付进程启动 + GRP 模型每 worker 各加载一份的开销(DataLoader 每个 epoch 都重建!)。设高了不会错,只是白费内存和启动时间。 |
| [online.server].sample_reuse_rate / threshold | 0 / 0 | 诚实说明:这两个键在 config.example.toml 里存在,但当前版本 server.py 并未读取使用(源码只读 buffer_dir / drain_dir / capacity / force_sequential)。保留默认 0 即可,别指望调它们生效。 |
优化器与稳定性 —— 别把 offline 学的东西训没了
最容易翻车的一章。核心矛盾:offline 起点是一个已经很强的策略,online 的自对弈数据却又少又噪——学猛了毁掉先验,学怂了原地踏步。还有一个源码细节垫底:从 offline checkpoint 切到 online 时,train.py 检测到模式变化,不会加载 optimizer/scheduler 状态,学习率完全按本章配置从零生效。
1e-5 也太怂了。offline 用 1e-4 都稳稳的,online 数据是模型自己产的、分布更贴合当前策略,学习率不该更大胆点吗?小 lr = 龟速进步 = 电费白烧。
三点反驳。①offline 有百万级人类对局和 CQL 正则托底,online 每轮只有 1600 场自产数据且CQL 已关闭——正则少了,步长必须跟着收;②MC 回报在麻将里方差巨大,大 lr 会让单批噪声直接写进权重,几轮就把 offline 学的先验冲垮(灾难性遗忘);③自对弈有正反馈回路:策略走偏 → 产出偏数据 → 更偏,大 lr 是这个回路的放大器。
online 本质是精调(fine-tune)而不是从头学,取 offline 峰值的 1/10。恒定而非退火:online 没有"训练集跑完"的概念,数据无限供给,不存在一个自然的退火终点;要降 lr 应该由你看着评估曲线手动改配置重启(反正 trainer 本来就周期性重启)。warm_up 也不需要——权重不是随机初始化的,没有冷启动震荡问题。
BN 统计量跟着数据走才是 BN 的本意。自对弈数据分布确实和人类牌谱不同,冻着不更新,网络永远用"人类牌谱时代"的均值方差做归一化,这不是刻舟求剑吗?
问题恰恰在于 online 的数据分布是非平稳的——它随着策略更新每轮都在漂。BN 统计量追着一个移动目标跑,会造成三重伤害:①训练端归一化基准持续抖动,等效于给每层输入注入噪声;②client 推理用的是 running stats,统计量一变,同一套权重的行为就变,战绩曲线失去可比性;③策略偏移 → 分布偏移 → BN 偏移 → 输出偏移,又一条正反馈回路。config.example.toml 的注释直接写着 "true in online training"——作者用真金白银的算力踩过这个坑。
这是 online 与 offline 在训练语义上的两大区别之一(另一个是 CQL 自动关闭)。冻结的只是 running statistics,BN 的 γ/β 仿射参数仍在正常学习——表达能力还在,只是归一化基准锚定在 offline 时代的大数据统计上,稳定压倒一切。
官方 example 就是 0!裁剪会偏置梯度方向,AdamW 本身自带二阶矩自适应,天生对梯度尺度不敏感,再裁一刀是画蛇添足。
AdamW 的自适应对"持续的尺度差异"有效,对突发的单批异常没那么快——二阶矩是指数滑动平均,需要几步才追上。而麻将回报是重尾的:一批样本里出现一局役满(±32000 点级的回报),MSE 损失的梯度瞬间放大几十倍。offline 有百万样本把这类事件摊薄,online 一轮才 1600 场,重尾事件占批次的比例高得多。clip 是专防这种单点雷击的保险丝。
这是全页唯一主动改离官方 example 的参数,理由是 online 小批量 + 重尾回报 + 无 CQL 的组合放大了单批雷击风险。1.0 是"平时摸不到、雷击时兜得住"的宽松阈值——正常训练梯度范数远小于 1,裁剪几乎不触发,不会引入不裁派担心的方向偏置。如果 TensorBoard 显示 loss 从无尖刺,想回 0 也无妨。
三个速裁参数
| 参数 | 裁决 | 理由与后果 |
|---|---|---|
| [optim].weight_decay | 0.1(与 offline 一致) | 只作用于 Linear/Conv1d 的 weight(源码手工分组)。保持与 offline 相同——正则强度突变本身就是一种分布冲击。设 0 权重会缓慢膨胀,长训后过拟合自对弈风格;设 1.0 会持续把 offline 学到的权重往原点拉,变相遗忘。 |
| [cql].min_q_weight | 5(保留但不生效) | 源码 if not online: cql_loss = ... —— online 下 CQL 项根本不计算。为什么关?CQL 惩罚"数据分布外的动作高 Q",offline 用它防止脱离人类先验;online 的数据就是当前策略自己产的,继续压分布外动作等于禁止探索成果被利用。留着这个键只为切回 offline 时不用改配置。 |
| [aux].next_rank_weight | 0.2(与 offline 一致) | 辅助任务:让编码器顺带预测本局结束后的顺位,给表征提供密集监督信号,缓解 MC 回报稀疏的问题。online 数据虽噪,顺位标签仍然是真实的,继续留着。设 0 丢失正则效果;设过高(1.0+)编码器被辅助任务绑架,Q 学习沦为副业。 |
奖励与评估 —— 模型到底在优化什么
奖励参数是所有参数里"看起来最无害、实际上最定调"的:它们直接定义了模型的价值观。评估参数则决定你敢不敢相信自己看到的曲线。
要上天凤打段位战,就该用真实 pt 表,比如特上桌的 [90, 45, 0, -135]!四位的惩罚是一位收益的 1.5 倍,模型必须从奖励里学会"避四大于争一"。用等差的 [6,4,2,0],模型对掉到四位的痛感不够,打出来的是"敢打敢拼但偶尔暴毙"的风格。
非对称 pt 表在训练早期是毒药:避四的梯度信号压倒一切,模型会学出畏缩的弃和风格,进攻能力萎缩。而且 [90,45,0,-135] 的数值尺度大了一个数量级,Q 值量纲跟着变,boltzmann_temp、mse loss 的绝对值全得重调。等差 pts 让四个顺位的信号均衡,先把"普适麻将力"练扎实。注意 test_play 显示用的 [90,45,0,-135] 是写死的展示口径,和训练 pts 无关。
课程式安排:online 主训练期用等差 pts 打基本功;确定目标场况后,末期把 pts 换成目标 pt 表的等比缩小版(如 [0.9, 0.45, 0, -1.35])做短期微调,让策略向"避四"偏置。切换 pts = 切换奖励函数,等于换了优化目标,切换后前几次评估波动属正常,别急着回滚。
RL 教科书说 γ<1 能降低远期回报的方差,还能表达"眼前的牌效优先于虚无缥缈的终局"。设个 0.99 稳一点。
信用分配的脏活已经被 GRP 干完了——奖励不是"整场比赛终局才结算",而是每一局(kyoku)结算一次期望 pt 增量,回报延迟最多几十步。这几十步内,γ=0.99 会让局开头的配牌选择只拿到 0.99^70 ≈ 0.5 倍的信号——可麻将恰恰是"开局几张牌的取舍决定整局走向"的游戏,前期决策才最该拿满信用!
无折扣派的结构性论证成立:GRP 逐局塑形 + 局内不折扣,是"方差控制"和"信用完整"兼得的设计。γ 唯一该动的场景是诊断性实验(比如怀疑模型过度铺后期大牌),正常训练锁死 1。
麻将的单场方差臭名昭著,强 9% 的模型也能连输几百场。3000 场的平均顺位标准误差还有 ±0.02 左右,两版模型的真实差距经常就在这个量级里!要我说打 10000 场才配叫评估。
评估是纯开销:不产训练数据、占满 GPU、结束还触发重启。10000 场一次评估要一小时起步,配合 test_every=8000 意味着评估时间逼近训练时间的一半。评估的目的不是精确测出 Elo,是发现明显退化 + 筛选明显进步,3000 场(其中 duplicate 座位轮换已砍掉配牌方差)足够识别 0.05 级别的顺位变化。
训练内评估当"烟雾报警器",3000 场够灵敏;真要给两个 checkpoint 排座次,用独立的 one_vs_three.py 跑几万场慢慢打,不占训练时间。另外记住 best.pth 的更新判据是平均 pt 和平均顺位双双不劣于历史最佳——双指标 + 3000 场,已经能挡住大部分统计噪声的假新高。
最终配置与启动 —— 辩论结果汇总落地
全部裁决落进一份 config_online.toml。带 ★ 的行是辩论庭的核心裁决,其余为路径与常规项。
[control] version = 4 online = true # ★ 在线模式总开关 state_file = '/data/mortal/online/mortal.pth' # 起点 = offline 的 best.pth 副本 best_state_file = '/data/mortal/online/best.pth' tensorboard_dir = '/data/mortal/tb/online' device = 'cuda:0' enable_cudnn_benchmark = true enable_amp = true # 4090 tensor core,省显存提吞吐 enable_compile = false batch_size = 256 # ★ 第 1 章:给同卡 client 留显存 opt_step_every = 2 # ★ 梯度累积,等效 batch 512 save_every = 400 # ★ 存档/监控粒度;须被 opt_step_every 整除 test_every = 8000 # ★ 20×save_every;评后子进程重启属正常 submit_every = 400 # ★ 与 client 单轮耗时对齐 [train_play.default] games = 800 # ★ 200 种子 × 4 座 duplicate log_dir = '/data/mortal/online/train_play' boltzmann_epsilon = 0.005 # ★ 微创探索:每局约1次以下 boltzmann_temp = 0.05 # ★ 低温:只在好动作邻域内摇摆 top_p = 1.0 # 低温已自截断,不再叠保险 repeats = 1 [test_play] games = 3000 # ★ 烟雾报警器精度;精评走 one_vs_three log_dir = '/data/mortal/online/test_play' [dataset] # online 不读 globs,但该段仍被加载 globs = ['/data/dataset/**/*.json.gz'] file_index = '/data/mortal/file_index.pth' file_batch_size = 15 reserve_ratio = 0.0 num_workers = 1 # ★ 每轮数据量小,多 worker 是负收益 player_names_files = [] num_epochs = 1 enable_augmentation = false augmented_first = false [env] gamma = 1 # ★ GRP 已逐局塑形,局内不再折扣 pts = [6.0, 4.0, 2.0, 0.0] # ★ 训练期等差;冲段前再切非对称微调 [resnet] # 必须与 offline 完全一致,否则权重加载失败 conv_channels = 192 num_blocks = 40 [cql] min_q_weight = 5 # online 下不参与计算,留作回切 offline [aux] next_rank_weight = 0.2 # 顺位辅助任务,密集监督信号 [freeze_bn] mortal = true # ★★ online 必须冻结 BN,头号隐形杀手 [optim] eps = 1e-8 betas = [0.9, 0.999] weight_decay = 0.1 max_grad_norm = 1.0 # ★ 防役满级回报的单批雷击(偏离官方示例) [optim.scheduler] peak = 1e-5 # ★ offline 峰值的 1/10;模式切换时优化器重置 final = 1e-5 warm_up_steps = 0 # 非随机初始化,无需预热 max_steps = 0 # peak==final ⇒ 恒定学习率 [baseline.train] # client 的自对弈对手 device = 'cuda:0' enable_compile = false state_file = '/data/mortal/online/baseline.pth' [baseline.test] # trainer 的评估对手 device = 'cuda:0' enable_compile = false state_file = '/data/mortal/online/baseline.pth' [online] history_window = 50 # 纯监控:滑动战绩窗口 enable_compile = false [online.remote] host = '127.0.0.1' # 多机采样:server 改 0.0.0.0,client 填其 IP port = 5000 [online.server] buffer_dir = '/data/mortal/online/buffer' # server 启动会清空重建! drain_dir = '/data/mortal/online/drain' sample_reuse_rate = 0 # 当前版本 server 未使用 sample_reuse_threshold = 0 capacity = 1600 # ★ 2×games:陈旧度 ≤ 两版参数 force_sequential = false # ★ 流水线并行;做消融实验时才切 true [grp] state_file = '/data/mortal/grp.pth' # 与 offline 同一个 GRP [grp.network] hidden_size = 64 num_layers = 2
启动三部曲
# 准备:用 offline 成果初始化(起点与对手是同一个模型) mkdir -p /data/mortal/online cp /data/mortal/best.pth /data/mortal/online/mortal.pth cp /data/mortal/best.pth /data/mortal/online/baseline.pth cd $MORTAL_ROOT/mortal # 终端 1:参数/样本服务器(纯 CPU,最先启动) MORTAL_CFG=config_online.toml python server.py # 终端 2:训练器(启动即提交初始参数,然后阻塞等样本) MORTAL_CFG=config_online.toml python train.py # 终端 3:自对弈采样器(可多开;高探索副本用 profile 区分) MORTAL_CFG=config_online.toml python client.py # TRAIN_PLAY_PROFILE=explore MORTAL_CFG=config_online.toml python client.py
4090 显存分账
| 进程 | 占用内容 | 估算 |
|---|---|---|
| trainer | 训练态 Brain+DQN+Aux、AdamW 一二阶矩、batch 256 AMP 激活 | ~6–10 GB |
| trainer 内 test_play | 评估期额外的推理态 challenger + baseline | ~2–3 GB(峰值期) |
| client | 推理态 trainee + baseline(AMP 推理) | ~3–4 GB |
nvidia-smi 稳态,确认峰值有余量后再逐步上调 batch_size。收敛后做什么:轮换 baseline
best.pth 复制为新的 baseline.pth,重启三进程,让模型和"更强的自己"继续对弈——这就是 online 阶段的版本迭代循环。别太频繁(评估噪声),也别太久不换(打赢弱对手学不到东西),以评估曲线连续两三次创新高为节奏。