ICML-2027 / docs /Embodied-Step-RL-Paper-Code-DeepDive.md
Wendy-Fly's picture
Upload EasyR1-V4-Embody code and docs
2f59739 verified
|
Raw
History Blame Contribute Delete
39.1 kB

EasyR1-V4-Embody 当前实现与两篇论文的完整串讲

本文档把三件事串起来讲清楚:

  1. arXiv:2505.22050, Reinforced Reasoning for Embodied Planning
  2. arXiv:2605.27140v1, StepOPSD: Step-Aware Online Preference Distillation for Agent Reinforcement Learning
  3. 当前仓库 EasyR1-V4-Embody 的代码实现,尤其是 grpo_steprule step reward、forward_proxy、residual gate / prompt gate。

结论先放在前面:

2505.22050 是一个 embodied VLM 训练 recipe:
SFT 初始化 + rule-based reward + GRPO/RFT,让 Qwen2.5-VL 学会输出多步可执行计划。

2605.27140 StepOPSD 是一个 agent RL 信用分配算法:
把 OPSD teacher-student logp gap 放到 step/action span 上,做 GRPO advantage shaping。

当前 EasyR1-V4-Embody 介于两者之间:
任务设定、数据和输出形式更像 2505.22050;
算法改动、step-level advantage shaping 和 forward_proxy 更像 StepOPSD;
同时又额外实现了 rule step reward、GT-reference teacher、additive advantage、action_id/action_name span control、residual/prompt gates。

1. 三个对象各自解决什么问题

1.1 2505.22050 解决的是“怎么训练 embodied planner”

这篇论文的主问题是:开源 VLM 在具身场景里很难做长程、多步、视觉条件下的 planning。模型要看当前视觉 observation、历史动作、自然语言任务,然后输出后续 action sequence。

它的方案是两阶段:

Stage 1: SFT
  用强闭源模型 Gemini-2.0-flash 蒸馏出高质量 reasoning + plan 数据。
  让 Qwen2.5-VL-7B 先学会任务分解、空间常识、输出格式。

Stage 2: RFT / GRPO
  构造 ALFRED 轨迹分解数据。
  对模型输出的多步 action plan 设计 rule-based reward。
  用 GRPO 做 reinforcement fine-tuning。

它的 reward 是规则式的:

R = R_format + R_accuracy

R_format:
  JSON 结构是否正确;
  必需字段是否存在;
  action_id / action_name 是否有效匹配。

R_accuracy:
  按 action sequence 和 gold sequence 做 prefix matching。
  若连续匹配 n 步,gold 总长为 k:
  R(n; k) = n(n + 1) / (k(k + 1))

所以 2505.22050 的重点不是“OPSD 信号怎么改 advantage”,而是“具身规划任务上,SFT + 规则奖励 + GRPO 这条 pipeline 能不能训动”。

1.2 StepOPSD 解决的是“长轨迹 sparse reward 怎么分给关键 step”

StepOPSD 的主问题是 agent RL 的 credit assignment:

一条长 rollout 可能因为一个局部 action 错了而失败。
标准 GRPO 只有一个 terminal reward。
这个 reward 被广播到整条 response 的所有 token。
结果是:模型知道整条轨迹不好,但不知道是哪一步导致不好。

StepOPSD 的核心流程:

rollout
-> trajectory reward / GRPO advantage
-> parse action-centered step spans
-> hindsight teacher context rescoring
-> compute token-level logp gap Delta
-> convert Delta into bounded multiplicative weights
-> reshape GRPO advantage
-> policy update

它的信号是:

Delta_{k,j}
  = log pi_T(z_{k,j} | hindsight context, prefix)
  - log pi_S(z_{k,j} | causal context, prefix)

它的 advantage 注入方式是:

w_raw = 2 * sigmoid(sign(A) * Delta)
w     = clip(w_raw, 1 - alpha_clip, 1 + alpha_clip)

A_tilde = (1 - lambda_mix) * A + lambda_mix * (w * A)

关键性质:

w 始终为正。
因此 StepOPSD 只改变原始 GRPO advantage 的 magnitude,不改变 sign。
它是 sign-preserving 的 credit redistribution。

StepOPSD 的 hindsight 来源主要是同一个 GRPO group 中的 successful peer trajectory;teacher 是 stale reference policy,避免 moving target。

1.3 当前代码解决的是“one-shot embodied JSON plan 内部的 step credit assignment”

当前仓库的任务形态:

输入:
  图像 + human instruction + action history + action list

输出:
  一次性生成完整 JSON:
  {
    "visual_state_description": "...",
    "reasoning_and_reflection": "...",
    "language_plan": [...],
    "executable_plan": [
      {"action_id": 133, "action_name": "..."},
      ...
    ]
  }

注意:当前代码不是多轮在线环境交互。它是一行样本生成一整条后续 trajectory plan。

代码里的核心目标:

原来只有整条 trajectory 的 scalar reward。
现在希望在同一条 response 内部,把不同 step 的 token 赋予不同 advantage。

A_token = A^E + omega * A^S

A^E:
  原始 GRPO episode advantage。
  来自整条轨迹 scalar reward 的组内归一化。

A^S:
  新增 step-level per-token advantage。
  可以来自 rule action_id 匹配,也可以来自 forward_proxy teacher-student logp contrast。

因此,当前实现和 2505.22050 的任务设定很近,和 StepOPSD 的算法思想也很近。


2. 当前代码端到端数据流

2.1 Dataset:把 EB-ALFRED 样本变成 VLM prompt

主要文件:

verl/utils/dataset.py

当前 embodied dataset 的 __getitem__ 做了这些事:

  1. 从 parquet 行里读:
instruction
history
action_id       # 可用动作列表
image
gt_bbox
gt_action
gt_input_text
gt_action_close / gt_action_open 等字段保留在 row_dict 中
  1. 构造 prompt:
You are Embodied-R1 ...
Given the current visual state <image> ...
Current context: {history}

Action Descriptions and Validity Rules
...

The available action names are:
{actionlist}

Output ONLY a valid JSON object:
  visual_state_description
  reasoning_and_reflection
  language_plan
  executable_plan
  1. <image> 替换为 Qwen-VL 视觉占位:
<|vision_start|><|image_pad|><|vision_end|>
  1. 用 processor 编码图像和 prompt,生成:
input_ids
attention_mask
position_ids
raw_prompt_ids
multi_modal_data = {"images": raw_images}
ground_truth = json.dumps(gt)

这里的 ground_truth 不是直接的 executable_plan list,而是一个包装后的 JSON,内部包含:

action
gt_bbox
input_text
gt_open_ans

其中 input_text 里还能解析出 GT 的 executable_plan

2.2 Rollout:模型一次性生成 response

训练主循环在:

verl/trainer/ray_trainer.py

核心顺序:

prepare_rollout_engine
-> _make_batch_data
-> release_rollout_engine
-> _balance_batch
-> compute_reward
-> compute_old_log_probs
-> compute_ref_log_probs
-> build step_adv_raw if adv_estimator == grpo_step
-> compute_advantage
-> update actor

这里的 response 是完整 JSON plan,不是一步环境 action。

2.3 Trajectory-level reward:verify model 给整条计划打分

主要文件:

verl/workers/reward/function.py
verl/workers/reward/verify.py

compute_reward_batch 的行为是:

  1. decode response。
  2. 尝试解析 JSON。
  3. 从 response 中提取:
executable_plan
  1. 从 ground truth 中提取:
ground_truth_open = json.loads(ground_truth["input_text"])["executable_plan"]
  1. 构造 verify prompt:
Sentence 1: {GT executable_plan}
Sentence 2: {pred executable_plan}

You are judging whether a predicted embodied action trajectory matches
the ground-truth trajectory at the intent level.

Focus on FUNCTIONAL EQUIVALENCE ...
Compare mainly by action_name, not exact action_id.
Be lenient about extra/missing find steps ...
Only output: True or False.
  1. 调用 ask_llm,用 verify VLM 生成一个 token,取 True / False 相关概率。

  2. 得到:

format = 1.0 if JSON parse ok else 0.0
accuracy = verify probability, False 会转成负值
overall = 0.8 * accuracy + 0.2 * format
  1. overall 写到最后一个 response token:
reward_tensor[i, cur_response_length - 1] = score["overall"]

这意味着整条 trajectory 的 reward 在张量里仍然是一个 terminal scalar。

2.4 原始 GRPO:A^E 怎么来

主要文件:

verl/trainer/core_algos.py

标准 GRPO advantage 逻辑是:

scores = token_level_rewards.sum(-1)

按 uid 分组:
  同一个 prompt 的 n 条 rollout 共享 uid。

对每个 uid group:
  A^E_i = (R_i - mean(R_group)) / (std(R_group) + eps)

然后:
  A^E_i 广播到该 response 的所有有效 token。

所以没有 step reward 时,同一条 response 里的每个 token 都拿同一个 advantage。

这正是当前工作要改的地方。

2.5 grpo_step:在 A^E 上加 A^S

主要文件:

verl/trainer/core_algos.py
verl/trainer/ray_trainer.py
verl/trainer/step_reward.py

新增 estimator:

AdvantageEstimator.GRPO_STEP = "grpo_step"

核心函数:

episode_adv, _ = compute_grpo_outcome_advantage(...)

if step_adv_raw is None:
    return episode_adv, episode_adv

step_adv = step_adv_raw * response_mask
advantages = episode_adv + step_advantage_w * step_adv
return advantages, advantages

公式就是:

A_token = A^E + omega * A^S_token

其中:

A^E:
  仍然是原始 GRPO,完全复用。

A^S:
  shape = (bs, response_length)
  response 内不同 token 可以不同。
  解析失败、scaffolding、非目标 token 为 0。

这个点非常关键:当前实现不是额外加一个 KL loss,也不是重写 GRPO,而是在 advantage 估计阶段加一条旁路信号。


3. step_adv_raw 的两条来源

当前代码把 step_adv_raw 做成可切换的:

algorithm.adv_estimator: grpo_step
algorithm.step_reward_kind: rule | forward_proxy

3.1 Rule:硬规则 step reward

文件:

verl/trainer/step_reward.py

入口:

build_step_adv_raw
-> build_step_adv_vector
-> compute_step_match_rewards

流程:

  1. decode response。
  2. 容错解析 executable_plan
  3. 解析 GT:
gt_steps_from_close(gt_close)
  1. 对 predicted steps 和 GT steps 做匹配。

支持三种匹配:

exact:
  pred[i] == gt[i] 才算对。

prefix:
  最长正确前缀;第一个错后面全算错。

lcs:
  longest common subsequence,对顺序扰动更宽松。

默认匹配键:

algorithm.step_match_key: action_id

reward 转 advantage:

匹配:+1
不匹配:-1
scaffolding / 非 step token:0
解析失败:整条 A^S = 0,退化为纯 GRPO

这条路和 2505.22050 很接近:都是用 GT action sequence 做 rule-based correctness signal。

但也有差别:

2505.22050:
  rule reward 先变成 response-level scalar reward,再进入 GRPO。
  prefix reward R(n;k) 是整条 response 的 reward。

当前代码 rule:
  rule reward 直接变成 per-token step advantage A^S。
  一条 response 内部可以前两步 +1,第三步 -1。

也就是说,当前 rule 比 2505.22050 更细:不是只给整条轨迹一个分,而是把 step 对错广播到对应 token。

3.2 Forward Proxy:GT-aware teacher-student logp contrast

文件:

verl/trainer/teacher_proxy.py
verl/workers/reward/function.py
verl/workers/reward/verify.py
verl/trainer/step_reward.py

配置:

algorithm.step_reward_kind: forward_proxy
algorithm.step_squash: contrast
algorithm.step_teacher_path: ...
algorithm.step_score_source: action_name | action_id | semantic
algorithm.step_credit_target: action_name | action_id | semantic

3.2.1 为什么要跑在 reward actor 上

最初如果在 driver 上做 teacher forward,会遇到:

driver 进程没有 GPU。
verl/Ray 中 GPU 分给 worker actor。
teacher.to("cuda") 会失败。

所以当前代码把 forward_proxy 放到 reward actor:

ray.get(self.reward_fn.compute_step_adv.remote(...))

reward actor 本来就有 verify model 的 GPU 资源,因此可以 lazy-load teacher scorer。

3.2.2 TeacherLogpScorer 做什么

TeacherLogpScorer.logp_teacher_student 对同一段 predicted response tokens 跑两次 forward:

teacher context:
  image + "Here is a reference successful language plan:"
  + GT reference plan
  + transition
  + predicted response tokens

student context:
  image + "Problem: {instruction}"
  + student instruction
  + predicted response tokens

返回:

teacher_logp[t] = log p_model(response_token_t | image, GT context, response_prefix)
student_logp[t] = log p_model(response_token_t | image, problem context, response_prefix)

然后:

Delta_t = teacher_logp[t] - student_logp[t]

这个 Delta 的语义是:

如果模型看了 GT 后更支持这个 response token,Delta 为正。
如果模型看了 GT 后更不支持这个 response token,Delta 为负。

3.2.3 当前代码里的一个重要现实:teacher 不一定等于 policy

teacher_proxy.pyconfig.py 的注释里仍然写着:

teacher should be same path as policy
same tokenizer, true OPSD self-distillation

但当前运行脚本 examples/qwen25_vl_3b_Domain.sh 实际是:

policy  = Qwen2.5-VL-3B-RobotGPT-R1
teacher = Qwen2.5-VL-3B-Instruct

这意味着当前 forward_proxy 的真实语义不是严格的“policy 自蒸馏 OPSD”,而是:

冻结外部 Instruct teacher 在 GT context 和 no-GT context 下的 logp contrast。

这点要在论文/报告中说准。

如果 teacher 等于 policy:

更像原始 OPSD / self-distillation。
但实测容易因为 policy 对自己生成的 token 过度自信而 logp diff 塌平。

如果 teacher 是未微调 Instruct:

信号更容易有波动。
但它不再代表 policy 自己的 preference shift;
而是外部 teacher 的 GT-conditioned preference shift。

当前 step_adv_heatmaps/_diag.txt 中能看到 forward_proxy 已有波动,例如:

std ~= 0.9 - 2.3
mean 通常为正
min/max 可到约 -15 / +13

这说明“信号活了”,但不自动说明“信号方向一定奖对惩错”。

3.2.4 Delta 怎么变成 step score

文件:

verl/trainer/step_reward.py
build_step_adv_vector_from_logp

先选 score tokens:

step_score_source:
  action_name  # 默认
  action_id
  semantic     # action_id + action_name

对这些 token 聚合:

contrast 模式:
  token_signal = teacher_logp - student_logp
  raw_step = robust_mean(token_signal over score tokens)
  score = tanh(normalize(raw_step) / score_tau)

prob 模式:
  score = 2 * exp(mean teacher_logp) - 1

代码和实验笔记都说明:prob 容易恒平,因为 GT-conditioned teacher 对很多 token 都很自信;真正有意义的是 contrast

3.2.5 score 写回哪些 token

再选 credit target:

step_credit_target:
  action_name
  action_id
  semantic

默认 action_name,一些 ablation 用:

step_score_source: semantic
step_credit_target: action_id
step_action_name_weight: 0.0

这个设计很重要:

score_source 决定“用哪些 token 判断这一步好坏”。
credit_target 决定“训练时把 advantage 写到哪些 token 上”。

例如:

score_source = semantic:
  用 action_id + action_name 一起估计这步是否被支持。

credit_target = action_id:
  主要更新离散动作选择,而不是训练自然语言措辞。

这是当前实现相对 StepOPSD 的一个具身 JSON plan 特化点。


4. Token span 对齐:当前实现的工程核心

step reward 最终要进入 PPO/GRPO loss,因此必须是 token 级。

当前代码做了以下事情:

response_ids
-> decode response text
-> extract executable_plan
-> locate each step's action_id / action_name character span
-> map character span back to token indices
-> write step score into selected token positions

主要函数:

extract_plan_steps
pred_step_token_spans
_token_char_offsets
_find_action_id_value_span
_find_action_name_value_span
_select_span_tokens

4.1 JSON 解析

extract_plan_steps 能处理:

正常 JSON dict
带 json 标记的 fenced code block
前后有散文的 response
尾部 <|im_end|> / <|endoftext|>
bare list

解析失败返回 None,后续整条 A^S 为 0。

4.2 char offset

_token_char_offsets 先尝试:

tokenizer(text, return_offsets_mapping=True)

如果 re-tokenize 后 input_ids 和原始 response_ids 一致,就用 tokenizer offset。

如果不一致,则降级为 prefix decode:

逐 token decode ids[:t+1]
用字符串长度差构造 offsets

这样比“decode 后重编码”更稳,因为 Qwen/VL tokenizer 在 special token、空格清理、JSON 标点附近可能 round-trip 不一致。

4.3 step span

代码先找到 "executable_plan": 后面的 list 起点,然后按顺序找每一步:

action_id value span
action_name value span

每个 step 得到:

StepTokenSpans(
    action_id_tokens=[...],
    action_name_tokens=[...],
)

然后根据配置选择:

action_id tokens
action_name tokens
semantic tokens = action_id + action_name

4.4 当前 span 设计与 StepOPSD 的差别

StepOPSD 面向的是一般 agent trajectory,常见 span 是:

<action>...</action>
clean_step_no_observation

当前代码面向的是 embodied JSON plan,span 是:

action_id value
action_name value

所以当前实现对 executable_plan 的结构假设更强,但也更精确:可以只训练 action id,不训练 JSON scaffold 或语言描述。


5. Gate 扩展:控制 forward_proxy 信号进来的位置和强度

当前代码不止有 plain forward_proxy,还实现了两类 gate。

5.1 Residual contrast gate

配置:

algorithm.step_gate: residual_contrast
algorithm.step_gate_residual: response_median
algorithm.step_gate_threshold: 0.0
algorithm.step_gate_tau: 1.0
algorithm.step_budget_tau: 1.0
algorithm.step_min_score_tokens: 2

逻辑:

  1. 先对 token_signal 去 response-level median:
d'_t = d_t - median(d over valid response tokens)
  1. 每步算 raw score:
raw_k = robust_mean(d'_t over score tokens)
soft_score_k = tanh(z_k / score_tau)
  1. 再用 unsigned contrast 估计 importance:
imp_k = robust_mean(abs(d'_t) over score tokens)
  1. 组合三个 gate:
decision_gate = sigmoid((imp_z - threshold) / gate_tau)
budget_gate   = clamp(softmax(imp / budget_tau) * num_steps, 0, 1)
confidence    = len_gate * var_gate

gate_k = decision_gate * budget_gate * confidence
  1. 最终:
A^S_k = gate_k * soft_score_k

直觉:

soft_score 决定方向:支持还是反对。
gate 决定强度:这一步是否值得更新。

5.2 Prompt gate

配置:

algorithm.step_gate: prompt
algorithm.step_prompt_gate_mode: generation
algorithm.step_prompt_gate_model_path: ...
algorithm.step_prompt_kl_weight: ...
algorithm.step_use_prompt_causal_gate: true
algorithm.step_use_prompt_redundancy_gate: true

PromptGateScorer 会让 judge 输出每个 predicted step 的:

{
  "decision_gate": 0.0,
  "redundancy_gate": 0.0,
  "causal_gate": 0.0,
  "prompt_kl_score": 0.0,
  "reason": "..."
}

代码把 gate 合成:

gate = decision * (1 - redundancy) * (0.5 + 0.5 * causal)

并可把 prompt score 加进 supervised KL score:

supervised_scores =
  step_supervised_kl_weight * supervised_scores
  + step_prompt_kl_weight * prompt_scores

这个版本的目标是处理 residual contrast 看不懂的情况:

某步 KL 波动很大,但其实只是冗余 find;
某步看起来错,但由于前一步已经错了,后续动作不该被强烈惩罚;
某步是关键 causal action,应该放大。

这已经超出 StepOPSD 原始设计,属于当前实现的额外探索。


6. 当前实现与 2505.22050 的详细对比

6.1 共同点

两者共同点很多:

都做 embodied planning。
都使用 Qwen2.5-VL 系列。
都要求模型输出结构化 multi-step plan。
都使用 GRPO/RFT。
都重视 action sequence 的正确性。
都有格式约束。
都不直接在真实机器人上训练。

6.2 数据和任务形式

2505.22050:

训练:
  SFT 数据来自 Gemini-2.0-flash 蒸馏。
  RFT 数据来自 ALFRED 轨迹分解。

评测:
  Embench。
  EB-ALFRED seen。
  EB-Habitat unseen。

模型:
  Qwen2.5-VL-7B。

当前代码:

训练文件:
  eb_alfred_success_opsd_cot_multimodal_v2_embody.parquet

输入:
  当前图像 + instruction + history + action list。

输出:
  一次性完整 JSON plan。

policy:
  Qwen2.5-VL-3B-RobotGPT-R1。

teacher for forward_proxy:
  Qwen2.5-VL-3B-Instruct。

因此,当前代码更像把 2505.22050 的 embodied planning setting 改成了 3B policy + EasyR1/verl 风格训练。

6.3 Reward 设计差异

2505.22050 的 reward:

R_total = R_format + R_accuracy
R_accuracy = prefix curve n(n+1)/k(k+1)

它是 response-level reward。即使 accuracy 来自 step prefix,最后仍然变成一个 scalar reward 给 GRPO。

当前代码的 reward 分两层:

trajectory-level reward:
  verify model 判断 pred plan 与 GT plan 是否 functional equivalent。
  overall = 0.8 * verify + 0.2 * format。
  写到最后一个 token。

step-level A^S:
  rule: action_id exact/prefix/lcs -> per-token +/-1。
  forward_proxy: teacher-student logp contrast -> per-token [-1,1]。

也就是说,当前代码比 2505.22050 多了一个显式 step advantage 通道。

6.4 GRPO 使用方式差异

2505.22050:

GRPO 直接用 composite reward。
每条 sampled response 得一个 reward。
组内归一化后更新整条 response。

当前代码:

GRPO 仍然用 trajectory reward 得到 A^E。
但在 compute_advantage 之前额外构造 step_adv_raw。
最终 A = A^E + omega * A^S。

所以当前实现是:

GRPO + per-token step advantage shaping

而不是单纯的:

GRPO + rule scalar reward

6.5 输出格式差异

2505.22050 论文要求输出类似:

visual_state_description
reasoning_and_reflection
language_plan
executable_plan

当前代码的 prompt 也要求这些字段,而且 reward JSON parser 优先取 executable_plan

但当前代码的 step credit 只关注 executable_plan 中的:

action_id
action_name

不会给 visual_state_descriptionreasoning_and_reflectionlanguage_plan 里的 token 直接加 step advantage。

这和当前方法目标一致:训练动作决策,而不是训练解释文本。


7. 当前实现与 StepOPSD 的详细对比

7.1 最大共同点

两者都可以概括为:

step-aware advantage shaping for GRPO。

更具体:

都不是把 teacher signal 单独做一个 KL/distillation loss。
都在 rollout 完成后处理 sampled response。
都解析 step/action span。
都把局部 step 信号注入 policy gradient 的 advantage 通道。
都试图修正 trajectory-level sparse reward 的信用分配问题。

如果当前论文主方法强调 forward_proxy,那和 StepOPSD 的相似度很高,不能写成“完全不同路线”。

7.2 Hindsight / teacher 来源

StepOPSD:

teacher context = causal prefix + peer-trajectory hindsight
teacher model   = stale reference policy

peer-trajectory hindsight:
  同一个 GRPO group 中,如果有成功 rollout,
  用第一个成功 peer 给失败 trajectory 提供 hindsight。

当前 forward_proxy:

teacher context = image + GT reference plan + transition
student context = image + problem instruction
teacher model   = step_teacher_path 指定的模型

当前脚本实际:

teacher = 未微调 Qwen2.5-VL-3B-Instruct
policy  = Qwen2.5-VL-3B-RobotGPT-R1

差异:

StepOPSD 更通用:
  不要求数据集中有显式 GT action plan。
  只要 group 里有 successful peer。

当前代码监督更强:
  直接使用数据集 GT reference plan。
  更适合 EB-ALFRED 这种有 reference trajectory 的任务。

7.3 Delta 到 advantage 的变换方式

StepOPSD:

Delta -> token weight w
w > 0
A_tilde = (1-lambda)A + lambda(wA)

性质:

只改变 A 的大小。
不改变 A 的正负。
如果整条 trajectory 的 GRPO advantage 是负的,
局部 step 也不会被改成正方向,只能少罚或多罚。

当前代码:

Delta -> step scalar A^S_k = tanh(...)
A = A^E + omega * A^S_k

性质:

可以改变局部 token 的训练方向。
如果整条 trajectory A^E 为负,但某一步 rule/teacher 认为是对的,
该 step 的 token 可能被加到正方向。

如果整条 trajectory A^E 为正,但某一步是错的,
该 step 的 token 可能被拉成负方向。

这是当前实现和 StepOPSD 最核心的算法差异:

StepOPSD:
  sign-preserving multiplicative reweighting。

当前代码:
  additive local step advantage。

7.4 step 粒度

StepOPSD:

token-level Delta
token-level weight
step normalization 用来控制每个 step 的 credit budget

当前代码:

先把 token_signal 聚合成 step scalar。
再把 step scalar 写回 action_id/action_name/semantic tokens。

当前代码更强调:

step 是决策单位。
token 只是承载这个 step 决策的优化位置。

这和 embodied action plan 更匹配,因为 {"action_id": 133, "action_name": ...} 整体是一个动作选择。

7.5 span 定义

StepOPSD:

ALFWorld:
  action_only

Search-QA:
  clean_step_no_observation

当前代码:

pred_step_token_spans:
  action_id_tokens
  action_name_tokens
  semantic_tokens = union

这不是泛化的 agent transcript span,而是 embodied JSON plan 的结构化 span。

7.6 normalization / clipping

StepOPSD 有两个核心 knob:

alpha_clip:
  控制局部 weight trust region。

lambda_mix:
  控制 shaped signal 与原始 advantage 的混合强度。

当前代码的 knob:

step_advantage_w:
  A^S 总权重 omega。

step_score_tau:
  tanh 温度。

step_score_norm:
  none / per_response。

step_gate:
  none / residual_contrast / prompt。

step_budget_tau:
  residual gate 的 per-response budget 控制。

当前代码没有实现 StepOPSD 同款:

w = clip(2 * sigmoid(sign(A) * Delta), 1-alpha, 1+alpha)

也没有完全等价的:

equal_step_mean_abs

但 residual gate 里的 budget gate 有类似“不要让所有 step 都满强度更新”的意图。

7.7 对原始 rollout 的干预

两者都不改 rollout:

先采样 rollout。
再做 post-rollout step credit。
最后更新 policy。

这点是共同点。


8. 当前实现的优点、风险和需要说准的地方

8.1 优点

优点 1:把 trajectory reward 和 step credit 分开

当前实现没有破坏原始 verify reward:

verify reward 仍然决定整条 trajectory 的 A^E。
step reward 只作为 A^S 叠加。

因此:

omega = 0 可以退回纯 GRPO。
解析失败可以 A^S=0,不影响主训练。

优点 2:rule step reward 对当前任务非常强

因为 action space 是离散的 action_id,且 GT 中有 reference trajectory:

action_id exact/prefix/lcs 是直接的 correctness signal。

相比 forward_proxy,它:

不需要额外 teacher。
不占 GPU。
不会自蒸馏塌平。
不会把“像正确动作的错误动作”误判为正。

优点 3:forward_proxy 是一个活的软信号对照

当前诊断显示 teacher-student logp diff 不再全平:

std 约 1-2
min/max 有明显波动

这意味着 forward_proxy 至少具备可训练信号,不是全 0 对照。

优点 4:action_id/action_name 分离很适合 embodied plan

当前代码可以:

用 semantic tokens 算分。
只把 credit 写到 action_id token。
不给 JSON scaffold 梯度。

这比直接对整段 JSON 做 KL 更贴近“动作选择”。

8.2 风险

风险 1:forward_proxy 的 Delta 不等于动作正确性

Delta 表示:

teacher 看 GT 后是否更愿意生成这个 token。

它不一定表示:

这个 action 是否真的等于 GT action。

典型问题:

GT 是 find DiningTable。
预测是 find Sofa。
teacher 可能因为 "find a furniture" 这个模式合理而给正 Delta。

所以 forward_proxy 的正确性需要专门验证,不能只看 diff std。

风险 2:外部 teacher 改变了 OPSD 语义

如果 teacher 是 Qwen2.5-VL-3B-Instruct,policy 是 RobotGPT-R1

Delta = Instruct(GT context) - Instruct(no-GT context)

而不是:

Delta = policy_or_stale_ref(GT context) - policy(causal context)

这在论文写法上必须说明,否则会被质疑“这不是 StepOPSD/OPSD 的同模型 rescoring”。

风险 3:additive advantage 可能过强

当前:

A = A^E + omega * A^S

如果 omega=1,而 A^E 的 group-normalized magnitude 也大约在 0-1 附近,A^S 可能显著改变更新方向。

这既是能力,也是风险:

能力:
  能在失败轨迹里保留正确 step。

风险:
  如果 A^S 判错,会直接反向更新局部动作。

StepOPSD 的 sign-preserving 设计更保守,当前 additive 设计更激进。

风险 4:rule exact 可能太硬

action_id exact 对当前任务很干净,但也可能:

对等价替代路径不宽容。
对多余 find / 顺序轻微变化不宽容。
对 object instance 探索不宽容。

代码已经提供 prefixlcs,但默认 exact,需要用实验比较。


9. 三方总表

维度 2505.22050 StepOPSD 当前 EasyR1-V4-Embody
主问题 训练 embodied VLM planner agent RL credit assignment one-shot embodied JSON plan 的 step credit assignment
基础算法 SFT + GRPO/RFT GRPO + step-aware OPSD shaping GRPO + additive step advantage
模型 Qwen2.5-VL-7B Qwen3-1.7B / Qwen2.5-3B Qwen2.5-VL-3B RobotGPT-R1
任务 Embench / EB-ALFRED / EB-Habitat ALFWorld / Search-QA EB-ALFRED parquet embodied planning
输出 structured reasoning + executable_plan agent trajectory / actions / search steps JSON with executable_plan
trajectory reward rule format + prefix accuracy env/QA reward verify VLM functional equivalence + format
step 信号 prefix correctness 汇总成 scalar teacher-student logp gap rule action_id 或 forward_proxy logp gap
step 信号进入方式 作为 response reward 的一部分 multiplicative reweight A additive A = A^E + omega A^S
是否保留 A 符号 GRPO scalar reward 决定 否,可能局部翻转
teacher / hindsight Gemini 用于 SFT,不是 RFT teacher successful peer + stale ref GT reference + configured teacher
span action sequence prefix action-centered step action_id/action_name token span
是否需要 GT step 需要 gold sequence 不需要显式 GT,依赖 peer success rule 需要;forward_proxy 使用 GT context
是否额外 KL loss
最大风险 reward 仍较粗,长程困难 与当前方法撞车点强 forward_proxy 正确性、additive 过强、GT 依赖

10. 应该如何给当前方法定位

10.1 不建议的写法

如果主方法是 forward_proxy,不建议写:

We propose to use OPSD-style teacher-student logp gap for step-level GRPO advantage shaping.

这个表述和 StepOPSD 撞得太正。

也不建议写:

Our method is fundamentally different from StepOPSD.

因为两者确实同属 step-aware OPSD advantage shaping。

10.2 更准确的写法

可以这样定位:

We study step-level credit assignment for one-shot embodied trajectory planning,
where a VLM generates a structured JSON executable plan in a single response.
Unlike general multi-turn agent trajectories, the decision variables are explicit
action_id/action_name fields. We inject step-local advantages into GRPO by mapping
either GT action matching or GT-reference-privileged teacher contrast onto these
structured action spans.

中文表达:

我们不是提出“OPSD 信号用于 advantage shaping”这个大方向本身;
这个方向 StepOPSD 已经非常接近。

我们的具体切入点是:
在 one-shot embodied JSON trajectory planning 中,
利用显式 action_id/action_name 结构,把 GT-reference 或 rule step signal
映射到动作字段 token,并以 additive local step advantage 的方式注入 GRPO。

10.3 当前实现最清楚的贡献点

可以强调:

  1. 任务形态差异
one-shot embodied trajectory planning,不是一般 multi-turn agent transcript。
  1. 结构化 action span
显式区分 action_id 和 action_name。
可以 score semantic tokens,但只 credit action_id。
  1. additive local advantage
允许局部 step 改变更新方向。
这和 StepOPSD 的 sign-preserving multiplicative reweighting 不同。
  1. rule vs forward_proxy 的系统比较
当任务有干净离散 action_id GT 时,hard rule step reward 可能比 KL soft proxy 更可靠。
forward_proxy 作为软信号和无 step verifier 场景的泛化对照。
  1. gate 机制
residual gate / prompt gate 用来控制 soft KL 信号进入 action tokens 的强度。

11. 建议补的实验和分析

11.1 必做:四个主 baseline

建议最小实验表:

1. GRPO
2. GRPO + rule step reward
3. GRPO + forward_proxy additive
4. GRPO + forward_proxy + residual gate

如果资源允许,再加:

5. GRPO + forward_proxy + prompt gate
6. GRPO + StepOPSD-style multiplicative reweighting

11.2 强烈建议实现 StepOPSD-style multiplicative baseline

为了和 StepOPSD 讲清楚,建议在当前代码里加一个模式:

step_injection_mode:
  additive      # current
  multiplicative_stepopsd

multiplicative 公式:

Delta_step 或 Delta_token 来自 forward_proxy。
w = clip(2 * sigmoid(sign(A^E) * Delta), 1 - alpha_clip, 1 + alpha_clip)
A = (1 - lambda_mix) * A^E + lambda_mix * (w * A^E)

然后比较:

additive 是否真的优于 sign-preserving?
additive 的 sign flip 是否带来收益?

这个实验对论文非常关键,因为它直接回应 StepOPSD。

11.3 sign flip 统计

当前 additive 最大差异是可能局部翻转方向。建议统计:

sign_flip_ratio =
  mean[ sign(A^E + omega*A^S) != sign(A^E) over action tokens ]

并分组看:

成功 trajectory vs 失败 trajectory
正确 step vs 错误 step
Heat/Cool/Clean/PickTwo 等长程任务

如果能证明:

失败轨迹中的正确前缀 step 被 additive 保留下来;
成功轨迹中的错误/冗余 step 被局部压低;

那就是相对 StepOPSD 的强证据。

11.4 forward_proxy 正确性分析

不要只看:

teacher-student diff std

还要看:

rule match = 1 的 step,forward_proxy A^S 分布。
rule match = 0 的 step,forward_proxy A^S 分布。

理想情况:

correct steps: mean A^S > 0
wrong steps:   mean A^S < 0

如果 wrong steps 也大量为正,说明 forward_proxy 更像 style/trajectory prior,而不是 correctness signal。

11.5 rule mode 消融

比较:

exact
prefix
lcs

可能结论:

exact:
  信号干净但太硬。

prefix:
  更符合 embodied execution,一旦前面错了后面不该继续强判。

lcs:
  对顺序和多余 find 更宽容,但可能奖励不可执行的乱序计划。

11.6 credit target 消融

比较:

score_source=action_name, credit_target=action_name
score_source=semantic,    credit_target=action_id
score_source=semantic,    credit_target=semantic

重点看:

只训练 action_id 是否比训练 action_name 更稳定。
action_name token 是否引入措辞/格式梯度噪声。

12. 当前代码路径速查

12.1 数据和 prompt

verl/utils/dataset.py
  embodied prompt 构造
  image processor
  multi_modal_data
  ground_truth 包装

12.2 trajectory reward

verl/workers/reward/function.py
  compute_reward_batch
  verify prompt
  overall = 0.8 * accuracy + 0.2 * format
  reward 写最后一个 token

verl/workers/reward/verify.py
  ask_llm
  load_verify / load_verify_qwen3

12.3 step reward / step advantage

verl/trainer/step_reward.py
  extract_plan_steps
  compute_step_match_rewards
  pred_step_token_spans
  build_step_adv_vector
  build_step_adv_vector_from_logp
  build_step_adv_raw

12.4 forward_proxy teacher

verl/trainer/teacher_proxy.py
  TeacherLogpScorer
  PromptGateScorer

verl/workers/reward/verify.py
  step_logp_vl

verl/workers/reward/function.py
  AutoRewardManager.compute_step_adv

12.5 GRPO 接线

verl/trainer/core_algos.py
  AdvantageEstimator.GRPO_STEP
  compute_grpo_step_outcome_advantage

verl/trainer/ray_trainer.py
  compute_advantage
  _build_step_adv
  _dump_adv_heatmaps
  _save_total_adv_heatmaps

12.6 配置和脚本

verl/trainer/config.py
  AlgorithmConfig 中所有 step_* 字段

examples/qwen25_vl_3b_Domain.sh
  当前 forward_proxy 主运行脚本

examples/qwen25_vl_3b_residual_gate_ablation.sh
  residual gate 消融

examples/qwen25_vl_3b_prompt_gate_ablation.sh
  prompt gate 消融

13. 最后总结

如果用一句话串起来:

2505.22050 告诉我们 embodied planning 可以用 SFT + rule reward + GRPO 训;
StepOPSD 告诉我们长轨迹 agent RL 的关键是 step-aware advantage shaping;
当前 EasyR1-V4-Embody 把这两个方向接到一起:
在 one-shot embodied JSON plan 上,用 verify model 给整轨 reward,
再用 rule 或 GT-aware forward_proxy 给 executable_plan 的 action spans 加 step-local advantage。

当前实现最应该讲清楚的不是“我们用了 GRPO”,也不是“我们用了 OPSD”,而是:

我们如何把 embodied JSON plan 中的结构化动作字段变成可训练的 token-level step credit;
以及 additive step advantage 相比 StepOPSD sign-preserving reweighting 的差异、收益和风险。

当前代码已经具备一套完整实验框架:

GRPO
GRPO + rule
GRPO + forward_proxy
GRPO + residual gate
GRPO + prompt gate

下一步最有价值的是补:

StepOPSD-style multiplicative baseline
sign-flip analysis
forward_proxy correctness-vs-rule correlation
rule exact/prefix/lcs 消融
credit target 消融

这些实验能把“和两篇论文的关系”从文字解释变成可验证证据。