PenGym 单智能体 izumi 当日进展报告:success distillation 压出 tiny-small deterministic 成功策略,并迁移到 tiny-hard BC solved 基线

一、这篇文章要说明什么

上一篇记录结束时,项目已经推进到一个新的关键位置:

  • 当前主线已经固定为 646/192,即 obs_dim=646action_dim=192
  • unified demo dataset 与 BC 训练链路已经打通;
  • BC 到 MaskablePPO 的 warm-start 工程链路已经打通;
  • tiny-small 的问题已经从“接口不通 / 数据不足”推进到更具体的策略提取问题;
  • 修复 repeat guard 后,stochastic policy 已经能偶尔成功,但 deterministic argmax 仍然不稳定;
  • 继续普通 PPO 训练、继续简单 root bonus、继续无控制采样都没有形成稳定 deterministic 成功策略。

本阶段的核心目标不再是继续盲目堆训练步数,而是做一件更聚焦的事:

把 stochastic 已经走出来的成功轨迹蒸馏回 BC,使 deterministic policy 稳定复现成功路径。

今天的真实结果可以概括为八点:

  1. 已完成 tiny-small 的 low-cost stochastic success trace collection,收集到 2 条真实成功轨迹。
  2. 已将 stochastic success traces 合并进 multidemo dataset,形成 successdistill dataset。
  3. 已训练出 tiny-small successdistill BC checkpoint,并在 deterministic eval 下达到 10/10 成功。
  4. 已确认普通 PPO warm-start 会破坏 BC actor,使策略从 10/10 退化到 0/10
  5. 已新增 actor-freeze / value-only PPO 路线,并解决 SB3 optimizer state 参数组不一致的加载问题。
  6. 已验证 actor-freeze PPO 在 tiny-small 上 deterministic 达到 10/10,并把平均步数从 BC 的 16 步进一步压到 12 步。
  7. 已将同一套 unified demo + BC 路线迁移到 tiny-hard,构造出第一个 tiny-hard unified dataset。
  8. 已训练并验证 tiny-hard BC deterministic 达到 10/10,说明 tiny-hard 也已获得可复现的 BC solved baseline。

因此,本阶段的最重要结论是:

tiny-small 已经不再只是 stochastic 偶发成功,而是通过 success distillation 压成了 deterministic 成功策略;并且 actor-freeze PPO 可以在不破坏 BC actor 的前提下保留甚至优化这条策略。与此同时,tiny-hard 也已经通过最小 expert demo + BC 训练达到 deterministic 10/10 成功。


二、为什么不继续普通 PPO,而是转向 success distillation

在本阶段开始时,tiny-small 的核心现象已经很明确:

  • guard bug 修复后,e_http@(3,1) 已经可以执行;
  • 50k 与 100k 训练都能看到 stochastic 成功;
  • 但 deterministic argmax 长期无法稳定选择正确动作;
  • 特别是在 (4,0) 附近,策略会在 e_sshe_ftp 之间产生错误偏好。

100k 的 logit 诊断进一步说明:

1
2
50k:  e_ftp - e_ssh = -1.78
100k: e_ftp - e_ssh = -2.10

也就是说,继续训练不但没有把 e_ftp@(4,0) 拉成 argmax,反而让 e_ssh 的优势更强。

随后尝试了 root transition bonus,希望通过更强的 ROOT 奖励把 e_ftp@(4,0) 拉起来。但 rootbonus_20k 训练中出现了一个关键现象:

1
ROOT_BONUS never fired

这说明奖励虽然实现了,但策略在训练过程中并没有稳定走到能触发该奖励的位置。因此继续调 root bonus 并不是低成本优先解。

于是本阶段转向了更直接的办法:

1
2
3
4
5
stochastic success rollout
-> 保存成功轨迹
-> 合并进 unified dataset
-> 重新 BC
-> deterministic eval

这条路线的核心判断是:

既然 stochastic policy 已经能走出成功路径,就不要继续让 PPO 随机漂移,而是直接把成功轨迹蒸馏成监督数据,强行让 deterministic policy 学会这些状态下的正确动作。


三、低成本收集 tiny-small stochastic 成功轨迹

为了避免此前 Claude 长时间后台采集导致成本失控,本阶段把采集目标改成低成本版本:

1
2
3
4
5
target_successes = 2
max_episodes = 20
max_steps = 80
timeout = 5m
save_partial = true

最终使用模型:

1
prototype/models/maskable_ppo_tiny_small_bc_multidemo_guardfix_rootbonus_20k.zip

收集结果为:

1
2
3
4
5
6
n_episodes_run = 9
n_success = 2
success_rate = 2/9 = 22.2%
total_samples = 36
obs_dim = 646
action_dim = 192

输出数据集:

1
2
prototype/models/tiny_small_stochastic_success_traces.npz
prototype/models/tiny_small_stochastic_success_traces_meta.json

两条成功轨迹都包含关键动作:

1
e_ftp@(4,0)

其中一条典型成功轨迹为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
service_scan@(1,0)
os_scan@(1,0)
e_http@(1,0)
pe_tomcat@(1,0)
subnet_scan@(1,0)
os_scan@(3,1)
e_ssh@(2,0)
pe_tomcat@(2,0)
service_scan@(3,1)
service_scan@(2,0)
os_scan@(2,0)
e_http@(3,1)
subnet_scan@(3,1)
service_scan@(3,1)
service_scan@(4,0)
os_scan@(4,0)
e_ftp@(4,0)

这一步非常关键,因为它证明:

成功路径不是人工幻想出来的,而是当前 policy 在真实 stochastic rollout 中实际走出来的成功 episode。


四、合并 successdistill dataset 并训练 tiny-small BC

随后将原有 multidemo dataset 与 stochastic success traces 合并。

原始 multidemo dataset:

1
2
3
prototype/models/demo_tiny_small_unified_multidemo_dataset.npz
samples = 77
episodes = 4

新增 stochastic success traces:

1
2
3
prototype/models/tiny_small_stochastic_success_traces.npz
samples = 36
episodes = 2

合并后 successdistill dataset:

1
2
3
4
5
prototype/models/demo_tiny_small_unified_successdistill_dataset.npz
samples = 113
episodes = 6
obs_dim = 646
action_dim = 192

随后训练 successdistill BC:

1
prototype/models/bc_unified_tiny_small_successdistill_300e.pt

训练结果:

1
2
3
4
5
6
obs shape    = (113, 646)
actions shape = (113,)
obs_dim = 646
action_dim = 192
final loss = 0.641556
final acc = 0.6903

这里 acc 没有达到 1.0,但这不是坏事。原因是 successdistill dataset 中包含多条不同轨迹和不同状态分支,不再是单轨迹重复记忆。

真正关键的是 deterministic rollout 结果。


五、tiny-small successdistill BC 达到 deterministic 10/10

使用 eval_bc_unified.py 对 successdistill BC checkpoint 做 deterministic eval,结果为:

1
2
3
4
5
6
7
Aggregate:
episodes : 10
success_rate : 10/10 = 100.0%
avg_reward : +180.000
avg_steps : 16.0
reached_(4,0) : 10/10
chose_e_ftp@(4,0) : 10/10

稳定复现的 16 步成功路径为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
service_scan@(1,0)
os_scan@(1,0)
e_http@(1,0)
pe_tomcat@(1,0)
subnet_scan@(1,0)
service_scan@(2,0)
e_ssh@(2,0)
pe_tomcat@(2,0)
subnet_scan@(2,0)
service_scan@(3,1)
e_ftp@(3,1)
e_http@(3,1)
subnet_scan@(3,1)
service_scan@(4,0)
os_scan@(4,0)
e_ftp@(4,0)

这一步是本阶段第一个真正的成功里程碑:

tiny-small 已经可以被 successdistill BC deterministic 稳定解决。

对应 checkpoint:

1
prototype/models/bc_unified_tiny_small_successdistill_300e.pt

建议固化副本:

1
prototype/models/bc_unified_tiny_small_successdistill_SOLVED_10of10.pt

六、普通 PPO warm-start 会破坏 BC actor

在 BC deterministic 10/10 后,尝试将该 BC checkpoint 接回普通 MaskablePPO warm-start。

训练命令使用:

1
2
3
BC_CHECKPOINT=prototype/models/bc_unified_tiny_small_successdistill_300e.pt
WARMSTART_TIMESTEPS=20000
OUT_MODEL=prototype/models/maskable_ppo_tiny_small_successdistill_warmstart_20k.zip

训练本身可以完成,日志中也显示:

1
2
3
entropy_loss: around -2.02 -> -1.53
clip_fraction: non-zero
value_loss: 4.31e+03 -> 1.07e+03

但 deterministic eval 结果严重退化:

1
2
3
4
5
6
PPO deterministic:
success_rate = 0/10
reached_(4,0) = false
chose_e_ftp@(4,0) = false
avg reward = -310
max steps = 100

典型失败轨迹表现为:

1
2
3
4
5
6
7
8
9
10
os_scan@(1,0)
e_http@(1,0)
subnet_scan@(1,0)
service_scan@(3,1)
e_ftp@(3,1)
e_http@(3,1)
e_ssh@(2,0)
pe_tomcat@(2,0)
...
service_scan@(3,1) repeated

这说明普通 PPO fine-tune 把 BC actor 中已经学到的成功路径冲坏了。

因此,本阶段明确得到一个结论:

successdistill BC 已经解决 tiny-small,但普通 PPO actor 更新会破坏它,不能继续使用无约束 PPO warm-start。


七、actor-freeze / value-only PPO 路线打通

为了解决“PPO 破坏 BC actor”的问题,本阶段新增了 actor-freeze PPO 路线。

新增脚本:

1
prototype/rl/train_maskable_ppo_tiny_small_bc_actorfreeze.py

核心思路是:

  1. 加载 successdistill BC checkpoint;
  2. 将 BC actor/action 权重迁移到 MaskablePPO;
  3. 冻结 actor / policy_net / action_net;
  4. 只训练 value branch;
  5. 避免 PPO 在早期直接破坏已成功的 BC policy。

训练结果显示:

1
2
3
4
5
6
approx_kl       ≈ 2e-08
clip_fraction = 0
policy_gradient_loss ≈ 0
value_loss = 2.68e+03 -> 1.26e+03
ep_rew_mean = 133
ep_len_mean = 27

这说明 actor 基本没有被改动,主要是 value branch 在更新。

中途还遇到一个 SB3 optimizer state 问题:

1
ValueError: loaded state dict contains a parameter group that doesn't match the size of optimizer's group

原因是 actor-freeze 训练时 optimizer 只包含 value branch 参数,而 SB3 load 时会重建完整 PPO optimizer,导致 parameter group size mismatch。

后续修复方式为:

  • 保存前重建 optimizer,使其覆盖全部 policy 参数;
  • eval 加载时加入 fallback,必要时丢弃 policy.optimizer 并恢复 policy 权重。

修改文件:

1
2
prototype/rl/train_maskable_ppo_tiny_small_bc_actorfreeze.py
prototype/rl/eval_tiny_small_ppo_model.py

八、actor-freeze PPO 在 tiny-small 上达到 10/10,并压到 12 步

使用 actor-freeze PPO 模型:

1
prototype/models/maskable_ppo_tiny_small_successdistill_actorfreeze_20k.zip

deterministic eval 结果为:

1
2
3
4
5
6
7
Aggregate:
episodes : 10
success_rate : 10/10 = 100.0%
avg_reward : +192.000
avg_steps : 12.0
reached_(4,0) : 10/10
chose_e_ftp@(4,0) : 10/10

相比 BC baseline:

1
2
3
4
5
6
7
8
9
BC successdistill:
success_rate = 10/10
avg_reward = +180
avg_steps = 16

actor-freeze PPO:
success_rate = 10/10
avg_reward = +192
avg_steps = 12

这说明 actor-freeze / value-only PPO 不仅保住了 BC policy,还压出了更短路径。

典型 12 步轨迹为:

1
2
3
4
5
6
7
8
9
10
11
12
os_scan@(1,0)
e_http@(1,0)
subnet_scan@(1,0)
os_scan@(2,0)
service_scan@(2,0)
e_ssh@(2,0)
pe_tomcat@(2,0)
service_scan@(3,1)
e_ftp@(3,1)
e_http@(3,1)
subnet_scan@(3,1)
e_ftp@(4,0)

stochastic eval 结果为:

1
2
3
4
5
success_rate      = 6/10 = 60.0%
avg_reward = -26.400
avg_steps = 53.2
reached_(4,0) = 7/10
chose_e_ftp@(4,0) = 6/10

当前目标是 deterministic solved,因此 stochastic 结果只作为参考。

本阶段第二个关键结论是:

普通 PPO 会破坏 BC actor,但 actor-freeze / value-only PPO 可以把 BC actor 安全封装进 PPO,并进一步压缩成功路径。

建议固化模型:

1
prototype/models/maskable_ppo_tiny_small_successdistill_actorfreeze_SOLVED_10of10_12steps.zip

九、迁移到 tiny-hard:先做最小 unified demo + BC

tiny-small 已经获得两个 solved baseline 后,继续把同样路线迁移到 tiny-hard

本阶段没有直接跑 PPO,也没有直接采集 stochastic success,而是先做最小闭环:

1
2
3
4
tiny-hard demo JSON
-> unified dataset
-> BC training
-> deterministic BC eval

首先新增 tiny-hard demo:

1
prototype/models/demo_tiny_hard_candidate.json

动作路径为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
host1-0:
ServiceScan
OSScan
Exploit_Http
ProcessScan
PrivilegeEscalation_Tomcat
SubnetScan

host2-0:
ServiceScan
OSScan
Exploit_Ssh
ProcessScan
PrivilegeEscalation_Tomcat
SubnetScan

host3-0:
ServiceScan
OSScan
Exploit_Ftp

实际构建 dataset 时观察到一个细节:

1
PrivilegeEscalation_Tomcat@(1,0) failed

但整条 episode 仍然成功完成,最终通过 e_ftp@(3,0) 获得 ROOT。

构建结果:

1
2
prototype/models/demo_tiny_hard_unified_dataset.npz
prototype/models/demo_tiny_hard_unified_dataset_meta.json

inspect 结果:

1
2
3
4
5
6
7
8
9
10
observations shape : (15, 646)
actions shape : (15,)
total reward : 182.00
episode_ends : [14]
num episodes : 1
obs_dim : 646
action_dim : 192
unified : True
dagger_ready : True
Sanity checks : OK

这里曾经因为 --out 参数传了 .npz,builder 又自动追加 .npz,导致文件名变成:

1
demo_tiny_hard_unified_dataset.npz.npz

后续已改名整理为:

1
demo_tiny_hard_unified_dataset.npz

十、tiny-hard BC 训练与 deterministic eval 成功

使用 tiny-hard unified dataset 训练 BC:

1
prototype/models/bc_unified_tiny_hard_candidate_300e.pt

训练结果:

1
2
3
4
5
6
7
8
obs shape    = (15, 646)
actions shape = (15,)
obs_dim = 646
action_dim = 192

epoch=1, loss=5.207014, acc=0.0000
epoch=80, loss=0.436775, acc=1.0000
epoch=300, loss=0.001724, acc=1.0000

随后做 deterministic eval,最开始 eval_bc_unified.py 仍然输出 tiny-small 专用字段:

1
2
reached_(4,0)
chose_e_ftp@(4,0)

这对 tiny-hard 没意义,因为 tiny-hard 的目标是:

1
(3,0), e_ftp@(3,0)

因此对 eval_bc_unified.py 做了最小修改:

  • --scenario 包含 tiny-hard 时,target host 自动设为 (3,0)
  • 其他场景仍保留原先 (4,0) 统计;
  • 不改变模型加载、wrapper 或评估逻辑。

修改后 tiny-hard BC deterministic eval 结果为:

1
2
3
4
5
6
7
Aggregate:
episodes : 10
success_rate : 10/10 = 100.0%
avg_reward : +182.000
avg_steps : 15.0
reached_(3,0) : 10/10
chose_e_ftp@(3,0) : 10/10

这说明:

tiny-hard 也已经通过最小 expert demo + BC 路线得到 deterministic solved baseline。

建议固化模型:

1
prototype/models/bc_unified_tiny_hard_SOLVED_10of10_15steps.pt

十一、当前模型与数据资产整理

截至本阶段,关键资产如下。

1. tiny-small 数据集

1
2
3
prototype/models/demo_tiny_small_unified_multidemo_dataset.npz
prototype/models/tiny_small_stochastic_success_traces.npz
prototype/models/demo_tiny_small_unified_successdistill_dataset.npz

2. tiny-small BC baseline

1
prototype/models/bc_unified_tiny_small_successdistill_300e.pt

建议固化:

1
prototype/models/bc_unified_tiny_small_successdistill_SOLVED_10of10.pt

结果:

1
2
3
success_rate = 10/10
avg_reward = +180
avg_steps = 16

3. tiny-small actor-freeze PPO baseline

1
prototype/models/maskable_ppo_tiny_small_successdistill_actorfreeze_20k.zip

建议固化:

1
prototype/models/maskable_ppo_tiny_small_successdistill_actorfreeze_SOLVED_10of10_12steps.zip

结果:

1
2
3
success_rate = 10/10
avg_reward = +192
avg_steps = 12

4. tiny-hard 数据集

1
2
3
prototype/models/demo_tiny_hard_candidate.json
prototype/models/demo_tiny_hard_unified_dataset.npz
prototype/models/demo_tiny_hard_unified_dataset_meta.json

5. tiny-hard BC baseline

1
prototype/models/bc_unified_tiny_hard_candidate_300e.pt

建议固化:

1
prototype/models/bc_unified_tiny_hard_SOLVED_10of10_15steps.pt

结果:

1
2
3
4
5
success_rate = 10/10
avg_reward = +182
avg_steps = 15
reached_(3,0) = 10/10
chose_e_ftp@(3,0) = 10/10

十二、当前阶段的关键技术结论

1. success distillation 是 tiny-small 的有效解法

普通 PPO 继续训练没有把 stochastic success 压成 deterministic success。

相反,低成本收集真实 stochastic 成功轨迹,再蒸馏进 BC,直接得到 deterministic 10/10

这说明在当前问题上:

1
successful trajectory distillation > blind PPO fine-tuning

2. 普通 PPO 会破坏已成功的 BC actor

即使 BC deterministic 已经 10/10,普通 PPO warm-start 20k 仍然把策略冲坏到 0/10

原因不是 BC 不会,而是 PPO actor update 在当前 reward/mask/trajectory distribution 下会让策略偏离成功路径。

3. actor-freeze / value-only 是当前更安全的 PPO 接法

actor-freeze PPO 的结果说明:

  • actor 保持 BC 成功策略;
  • value branch 可以继续学习;
  • deterministic 成功率不下降;
  • 路径还能从 16 步压到 12 步。

因此后续如果要把 BC 接回 PPO,应优先考虑:

1
2
3
4
actor freeze
value-only warmup
KL-to-BC regularization
small learning-rate actor unfreeze

不应继续直接 PPO 更新 actor。

4. tiny-hard 并不是不可解,只是此前纯 DRL adaptation 没打出来

此前 tiny-hard 在 20k adaptation 下失败,但现在通过最小 expert demo + BC 训练已经 deterministic 10/10

这说明 tiny-hard 的核心难点并不是环境不可达,而是当前纯 PPO adaptation 在有限步数与有限探索下未能稳定找到正确路径。

5. 当前主线仍应保持 646/192

本阶段所有成功结果都建立在:

1
2
3
obs_dim = 646
action_dim = 192
reference = medium_reference_signatures.json

因此短期内不要切回 1368/420,否则所有 dataset、BC checkpoint、PPO checkpoint 都需要重新对齐。


十三、当前不应该继续做什么

当前阶段不建议继续:

  1. 继续普通 PPO warm-start;
  2. tiny-small 继续无约束 50k / 100k / 200k 训练;
  3. 继续重复采集大量 stochastic traces;
  4. 继续调 root bonus 作为主修复线;
  5. tiny-small 已 solved 后继续微调同一个场景;
  6. 切换到 1368/420 v2 主线;
  7. 上 Transformer 或完整 DAgger;
  8. 让 Claude 执行长时间训练或后台轮询。

尤其要避免再次出现:

1
收 10 条成功轨迹 / max 100 episodes / 后台轮询

这种高成本、低可控的任务模式。


十四、下一阶段建议

下一阶段应分成两条低风险线。

1. 固化 solved baselines

先把当前已经成功的模型复制成明确命名:

1
2
3
4
5
6
7
8
cp prototype/models/bc_unified_tiny_small_successdistill_300e.pt \
prototype/models/bc_unified_tiny_small_successdistill_SOLVED_10of10.pt

cp prototype/models/maskable_ppo_tiny_small_successdistill_actorfreeze_20k.zip \
prototype/models/maskable_ppo_tiny_small_successdistill_actorfreeze_SOLVED_10of10_12steps.zip

cp prototype/models/bc_unified_tiny_hard_candidate_300e.pt \
prototype/models/bc_unified_tiny_hard_SOLVED_10of10_15steps.pt

这些文件应该作为当前阶段的可复现实验资产保留。

2. 继续推进 tiny-hard actor-freeze PPO,但不要普通 PPO

tiny-hard 现在已经有 BC solved baseline,下一步可以仿照 tiny-small

1
2
3
tiny-hard BC solved
-> actor-freeze PPO
-> deterministic eval

但必须遵守:

  • 不直接普通 PPO 更新 actor;
  • 先 actor-freeze / value-only;
  • eval 成功后再考虑小步解冻;
  • 如果 actor-freeze 退化,先查 wrapper 和 freeze,不继续训练。

3. 后续再考虑跨 tiny-family 的策略抽象

当前 tiny-smalltiny-hard 都已经能通过 BC solved。后续更有价值的问题不是继续单点调参,而是研究:

  • 是否能从 tiny-small 的 successdistill 数据迁移到 tiny-hard
  • 是否能统一 tiny family 的 demo schema;
  • 是否能让一个 BC / PPO policy 覆盖多个 tiny scenarios;
  • 是否能结合 LLM 做高层任务分解,而不是底层 step-level override。

十五、今天最终阶段结论

本阶段的核心突破是:

tiny-small 已经从 stochastic 偶发成功推进到 deterministic solved,并进一步通过 actor-freeze PPO 得到更短路径;tiny-hard 也已经通过最小 unified demo + BC 达到 deterministic solved。

当前最重要的两个结果是:

1
2
3
4
5
6
7
8
9
10
11
tiny-small actor-freeze PPO:
success_rate = 10/10
avg_reward = +192
avg_steps = 12
target = e_ftp@(4,0)

tiny-hard BC:
success_rate = 10/10
avg_reward = +182
avg_steps = 15
target = e_ftp@(3,0)

这说明当前 646/192 unified cross-family 主线已经不仅能跑通 medium/small/tiny 的接口,也已经开始能通过 imitation + safe PPO warmup 解决原本纯 PPO adaptation 失败的 tiny-family 难点。

下一阶段应停止在已 solved 的 tiny-small 上继续消耗训练预算,转而把 actor-freeze PPO 安全迁移到 tiny-hard,并逐步研究 tiny family 内的统一策略与高层规划问题。