| status | active |
|---|---|
| created | 2026-06-18 |
| updated | 2026-07-02 |
| owners | |
| reviewers |
状态:active —— 诊断实验跑了一半(base + 臂 A 完成,臂 B/C 未跑)。 优先级提示(2026-07-02,纲领 §2.3 战略转向):Pendulum 已降级为非关键路径(真实目标为象棋 + 图像游戏,均不在连续动作轴上);本 issue 保持诊断态、不作发版门禁,value-head 坍缩根因待有余力或连续需求出现时再推进。文中 CartPole 数字为 pre-autograd-fix 旧口径(新基线见账本)。 审阅结论(2026-06-18):方法论成立——失败区间内不下组件裁决。已据此把两份 README 的 Pendulum
➖改回⏳(无信号),保留诊断旋钮,删除原始日志。 诊断更新(2026-06-18,dynamics 探针):base 150ep 跑 dynamics 诊断(对比 model 想象 vs 真实)——reward head 健康(预测 std 1.86 ≈ 真实 1.87),value head 坍缩成常数(预测 std 26 vs 真实 MC return std 175;episode 末尾真实剩余 −10 仍预测 −500)。病根精确锁定在 value 学习,不在 reward / 搜索 / 离散粒度——这也再次证伪「reward 分辨率头号嫌疑」。下一步:先区分 value target 本身坍缩(td_steps/gamma)vs head 学不动(网络/loss/lr),再对症,不盲改。 背景对话:本 issue 源于一次对话中关于"consistency 在 Pendulum 上是否为中性"的争论。原作者认为:当前 Pendulum 所有配置都在"失败区间"(−353 ~ −1445,门禁 −200),在模型根本没学会的任务上做组件消融,➖ 裁决全是噪声,不足以回答中性问题。故转向"先查清为什么学不会"。 关联文档:MyZero 总览 · Pendulum 详情 · RL 状态总览 代码位置更新(2026-06-18,v0.25 Phase 0/1 重构):MyZero 已统一进库src/rl/algo/my_zero/——模型在network.rs(原examples/my_zero/cartpole/model.rs,consistency 块在其train_unroll),训练循环 / dynamics 诊断在runner.rs,旋钮在config.rs::apply_env_overrides(原pendulum/main.rs的Hyperparams/ env 解析),动作离散化在action.rs。组件(consistency / value_prefix / n_step / support 等)已从 muzero/ez 吸收进 my_zero。本 issue 下文旧的model.rs:NNN/main.rs:NNN行号据此失效,对应逻辑见上述新文件。 value-head 容量诊断(2026-06-18,决定性新结果):库内单测my_zero::tests::value_head_capacity喂高方差、obs 可分的 value 目标,只训 repr+pred 的 value head——高/低价值组预测间隔从 1.77 训练到 14.00(真实间隔 14.0,精确拟合)→ §一 的分叉 (b)「head 学不动」证伪。坍缩来自上游(n-step target 构造 / 搜索),非 head 表达力。下一步探针:在 Pendulum 跑里实测 n-step target 的 std,与全程 MC std(≈175)、模型预测 std(≈26) 三者对比,定位是 target 截断(td_steps/gamma)还是搜索。 压测更新(2026-06-21):已接入与 CartPole 相同的 consistency + reconstruction + Sampled 栈(B=7 · K_eff=5 · sims=20 · γ=0.997 · r_scale=0.1);修复 Sampledpolicy_target投射 full action_dim(ep10 训练崩溃)。600ep / 120k env-steps:best greedy −942.2(门禁 −200 未达标)。详见 §十。 事实源锁定(2026-06-24):recipe.rs中 Pendulum 仍复用consistency + reconstruction + Sampled,但代码命名与文档口径统一为诊断栈,不是已验收 promote;DIAG=1诊断将输出 MC return / n-step target / search root / predicted value 的分布,作为下一步 P0 证据。 论文口径审计(2026-06-25):修复 Sampled MuZeroπ̂_β公式错误:应为(β̂/β)·π,不是β̂/(β·π)。复核 consistency 后确认negative_cosine_similarity()内部已对 target branchdetach(),原实现已有 stop-gradient;剩余差距是没有独立 EMA target encoder / target projector。旧 §十 压测结果基于修复前 Sampled 公式,后续需重跑。 transition discount 语义重构(2026-06-25):MyZero 将terminated / truncated / continuation作为基础 backbone 语义接入:真终止continuation=0,time-limit truncation 仍continuation=1并 bootstrap;Dynamics 学习 continuation,MCTS imagined edge 使用gamma * predicted_continuationbackup。该改动修正 terminal/truncation 闭环,但不宣称 Pendulum 已解决,仍需以 TD=5 诊断和 greedy eval 判读。 TD=5 + continuation 实测(2026-06-25):600ep / 120k env-steps 仍未达标;best greedy −1085.2 @ ep200,final greedy −1252.2。DIAG:MC return std 232.1,n-step(td=5) std 28.1,search root std 23.7,network root std 21.7;value 链路仍被压扁,continuation 语义闭环不是 Pendulum 的充分修复。 continuation 二值门修复实测(2026-06-25):search discount 由 softγ·c改 binaryγ·(1−done)后复跑(td=5 · 600ep / 120k env-steps):best greedy −959.3 @ ep250、final −1256.5,仍远未达标(门禁 −200)。解读:二值门移除了软折扣对健康边 value 的虚假压低(best 较 soft 的 −1085 略好),但 Pendulum 主瓶颈是上游 value-head 坍缩,非 search discount,故此修复在 Pendulum 上仅中性偏正、不构成充分修复。注:CartPole 同一修复把样本效率从 30.2k 修回 ~13.1k(确认软折扣是 CartPole 回归主因)。
MyZero 的"纯离散化 MCTS"方案,在 Pendulum-v1 上、num_simulations ≤ 50 的合理算力约束下,到底能不能学会?
这个问题之所以关键:pendulum/main.rs:5 的设计者注释早已埋下伏笔——
"忠实 Gumbel 连续搜索留作后续——仅当离散化触顶(达不到 SAC 水平)时才需要"
现在的实测在喊:离散化方案连 −200 的门禁都摸不到(最好 −929),这不是"触顶",是"没学会"。答案直接决定 MyZero 的技术路线:
- 如果能学会(调参/加大离散档数后能到 −200 甚至 SAC 的 ~−150):那 base vs +consistency 的对照才有意义,consistency 的 ✅/➖/❌ 才是可信裁决。
- 如果根本学不会:结论就不是"某组件中性",而是"纯离散化 MCTS 这条路在连续控制上走不通,必须提前上 Gumbel-root / 连续采样候选"——矩阵里 Gumbel-root 那一行要从"可选增强 ⏳"升级为"关键路径"。
请专家重点审:方法论(§三)、判读门限(§四)、已得负面结果(§五)是否支持上面的分叉判断。
为了让 A/B 干净(不每次重编译),给 examples/my_zero/pendulum/main.rs 加了 3 个环境变量旋钮。这些改动不动任何算法逻辑,只是把原本硬编码的 const 提升为可配置:
| 旋钮 | 原 const | 默认值 | 改动点 |
|---|---|---|---|
NUM_ACTIONS |
const NUM_ACTIONS: usize = 9 |
9 | main.rs:31-79 新增 Hyperparams 结构体 + from_env() |
RSCALE |
const REWARD_SCALE: f32 = 0.1 |
0.1 | 同上 |
LR |
无(固定 cfg.base.lr=0.02) |
0.02 | main.rs:644-648 加 env 覆盖(与 GAMMA/SIMS 同模式) |
附带:
idx_to_torque加num_actions参数;self_play_one_episode加reward_scale参数;run_one_training加hyper: &Hyperparams参数(纯参数透传,签名已#[allow(clippy::too_many_arguments)])。justfile加smoke-my-zero-pendulumrecipe(SMOKE 管线验证,已通过)。- 两份 README 的环境变量支持列表已同步。
改动已 cargo check + SMOKE 验证通过,未提交(git status 见工作区)。专家若认为方向不对,可整体 revert;若认可,可作为后续 sweep 的基础。
原作者的核心判断(请重点审这个逻辑是否成立):
CartPole 上 consistency 判 ✅,是因为它的价值是真实的:loss 从 9.6 掉到 0.7(一个数量级),avg_R 从 80 拉到 97。那是在"模型正在学习"的曲线上测出来的增益。
Pendulum 上现在判的
consistency ➖,它的真实含义不是"consistency 在 Pendulum 上无效",而是**"我连这个任务都没跑通,所以我看不出 consistency 有没有用"**。这两个结论差别巨大。
类比:10 分类任务,随机水平 10% 准确率。现在测 base/+dropout/+BN,准确率全在 9%~11% 之间晃——不能下结论说"这些 trick 都中性",只能说"模型根本没学到东西,消融毫无信息量"。
- 唯一成功判据:greedy(temp=0) eval 10 局均值。
- 排除:
avg_R(self-play 带 Dirichlet 探索噪声,永远偏低);loss(仅辅助看是否 NaN/爆炸)。 - 看 trajectory(轨迹形态:上扬/平直/振荡),不看单点终点。
| 怀疑点 | 状态 | 依据 |
|---|---|---|
| value support 溢出 | ✅ 排除 | support.rs:69 clamp(-20,20),变换域 [−20,20] 覆盖原始 value ±440;Pendulum 最差 value target ≈ −316(h=−17.0),不溢出 |
| sims 不够 | ⏸ 硬约束 | MuZero/EZ 上限就是 50 sims;用户领域知识:"不靠加算力解决"。9 动作 × 50 sims → 每根动作平均 ~5.5 次访问 |
| reward head 分辨率(RSCALE) | ❌ 实测证伪 | 见 §五臂 A,RSCALE 0.1→0.5 不仅没解锁学习,反而略差 |
CONSISTENCY 实现 |
见 §六,偏离 SimSiam 设计,但 CartPole 开 cons 反而学得好 → 非致命 bug,须 base 对照定性 |
新增确认根因(2026-06-18,dynamics 诊断):value head 坍缩——base 150ep 跑 dynamics 探针,value 预测 std=26 vs 真实 MC return std=175(坍缩成近似常数;episode 末尾真实剩余 −10 仍预测 −500);reward head 反而健康(预测 std 1.86 ≈ 真实 1.87)。病根锁定在 value 学习,上表 reward 分辨率「证伪」由此再获印证。待区分:value target 本身坍缩(td_steps/gamma)vs head 学不动(网络/loss/lr)。
看 greedy eval 轨迹最高点(排除 avg_R/loss):
| 轨迹最高点 | 含义 | 后续动作 |
|---|---|---|
| −800 ≈ "质的飞跃" | 该旋钮解锁学习,方法通 | 进 A/B 正式定性 |
| −600 | 接近门禁一半,强解锁 | 同上 |
| 持续在 −1000 附近徘徊 | 失败区间,该旋钮无效 | 排除该旋钮 |
用户原话:"分数至少要比前代高出 200 分左右。现在一直在 −1000 左右徘徊,能达到 −800 就是质的飞跃,−600 更好。"
决策树:
所有臂都在 [-1000 附近徘徊]?
├─ 是 → 确认"纯离散化 MCTS 在 Pendulum、sims≤50 约束下走不通"
│ → Gumbel-root 升级为关键路径
└─ 否,某臂触达 [-800, -600]:
该臂 vs base 的唯一差异旋钮 = 头号根因
→ 在该旋钮上做 base vs 该旋钮 A/B,正式定性 consistency / CQ
日志:原始训练日志不入库,关键 eval 数值已摘录在下方各臂表中。
⚠️ 方法学警示:均为单 seed,且 base 因命令传递问题实际跑了 ~300ep(非预设 300,见 §七坑点);臂 A 跑满 300ep。仅作 trajectory 形态判断,不作精确数值裁决。
18 个 eval 点(每 25ep 一次),greedy eval 轨迹:
−1714.7 → −1340.3 → −1366.9 → −1108.1 → −1074.9 → −1138.5
→ −1380.2 → −1214.0 → −1493.2 → −1321.1 → −1149.9 → −1433.7
→ −1367.9 → −1430.8 → −1253.0 → −929.6 → −1470.5 → −1421.6
- 最高点 −929.6(env_steps=80k);整体在 −929 ~ −1715 区间振荡。
- self-play avg_R 稳死在 −1100~−1213(平直);loss 稳死在
1617(没下降)。 - 裁决:失败区间确认(−1000 附近徘徊,远离 −800 门槛)。
12 个 eval 点:
−1327.7 → −1271.8 → −1251.6 → −1236.1 → −1138.6 → −1284.2
→ −1173.9 → −1348.1 → −1440.7 → −1271.5 → −1134.7 → −1312.9(终)
[benchmark] seed=42 env_steps=60000 wall_clock=755.5s greedy_eval=-1312.9
- 最高点 −1134.7(env_steps=55k);整体在 −1134 ~ −1440 区间。
- 裁决:失败区间,且比 base 略差(base 最高 −929,臂 A 最高 −1134)。
🔑 关键负面发现:原作者押注的"reward 分辨率是头号嫌疑"被证伪。RSCALE 0.1→0.5(reward head 有效原子从 ~1.6 格 → ~3 格)不仅没解锁学习,反而略差。这排除了"reward 表示域分辨率不足"作为主因——瓶颈在别处(dynamics 误差累积?模型容量?离散化结构本身?)。
原作者暂停在此,转而写本 issue 求审。
examples/my_zero/cartpole/model.rs:432-443(Pendulum 通过 #[path] 复用):
if consistency_coef > 0.0 {
if let Some(next_obs) = next_obs_list.and_then(|list| list.get(i)) {
let repr_target = self.repr.forward(&Tensor::new(next_obs, ...))?; // 对 next_obs 正常前向
let proj_target = self.projector.forward(&repr_target); // ← 同一个 projector
let proj_online = self.projector.forward(&next_latent); // ← 同一个 projector
let pred_online = self.predictor.forward(&proj_online);
let cons_loss = negative_cosine_similarity(&pred_online, &proj_target)?;
...
}
}与 SimSiam(Chen & He 2021)/ EfficientZero 标准实现的差异:
proj_target与proj_online走同一个self.projector。标准 SimSiam 是 online encoder + online predictor vs target encoder(stop-gradient,且 target encoder 是 online 的 EMA)。这里 target 路径没有独立的 target projector / EMA。- target branch 已有 stop-gradient:
negative_cosine_similarity()内部会对proj_target调用detach();因此这里不是“无 stop-gradient” bug,而是“无 EMA / 独立 target encoder”的简化实现。
但:CartPole 上开 CONSISTENCY 反而学得显著更好(loss 9.6→0.7,一个数量级;avg_R 80→97),说明它不是致命 bug,而是环境敏感。
请专家判:
- 这个实现是"简化版 SimSiam(可接受)"还是"实现错误(应修)"?
- 它的"CartPole 有效 / Pendulum 中性"的分裂,是因为实现问题,还是因为 Pendulum 本身在失败区间(任何消融都无信号)?
cmd的set VAR=x && cargo run未必把环境变量传给子进程。原作者第一次跑 base 用set MAX_EP=300 && cargo run ...,结果 MAX_EP 没生效,程序按默认跑了 ~300ep(unwrap_or(600),但因 timeout 在更高 ep 被砍)。可靠做法:用 PowerShell$env:MAX_EP='300'; cargo run ...,或justrecipe。- 后台任务 timeout:默认 1200s(20min)。base 全关 300ep ≈ 7-12min,但 release 首次编译 + pyo3 初始化会吃掉前 ~1min。若一次跑多个臂要串行(pyo3 有导入竞态 + CPU 抢占会让单臂 wall 翻倍)。
- 日志编码坑:用 PowerShell 重定向训练输出到
.log时,中文会因 GBK/UTF-8 混杂出现乱码(原始日志已不入库)。下次要留存日志建议显式指定 UTF-8,或直接读终端输出。
- 方法论层面:§3.1"消融必须在学会的任务上做"是否成立?如果成立,Pendulum 的 consistency ➖ 裁决应否**降级为"无信号/待定"**而非"中性"?
- 是否继续跑臂 B/C?还是先回答 §一的核心分叉(离散化方案能否学会)?
CONSISTENCY实现(§六)是否需要修正为标准 SimSiam(独立 target encoder + EMA + stop-grad)后再做消融?- 是否承认"纯离散化 MCTS 走不通"并提前启动 Gumbel-root?还是再试更多离散化变体(非均匀离散化、更大 NUM_ACTIONS)?
基于 §五的负面结果(RSCALE 证伪 + base 失败区间),原作者倾向认为:
- 当前数据已倾向支持"纯离散化 MCTS 在 Pendulum sims≤50 下走不通"这个分叉。
- 但因单 seed + 只跑了 2 个臂,证据尚不充分,故 suspension 求审而非直接下结论。
- 若专家认可方法论,最低成本的收尾是:补跑臂 B/C(确认无逃脱)+ 多 seed 复现 base,然后据此决定 Gumbel-root 是否升级为关键路径。
配置:与 CartPole promote 相同组件栈(consistency + reconstruction + Sampled);
recipe.rs注入 B=7 bin 中点;main.rs仅reward_scale(0.1)+ 门禁 −200。训练默认 sims=20 · γ=0.997(与 CartPole 对齐,非旧 README 的 sims=50/γ=0.99)。 修复:Sampled 子集 policy_target 须投射回完整 action_dim(target.rs::scatter_policy_target);否则 ep10 训练起点 Tensor shape 崩溃。
| 指标 | 值 |
|---|---|
| best greedy eval | −942.2 @ ep575 |
| 训末 greedy | −1118.9 |
| load best → eval×10 | −942.2 |
| run×1 | −867.4 |
| loss 轨迹 | 48 → ~5–7(较 §五 base ~17 卡死有进步) |
| self-play avg_R | ~−1239 → ~−1032 |
| 门禁 −200 | ❌ 未达标 |
greedy eval 里程碑:ep25 −1338 → ep50 −1041 → ep100 −1037 → ep200 −1033 → ep500 −994 → best −942 @ ep575。
| 配置 | best greedy | loss | 备注 |
|---|---|---|---|
| §五 base(9 档 · 无 cons/recon · sims=50) | ~−929 @ 80k | ~17 不降 | 旧栈 |
| §十 cons+recon+Sampled(B=7 · sims=20) | −942 @ 115k | ~5–7 | 本次 |
- 仍处失败区间(远未触达 −800「质的飞跃」线),不能对 cons/recon/Sampled 下 ✅/➖/❌ 裁决。
- cons+recon+Sampled 改变了训练动力学(loss 降、单局 R 偶发 −660~−750),但 greedy 策略未入门 → 与 §一 value 上游 / 搜索弱信号假设仍一致。
- Sampled K=5<B=7 + sims=20 是否额外伤害,未隔离;待 P1:
sims=50/γ=0.99A/B。
- P0:n-step target std 探针(
.diagnose()或 runner 日志),区分 target 截断 vs 搜索。 - P1:
main.rs只改sims=50、gamma=0.99,MAX_EP=300 看 greedy 能否过 −800。 - P2:仍失败 → 评估 Gumbel-root 或加大 B;勿在失败区间叠 completedQ 等 CartPole ❌ 组件。