PenGym 单智能体 izumi 当日进展报告:reward2 主线保留,reward3 未胜出,长训收益有限,研究重点正式转向泛化接口

一、这篇文章要说明什么

这份记录只描述今天真实完成的事情,不把尚未完成的泛化接口、LLM 接入、多智能体协作写成已经做完。

今天最重要的结果可以概括为六点:

  1. 已确认当前单智能体学习型 izumi 的主 baseline 仍应保留为 MaskablePPO + reward2 + base_200k
  2. 已完成 reward3 分支训练与评测,但当前结果并没有超过 reward2,因此不作为主线。
  3. 已尝试在 base_200k 基础上继续训练,但在当前单局 deterministic eval 口径下,没有观察到比 200k 更明显的提升,因此暂不继续把精力放在长训上。
  4. 已尝试直接做跨场景泛化评测,但发现现有实现并不具备直接跨场景 zero-shot 的条件,核心阻塞是 observation / action space 不统一。
  5. 已通过阅读 PenGymEnvNASimEnvObservationHostVector 等实现,定位出当前泛化问题并不在 PPO 算法本身,而在状态表示与动作空间的场景绑定。
  6. 已明确下一阶段的实际工程目标:先做 medium family 的 unified observation adapter,在不更换 PPO 的前提下,为后续泛化训练和测试打底。

二、今天的主线目标

今天原本围绕两个方向推进:

  • 一条线是继续验证 reward2reward3,确认 reward 设计是否还能带来收益。
  • 另一条线是评估是否应该继续在 base_200k 基础上做更长训练,或者把研究重点转向泛化。

经过今天的实际训练、评测与源码排查,结论已经比较清楚:

reward2 继续保留,reward3 先停;长训暂时降级;泛化正式上升为下一阶段主问题。


三、今天具体做了什么

1. 梳理并确认当前主 baseline

首先重新对齐了当前主线模型的实际状态:

  • 当前已训练得到的主模型是 base_200k
  • 奖励主线是 reward2
  • 当前 agent 仍然是基于 MaskablePPO 的单智能体 izumi

这一步的意义在于防止后续实验线继续混乱。因为如果 baseline 本身没有锁定,后续无论是 reward 改造、长训还是泛化,最后都会失去比较基准。

因此,今天先做的不是盲目继续训,而是先把“当前哪个是主版本”这件事说清楚:

主 baseline = MaskablePPO + reward2 + base_200k


2. 完成 reward3 分支训练与评测

今天继续沿着“是否通过奖励函数设计提升渗透 agent 表现”这条线做了 reward3 分支。

reward3 的改动方向主要是:

  • 更强调真实推进(例如新主机发现、权限提升)
  • 更强调对无效扫描、无收益 exploit、显式错误的惩罚
  • 试图减少策略在无意义动作上的循环

从工程上,reward3 分支已经跑通:

  • 训练能正常启动
  • 模型能正常保存
  • 评测也能完整跑通

但真正关键的是结果,而不是“能跑”。

在当前单局评测口径下:

  • reward2total_reward = 155.0steps = 23
  • reward3total_reward = 153.0steps = 25

这说明 reward3 并没有在当前评测下打赢 reward2。二者都成功完成任务,但 reward3:

  • 总回报略低
  • 步数略多
  • 没有体现出明确的行为改善优势

所以,今天对 reward3 的结论不是“失败到完全不可用”,而是:

reward3 是一个有效实验分支,但目前不优于 reward2,不作为主线保留。


3. 验证长训收益,但未观察到明显增益

在确定 reward3 没有打赢之后,进一步尝试了继续长训的方向。

这里的核心问题并不是“训练有没有跑”,而是:

在当前评测口径下,继续训练后是否真的带来了更强表现。

实际结果是:

  • 已完成从 base_200k 继续训练的实验
  • 产生了新的 continuation checkpoint
  • 但在当前 deterministic 单局 eval 上,没有看到比 200k baseline 更明显的改进

这说明一件事:

至少在当前评测方式下,继续堆训练步数没有立刻转化为可见的策略收益。

因此,今天没有再把时间继续压在“再训、更久地训”上,而是做了一个更重要的判断:

当前阶段真正的瓶颈很可能不是训练步数,而是表示空间与泛化条件本身。


4. 尝试直接做跨场景泛化,发现结构性阻塞

随后,开始把 attention 从 reward 和长训转向泛化。

最先尝试的是直接把当前 base_200k + reward2 拿去做跨场景 zero-shot evaluation。

第一组测试选的是与训练场景同属于 medium family 的:

  • medium-multi-site(训练主场景)
  • medium-single-site
  • medium

在对 medium-single-site 做直接评测时,立即触发了关键报错:

  • 模型期望 observation shape 是 (476,)
  • 但新场景给出的 observation shape 是 (561,)

也就是说,问题不是“策略差”,而是:

当前模型根本没有资格直接跨场景推理,因为输入维度已经不匹配。

这一步非常关键,因为它把“泛化难题”从模糊的性能问题,变成了明确的工程与表示问题。

今天因此得出的一个重要科研结论是:

当前 izumi + MaskablePPO 实现仍然依赖固定 observation/action space,不能直接支持不同 NASim/PenGym 场景之间的 zero-shot 迁移。


5. 系统排查 PenGym / NASim 源码,定位阻塞位置

为了搞清楚为什么一换场景 observation 就变,今天没有停留在“报错现象”,而是继续往实现里追。

排查路径包括:

  • pengym/envs/environment.py
  • nasim/envs/environment.py
  • nasim/envs/observation.py
  • nasim/envs/host_vector.py

通过这一轮源码阅读,今天已经定位出几个关键事实。

(1)PenGymEnv 本身并不决定 observation shape

PenGymEnv 只是对 NASimEnv 做了一层包装,它真正做的主要是:

  • 继承 NASimEnv
  • 初始化 PenGymNetwork
  • 初始化 PenGymState

说明 observation 的真正生成逻辑并不在这层薄包装里。

(2)NASimEnv 直接把 observation 变成固定长度 flatten 向量

NASimEnv 在初始化时:

  • reset() 生成 last_obs
  • 再用 last_obs.shape_flat() 决定 observation space 的形状
  • step() / reset() 时把 observation 通过 numpy_flat() 压平给 PPO

这说明当前 PPO 接收到的输入不是结构化对象,而是一个已经被 flatten 的固定长度向量。

(3)Observation 本质上是二维张量再 flatten

Observation 的结构是:

  • 若干个 host row
  • 最后一行 auxiliary row(success / permission_error / connection_error / undefined_error 等)

最后通过 flatten() 变成一维 observation。

这意味着不同场景 observation 长度变化的根源,来自:

  • host row 数量
  • 每个 host row 的宽度

(4)HostVector 说明了 host row 的组成规则

HostVector 给出了一台主机对应那一行 observation 的字段组成:

  • subnet one-hot
  • host address one-hot
  • compromised / reachable / discovered / value / discovery_value / access
  • os one-hot
  • services one-hot
  • processes one-hot

而每行宽度的公式也被明确写出来了:

#subnets + max_hosts_in_subnet + 6 + #OS + #services + #processes

所以当前 observation 的场景绑定问题,不是随机产生的,而是因为:

  • 地址空间编码长度会随场景拓扑变化
  • action space 也会随 scenario 变化

这一步带来的真正收获是:

当前泛化问题已经被定位到表示层,而不是 PPO 算法本身。


6. 确认 medium family 是第一阶段泛化改造的最佳目标

今天还进一步对 medium family 做了结构信息对比,打印了:

  • address_space_bounds
  • subnets
  • os
  • services
  • processes

结果很重要:

  • medium-multi-site
  • medium-single-site
  • medium

这三个场景在:

  • os
  • services
  • processes

这三个部分上完全一致。

真正不同的是:

  • subnet 数量
  • host slot 数量
  • 地址编码方式

这意味着第一阶段泛化改造可以不碰 small / tiny,先集中做 medium family。

这是今天最正向的发现之一。因为它说明:

至少在 medium family 内,做统一 observation 接口是有现实工程可行性的。


7. 明确了 medium-family unified observation adapter 的方向

在上面这些排查基础上,今天已经把下一阶段泛化接口的大方向初步定下来了。

medium family 的 unified observation schema,计划先按下面这个 canonical 结构来做:

  • max subnet slots = 7
  • max host slots per subnet = 16
  • 标量字段 = 6
  • os = 1
  • services = 5
  • processes = 3

对应每一行固定宽度:

  • 7 + 16 + 6 + 1 + 5 + 3 = 38

再结合当前 medium family 的 observation row 结构,计划把第一版统一接口做成:

  • 17 rows × 38 cols
  • flatten 后是 646 维输入

这个设计意味着:

后续不需要先换 PPO,只需要先做统一 observation adapter,再在统一接口上重新训练 PPO。

这也就是明天继续推进时的起点。


四、今天做对了什么

今天最重要的正向进展,不是“又训练出一个更高分模型”,而是把问题看清了。

1. 没有在 reward3 上继续死磕

reward3 没赢就是没赢。今天没有为了“非要让 reward3 有价值”而继续硬凹,这一点是对的。

这让主线保持清楚:

  • reward2 保留
  • reward3 存档
  • 不继续在错误方向上消耗时间

2. 没有继续盲目堆长训

在 200k continuation 没打出明显增益后,今天没有简单粗暴地继续加码训练步数,而是转向追问:

  • 为什么长训没收益?
  • 真正的瓶颈在哪?

最终发现真正阻塞泛化的是 observation/action 空间问题,这比继续长训更有价值。

3. 没停留在“报错现象”,而是追到源码层

今天最有科研价值的一点,是没有把

  • Unexpected observation shape

当成一个“换个参数也许就好了”的小问题,而是直接追到了:

  • PenGymEnv
  • NASimEnv
  • Observation
  • HostVector

这一步把泛化问题定位成了一个清晰的系统设计问题。

4. 已经把下一阶段的第一目标缩小到 medium family

今天没有试图一口气解决:

  • medium
  • small
  • tiny
  • 不同 action_n
  • 不同 obs_shape

而是把问题缩小到最有希望先打通的 medium family。

这让明天开始的工作会更聚焦,也更容易产出阶段性结果。


五、今天的问题与不足

今天当然也有一些明确的问题,需要记下来。

1. 对“长训命名 / 训练步数语义”的表述一度混乱

今天在讨论 200k400k、continuation 训练时,对“当前这次 learn 跑了多少步”和“checkpoint 所代表的总训练量”一度存在表述混乱。

虽然最后已经把事情说清,但这个问题说明:

后续实验命名、checkpoint 命名、日志解释必须统一,不然很容易把训练语义和实验结论搅乱。

2. 现在还没有真正开始泛化训练

今天只是把泛化问题定位清楚,并确定 medium-family adapter 的目标方向,
但还没有真正写出 unified observation wrapper,更没有在新接口上重新训练 PPO。

所以现在还不能说“已经做出泛化 agent”,只能说:

已经完成了泛化问题的结构定位与方案收敛。

3. 当前评测口径仍然比较单薄

今天关于 reward2 / reward3、200k / continuation 的判断,依然主要基于:

  • deterministic
  • 单局或极少量局数
  • 当前已有 eval 脚本

这意味着后面即使进入统一接口训练后,也必须尽快补足更规范的多 episode、多 seed 评测方式。


六、今天停在哪

今天最终停在了一个比较清楚的位置:

  • reward2 继续作为主 baseline
  • reward3 归档为未胜出的实验分支
  • 长训暂时不是研究重点
  • 泛化正式成为下一阶段主线
  • 泛化的第一阶段目标锁定为 medium family
  • 下一步不是继续测,而是先做 unified observation adapter

也就是说,今天的暂停点不是“卡住不知道怎么做”,而是:

已经明确下一步该改哪一层,只是还没开始写 adapter 代码。


七、明天开始时的第一件事

明天继续时,不再回头重复 reward、长训、是否继续堆步数这些讨论,直接从这里接:

第一目标

实现一个 medium-family unified observation adapter wrapper

它要解决的问题

把:

  • medium-multi-site
  • medium-single-site
  • medium

当前各自不同长度的 flatten observation,统一映射成固定的 646 维输入。

这样做的意义

一旦这层统一接口打通,后面就可以在不更换 PPO 的前提下,真正开始做:

  • medium family 内部的泛化训练
  • medium family 内部的 held-out 场景测试

这才是下一阶段最实在的工作。


八、今天的结论

今天没有拿到一个“分数暴涨”的新模型,但拿到了一条更重要的结论链:

  1. reward3 没有超过 reward2,因此主线继续保留 reward2;
  2. 继续长训在当前口径下没有打出明显提升,因此长训不是当前主问题;
  3. 真正限制 agent 跨场景迁移的不是 PPO 本身,而是 observation / action space 被场景绑定;
  4. medium family 具备先做统一 observation 接口的条件,因此泛化的第一阶段方向已经确定。

所以,今天的价值不在于“又多训了一轮”,而在于:

已经把研究主线从 reward/长训试错,推进到了“统一表示空间下的泛化训练”这个更核心的问题上。

这也是明天最应该继续做的地方。