| # 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_step`、`rule` step reward、`forward_proxy`、residual gate / prompt gate。 |
|
|
| 结论先放在前面: |
|
|
| ```text |
| 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。 |
|
|
| 它的方案是两阶段: |
|
|
| ```text |
| 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 是规则式的: |
|
|
| ```text |
| 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: |
|
|
| ```text |
| 一条长 rollout 可能因为一个局部 action 错了而失败。 |
| 标准 GRPO 只有一个 terminal reward。 |
| 这个 reward 被广播到整条 response 的所有 token。 |
| 结果是:模型知道整条轨迹不好,但不知道是哪一步导致不好。 |
| ``` |
|
|
| StepOPSD 的核心流程: |
|
|
| ```text |
| 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 |
| ``` |
|
|
| 它的信号是: |
|
|
| ```text |
| Delta_{k,j} |
| = log pi_T(z_{k,j} | hindsight context, prefix) |
| - log pi_S(z_{k,j} | causal context, prefix) |
| ``` |
|
|
| 它的 advantage 注入方式是: |
|
|
| ```text |
| 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) |
| ``` |
|
|
| 关键性质: |
|
|
| ```text |
| 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” |
|
|
| 当前仓库的任务形态: |
|
|
| ```text |
| 输入: |
| 图像 + human instruction + action history + action list |
| |
| 输出: |
| 一次性生成完整 JSON: |
| { |
| "visual_state_description": "...", |
| "reasoning_and_reflection": "...", |
| "language_plan": [...], |
| "executable_plan": [ |
| {"action_id": 133, "action_name": "..."}, |
| ... |
| ] |
| } |
| ``` |
|
|
| 注意:当前代码不是多轮在线环境交互。它是一行样本生成一整条后续 trajectory plan。 |
|
|
| 代码里的核心目标: |
|
|
| ```text |
| 原来只有整条 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 |
|
|
| 主要文件: |
|
|
| ```text |
| verl/utils/dataset.py |
| ``` |
|
|
| 当前 embodied dataset 的 `__getitem__` 做了这些事: |
|
|
| 1. 从 parquet 行里读: |
|
|
| ```text |
| instruction |
| history |
| action_id # 可用动作列表 |
| image |
| gt_bbox |
| gt_action |
| gt_input_text |
| gt_action_close / gt_action_open 等字段保留在 row_dict 中 |
| ``` |
|
|
| 2. 构造 prompt: |
|
|
| ```text |
| 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 |
| ``` |
|
|
| 3. 把 `<image>` 替换为 Qwen-VL 视觉占位: |
|
|
| ```text |
| <|vision_start|><|image_pad|><|vision_end|> |
| ``` |
|
|
| 4. 用 processor 编码图像和 prompt,生成: |
|
|
| ```text |
| 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,内部包含: |
|
|
| ```text |
| action |
| gt_bbox |
| input_text |
| gt_open_ans |
| ``` |
|
|
| 其中 `input_text` 里还能解析出 GT 的 `executable_plan`。 |
|
|
| ### 2.2 Rollout:模型一次性生成 response |
|
|
| 训练主循环在: |
|
|
| ```text |
| verl/trainer/ray_trainer.py |
| ``` |
|
|
| 核心顺序: |
|
|
| ```text |
| 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 给整条计划打分 |
|
|
| 主要文件: |
|
|
| ```text |
| verl/workers/reward/function.py |
| verl/workers/reward/verify.py |
| ``` |
|
|
| `compute_reward_batch` 的行为是: |
|
|
| 1. decode response。 |
| 2. 尝试解析 JSON。 |
| 3. 从 response 中提取: |
|
|
| ```text |
| executable_plan |
| ``` |
|
|
| 4. 从 ground truth 中提取: |
|
|
| ```text |
| ground_truth_open = json.loads(ground_truth["input_text"])["executable_plan"] |
| ``` |
|
|
| 5. 构造 verify prompt: |
|
|
| ```text |
| 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. |
| ``` |
|
|
| 6. 调用 `ask_llm`,用 verify VLM 生成一个 token,取 True / False 相关概率。 |
|
|
| 7. 得到: |
|
|
| ```text |
| format = 1.0 if JSON parse ok else 0.0 |
| accuracy = verify probability, False 会转成负值 |
| overall = 0.8 * accuracy + 0.2 * format |
| ``` |
|
|
| 8. 把 `overall` 写到最后一个 response token: |
|
|
| ```text |
| reward_tensor[i, cur_response_length - 1] = score["overall"] |
| ``` |
|
|
| 这意味着整条 trajectory 的 reward 在张量里仍然是一个 terminal scalar。 |
|
|
| ### 2.4 原始 GRPO:A^E 怎么来 |
|
|
| 主要文件: |
|
|
| ```text |
| verl/trainer/core_algos.py |
| ``` |
|
|
| 标准 GRPO advantage 逻辑是: |
|
|
| ```text |
| 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 |
| |
| 主要文件: |
| |
| ```text |
| verl/trainer/core_algos.py |
| verl/trainer/ray_trainer.py |
| verl/trainer/step_reward.py |
| ``` |
| |
| 新增 estimator: |
| |
| ```text |
| AdvantageEstimator.GRPO_STEP = "grpo_step" |
| ``` |
| |
| 核心函数: |
| |
| ```python |
| 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 |
| ``` |
| |
| 公式就是: |
| |
| ```text |
| A_token = A^E + omega * A^S_token |
| ``` |
| |
| 其中: |
| |
| ```text |
| 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` 做成可切换的: |
| |
| ```yaml |
| algorithm.adv_estimator: grpo_step |
| algorithm.step_reward_kind: rule | forward_proxy |
| ``` |
| |
| ### 3.1 Rule:硬规则 step reward |
| |
| 文件: |
| |
| ```text |
| verl/trainer/step_reward.py |
| ``` |
| |
| 入口: |
| |
| ```text |
| build_step_adv_raw |
| -> build_step_adv_vector |
| -> compute_step_match_rewards |
| ``` |
| |
| 流程: |
| |
| 1. decode response。 |
| 2. 容错解析 `executable_plan`。 |
| 3. 解析 GT: |
|
|
| ```text |
| gt_steps_from_close(gt_close) |
| ``` |
|
|
| 4. 对 predicted steps 和 GT steps 做匹配。 |
|
|
| 支持三种匹配: |
|
|
| ```text |
| exact: |
| pred[i] == gt[i] 才算对。 |
| |
| prefix: |
| 最长正确前缀;第一个错后面全算错。 |
| |
| lcs: |
| longest common subsequence,对顺序扰动更宽松。 |
| ``` |
|
|
| 默认匹配键: |
|
|
| ```yaml |
| algorithm.step_match_key: action_id |
| ``` |
|
|
| reward 转 advantage: |
|
|
| ```text |
| 匹配:+1 |
| 不匹配:-1 |
| scaffolding / 非 step token:0 |
| 解析失败:整条 A^S = 0,退化为纯 GRPO |
| ``` |
|
|
| 这条路和 2505.22050 很接近:都是用 GT action sequence 做 rule-based correctness signal。 |
|
|
| 但也有差别: |
|
|
| ```text |
| 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 |
|
|
| 文件: |
|
|
| ```text |
| verl/trainer/teacher_proxy.py |
| verl/workers/reward/function.py |
| verl/workers/reward/verify.py |
| verl/trainer/step_reward.py |
| ``` |
|
|
| 配置: |
|
|
| ```yaml |
| 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,会遇到: |
|
|
| ```text |
| driver 进程没有 GPU。 |
| verl/Ray 中 GPU 分给 worker actor。 |
| teacher.to("cuda") 会失败。 |
| ``` |
|
|
| 所以当前代码把 forward_proxy 放到 reward actor: |
| |
| ```text |
| 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: |
| |
| ```text |
| 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 |
| ``` |
| |
| 返回: |
| |
| ```text |
| 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) |
| ``` |
| |
| 然后: |
| |
| ```text |
| Delta_t = teacher_logp[t] - student_logp[t] |
| ``` |
| |
| 这个 Delta 的语义是: |
| |
| ```text |
| 如果模型看了 GT 后更支持这个 response token,Delta 为正。 |
| 如果模型看了 GT 后更不支持这个 response token,Delta 为负。 |
| ``` |
| |
| #### 3.2.3 当前代码里的一个重要现实:teacher 不一定等于 policy |
| |
| `teacher_proxy.py` 和 `config.py` 的注释里仍然写着: |
|
|
| ```text |
| teacher should be same path as policy |
| same tokenizer, true OPSD self-distillation |
| ``` |
|
|
| 但当前运行脚本 `examples/qwen25_vl_3b_Domain.sh` 实际是: |
|
|
| ```text |
| policy = Qwen2.5-VL-3B-RobotGPT-R1 |
| teacher = Qwen2.5-VL-3B-Instruct |
| ``` |
|
|
| 这意味着当前 forward_proxy 的真实语义不是严格的“policy 自蒸馏 OPSD”,而是: |
| |
| ```text |
| 冻结外部 Instruct teacher 在 GT context 和 no-GT context 下的 logp contrast。 |
| ``` |
| |
| 这点要在论文/报告中说准。 |
| |
| 如果 teacher 等于 policy: |
| |
| ```text |
| 更像原始 OPSD / self-distillation。 |
| 但实测容易因为 policy 对自己生成的 token 过度自信而 logp diff 塌平。 |
| ``` |
| |
| 如果 teacher 是未微调 Instruct: |
| |
| ```text |
| 信号更容易有波动。 |
| 但它不再代表 policy 自己的 preference shift; |
| 而是外部 teacher 的 GT-conditioned preference shift。 |
| ``` |
| |
| 当前 `step_adv_heatmaps/_diag.txt` 中能看到 forward_proxy 已有波动,例如: |
| |
| ```text |
| std ~= 0.9 - 2.3 |
| mean 通常为正 |
| min/max 可到约 -15 / +13 |
| ``` |
| |
| 这说明“信号活了”,但不自动说明“信号方向一定奖对惩错”。 |
| |
| #### 3.2.4 Delta 怎么变成 step score |
| |
| 文件: |
| |
| ```text |
| verl/trainer/step_reward.py |
| build_step_adv_vector_from_logp |
| ``` |
| |
| 先选 score tokens: |
| |
| ```yaml |
| step_score_source: |
| action_name # 默认 |
| action_id |
| semantic # action_id + action_name |
| ``` |
| |
| 对这些 token 聚合: |
| |
| ```text |
| 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: |
| |
| ```yaml |
| step_credit_target: |
| action_name |
| action_id |
| semantic |
| ``` |
| |
| 默认 `action_name`,一些 ablation 用: |
| |
| ```yaml |
| step_score_source: semantic |
| step_credit_target: action_id |
| step_action_name_weight: 0.0 |
| ``` |
| |
| 这个设计很重要: |
| |
| ```text |
| score_source 决定“用哪些 token 判断这一步好坏”。 |
| credit_target 决定“训练时把 advantage 写到哪些 token 上”。 |
| ``` |
| |
| 例如: |
| |
| ```text |
| score_source = semantic: |
| 用 action_id + action_name 一起估计这步是否被支持。 |
| |
| credit_target = action_id: |
| 主要更新离散动作选择,而不是训练自然语言措辞。 |
| ``` |
| |
| 这是当前实现相对 StepOPSD 的一个具身 JSON plan 特化点。 |
| |
| --- |
| |
| ## 4. Token span 对齐:当前实现的工程核心 |
| |
| step reward 最终要进入 PPO/GRPO loss,因此必须是 token 级。 |
| |
| 当前代码做了以下事情: |
| |
| ```text |
| 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 |
| ``` |
| |
| 主要函数: |
| |
| ```text |
| 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` 能处理: |
| |
| ```text |
| 正常 JSON dict |
| 带 json 标记的 fenced code block |
| 前后有散文的 response |
| 尾部 <|im_end|> / <|endoftext|> |
| bare list |
| ``` |
| |
| 解析失败返回 `None`,后续整条 A^S 为 0。 |
| |
| ### 4.2 char offset |
| |
| `_token_char_offsets` 先尝试: |
|
|
| ```text |
| tokenizer(text, return_offsets_mapping=True) |
| ``` |
|
|
| 如果 re-tokenize 后 input_ids 和原始 response_ids 一致,就用 tokenizer offset。 |
|
|
| 如果不一致,则降级为 prefix decode: |
|
|
| ```text |
| 逐 token decode ids[:t+1] |
| 用字符串长度差构造 offsets |
| ``` |
|
|
| 这样比“decode 后重编码”更稳,因为 Qwen/VL tokenizer 在 special token、空格清理、JSON 标点附近可能 round-trip 不一致。 |
|
|
| ### 4.3 step span |
|
|
| 代码先找到 `"executable_plan":` 后面的 list 起点,然后按顺序找每一步: |
|
|
| ```text |
| action_id value span |
| action_name value span |
| ``` |
|
|
| 每个 step 得到: |
|
|
| ```python |
| StepTokenSpans( |
| action_id_tokens=[...], |
| action_name_tokens=[...], |
| ) |
| ``` |
|
|
| 然后根据配置选择: |
|
|
| ```text |
| action_id tokens |
| action_name tokens |
| semantic tokens = action_id + action_name |
| ``` |
|
|
| ### 4.4 当前 span 设计与 StepOPSD 的差别 |
|
|
| StepOPSD 面向的是一般 agent trajectory,常见 span 是: |
|
|
| ```text |
| <action>...</action> |
| clean_step_no_observation |
| ``` |
|
|
| 当前代码面向的是 embodied JSON plan,span 是: |
|
|
| ```text |
| 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 |
|
|
| 配置: |
|
|
| ```yaml |
| 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: |
| |
| ```text |
| d'_t = d_t - median(d over valid response tokens) |
| ``` |
| |
| 2. 每步算 raw score: |
| |
| ```text |
| raw_k = robust_mean(d'_t over score tokens) |
| soft_score_k = tanh(z_k / score_tau) |
| ``` |
| |
| 3. 再用 unsigned contrast 估计 importance: |
| |
| ```text |
| imp_k = robust_mean(abs(d'_t) over score tokens) |
| ``` |
| |
| 4. 组合三个 gate: |
| |
| ```text |
| 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 |
| ``` |
| |
| 5. 最终: |
| |
| ```text |
| A^S_k = gate_k * soft_score_k |
| ``` |
| |
| 直觉: |
| |
| ```text |
| soft_score 决定方向:支持还是反对。 |
| gate 决定强度:这一步是否值得更新。 |
| ``` |
| |
| ### 5.2 Prompt gate |
| |
| 配置: |
| |
| ```yaml |
| 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 的: |
| |
| ```json |
| { |
| "decision_gate": 0.0, |
| "redundancy_gate": 0.0, |
| "causal_gate": 0.0, |
| "prompt_kl_score": 0.0, |
| "reason": "..." |
| } |
| ``` |
| |
| 代码把 gate 合成: |
| |
| ```text |
| gate = decision * (1 - redundancy) * (0.5 + 0.5 * causal) |
| ``` |
| |
| 并可把 prompt score 加进 supervised KL score: |
| |
| ```text |
| supervised_scores = |
| step_supervised_kl_weight * supervised_scores |
| + step_prompt_kl_weight * prompt_scores |
| ``` |
| |
| 这个版本的目标是处理 residual contrast 看不懂的情况: |
| |
| ```text |
| 某步 KL 波动很大,但其实只是冗余 find; |
| 某步看起来错,但由于前一步已经错了,后续动作不该被强烈惩罚; |
| 某步是关键 causal action,应该放大。 |
| ``` |
| |
| 这已经超出 StepOPSD 原始设计,属于当前实现的额外探索。 |
| |
| --- |
| |
| ## 6. 当前实现与 2505.22050 的详细对比 |
| |
| ### 6.1 共同点 |
| |
| 两者共同点很多: |
| |
| ```text |
| 都做 embodied planning。 |
| 都使用 Qwen2.5-VL 系列。 |
| 都要求模型输出结构化 multi-step plan。 |
| 都使用 GRPO/RFT。 |
| 都重视 action sequence 的正确性。 |
| 都有格式约束。 |
| 都不直接在真实机器人上训练。 |
| ``` |
| |
| ### 6.2 数据和任务形式 |
| |
| 2505.22050: |
| |
| ```text |
| 训练: |
| SFT 数据来自 Gemini-2.0-flash 蒸馏。 |
| RFT 数据来自 ALFRED 轨迹分解。 |
|
|
| 评测: |
| Embench。 |
| EB-ALFRED seen。 |
| EB-Habitat unseen。 |
|
|
| 模型: |
| Qwen2.5-VL-7B。 |
| ``` |
| |
| 当前代码: |
| |
| ```text |
| 训练文件: |
| 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: |
| |
| ```text |
| 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 分两层: |
| |
| ```text |
| 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: |
| |
| ```text |
| GRPO 直接用 composite reward。 |
| 每条 sampled response 得一个 reward。 |
| 组内归一化后更新整条 response。 |
| ``` |
| |
| 当前代码: |
| |
| ```text |
| GRPO 仍然用 trajectory reward 得到 A^E。 |
| 但在 compute_advantage 之前额外构造 step_adv_raw。 |
| 最终 A = A^E + omega * A^S。 |
| ``` |
| |
| 所以当前实现是: |
| |
| ```text |
| GRPO + per-token step advantage shaping |
| ``` |
| |
| 而不是单纯的: |
| |
| ```text |
| GRPO + rule scalar reward |
| ``` |
| |
| ### 6.5 输出格式差异 |
| |
| 2505.22050 论文要求输出类似: |
| |
| ```text |
| visual_state_description |
| reasoning_and_reflection |
| language_plan |
| executable_plan |
| ``` |
| |
| 当前代码的 prompt 也要求这些字段,而且 reward JSON parser 优先取 `executable_plan`。 |
|
|
| 但当前代码的 step credit 只关注 `executable_plan` 中的: |
|
|
| ```text |
| action_id |
| action_name |
| ``` |
|
|
| 不会给 `visual_state_description`、`reasoning_and_reflection`、`language_plan` 里的 token 直接加 step advantage。 |
|
|
| 这和当前方法目标一致:训练动作决策,而不是训练解释文本。 |
|
|
| --- |
|
|
| ## 7. 当前实现与 StepOPSD 的详细对比 |
|
|
| ### 7.1 最大共同点 |
|
|
| 两者都可以概括为: |
|
|
| ```text |
| step-aware advantage shaping for GRPO。 |
| ``` |
|
|
| 更具体: |
|
|
| ```text |
| 都不是把 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: |
|
|
| ```text |
| teacher context = causal prefix + peer-trajectory hindsight |
| teacher model = stale reference policy |
| |
| peer-trajectory hindsight: |
| 同一个 GRPO group 中,如果有成功 rollout, |
| 用第一个成功 peer 给失败 trajectory 提供 hindsight。 |
| ``` |
|
|
| 当前 forward_proxy: |
| |
| ```text |
| teacher context = image + GT reference plan + transition |
| student context = image + problem instruction |
| teacher model = step_teacher_path 指定的模型 |
| ``` |
| |
| 当前脚本实际: |
| |
| ```text |
| teacher = 未微调 Qwen2.5-VL-3B-Instruct |
| policy = Qwen2.5-VL-3B-RobotGPT-R1 |
| ``` |
| |
| 差异: |
| |
| ```text |
| StepOPSD 更通用: |
| 不要求数据集中有显式 GT action plan。 |
| 只要 group 里有 successful peer。 |
| |
| 当前代码监督更强: |
| 直接使用数据集 GT reference plan。 |
| 更适合 EB-ALFRED 这种有 reference trajectory 的任务。 |
| ``` |
| |
| ### 7.3 Delta 到 advantage 的变换方式 |
| |
| StepOPSD: |
| |
| ```text |
| Delta -> token weight w |
| w > 0 |
| A_tilde = (1-lambda)A + lambda(wA) |
| ``` |
| |
| 性质: |
| |
| ```text |
| 只改变 A 的大小。 |
| 不改变 A 的正负。 |
| 如果整条 trajectory 的 GRPO advantage 是负的, |
| 局部 step 也不会被改成正方向,只能少罚或多罚。 |
| ``` |
| |
| 当前代码: |
| |
| ```text |
| Delta -> step scalar A^S_k = tanh(...) |
| A = A^E + omega * A^S_k |
| ``` |
| |
| 性质: |
| |
| ```text |
| 可以改变局部 token 的训练方向。 |
| 如果整条 trajectory A^E 为负,但某一步 rule/teacher 认为是对的, |
| 该 step 的 token 可能被加到正方向。 |
|
|
| 如果整条 trajectory A^E 为正,但某一步是错的, |
| 该 step 的 token 可能被拉成负方向。 |
| ``` |
| |
| 这是当前实现和 StepOPSD 最核心的算法差异: |
| |
| ```text |
| StepOPSD: |
| sign-preserving multiplicative reweighting。 |
|
|
| 当前代码: |
| additive local step advantage。 |
| ``` |
| |
| ### 7.4 step 粒度 |
| |
| StepOPSD: |
| |
| ```text |
| token-level Delta |
| token-level weight |
| step normalization 用来控制每个 step 的 credit budget |
| ``` |
| |
| 当前代码: |
| |
| ```text |
| 先把 token_signal 聚合成 step scalar。 |
| 再把 step scalar 写回 action_id/action_name/semantic tokens。 |
| ``` |
| |
| 当前代码更强调: |
| |
| ```text |
| step 是决策单位。 |
| token 只是承载这个 step 决策的优化位置。 |
| ``` |
| |
| 这和 embodied action plan 更匹配,因为 `{"action_id": 133, "action_name": ...}` 整体是一个动作选择。 |
| |
| ### 7.5 span 定义 |
| |
| StepOPSD: |
| |
| ```text |
| ALFWorld: |
| action_only |
|
|
| Search-QA: |
| clean_step_no_observation |
| ``` |
| |
| 当前代码: |
| |
| ```text |
| 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: |
| |
| ```text |
| alpha_clip: |
| 控制局部 weight trust region。 |
|
|
| lambda_mix: |
| 控制 shaped signal 与原始 advantage 的混合强度。 |
| ``` |
| |
| 当前代码的 knob: |
| |
| ```text |
| 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 同款: |
| |
| ```text |
| w = clip(2 * sigmoid(sign(A) * Delta), 1-alpha, 1+alpha) |
| ``` |
| |
| 也没有完全等价的: |
| |
| ```text |
| equal_step_mean_abs |
| ``` |
| |
| 但 residual gate 里的 budget gate 有类似“不要让所有 step 都满强度更新”的意图。 |
| |
| ### 7.7 对原始 rollout 的干预 |
| |
| 两者都不改 rollout: |
| |
| ```text |
| 先采样 rollout。 |
| 再做 post-rollout step credit。 |
| 最后更新 policy。 |
| ``` |
| |
| 这点是共同点。 |
| |
| --- |
| |
| ## 8. 当前实现的优点、风险和需要说准的地方 |
| |
| ### 8.1 优点 |
| |
| #### 优点 1:把 trajectory reward 和 step credit 分开 |
| |
| 当前实现没有破坏原始 verify reward: |
| |
| ```text |
| verify reward 仍然决定整条 trajectory 的 A^E。 |
| step reward 只作为 A^S 叠加。 |
| ``` |
| |
| 因此: |
| |
| ```text |
| omega = 0 可以退回纯 GRPO。 |
| 解析失败可以 A^S=0,不影响主训练。 |
| ``` |
| |
| #### 优点 2:rule step reward 对当前任务非常强 |
| |
| 因为 action space 是离散的 `action_id`,且 GT 中有 reference trajectory: |
|
|
| ```text |
| action_id exact/prefix/lcs 是直接的 correctness signal。 |
| ``` |
|
|
| 相比 forward_proxy,它: |
| |
| ```text |
| 不需要额外 teacher。 |
| 不占 GPU。 |
| 不会自蒸馏塌平。 |
| 不会把“像正确动作的错误动作”误判为正。 |
| ``` |
| |
| #### 优点 3:forward_proxy 是一个活的软信号对照 |
|
|
| 当前诊断显示 teacher-student logp diff 不再全平: |
|
|
| ```text |
| std 约 1-2 |
| min/max 有明显波动 |
| ``` |
|
|
| 这意味着 forward_proxy 至少具备可训练信号,不是全 0 对照。 |
| |
| #### 优点 4:action_id/action_name 分离很适合 embodied plan |
| |
| 当前代码可以: |
| |
| ```text |
| 用 semantic tokens 算分。 |
| 只把 credit 写到 action_id token。 |
| 不给 JSON scaffold 梯度。 |
| ``` |
| |
| 这比直接对整段 JSON 做 KL 更贴近“动作选择”。 |
| |
| ### 8.2 风险 |
| |
| #### 风险 1:forward_proxy 的 Delta 不等于动作正确性 |
| |
| Delta 表示: |
| |
| ```text |
| teacher 看 GT 后是否更愿意生成这个 token。 |
| ``` |
| |
| 它不一定表示: |
| |
| ```text |
| 这个 action 是否真的等于 GT action。 |
| ``` |
| |
| 典型问题: |
| |
| ```text |
| 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`: |
| |
| ```text |
| Delta = Instruct(GT context) - Instruct(no-GT context) |
| ``` |
| |
| 而不是: |
| |
| ```text |
| Delta = policy_or_stale_ref(GT context) - policy(causal context) |
| ``` |
| |
| 这在论文写法上必须说明,否则会被质疑“这不是 StepOPSD/OPSD 的同模型 rescoring”。 |
| |
| #### 风险 3:additive advantage 可能过强 |
| |
| 当前: |
| |
| ```text |
| A = A^E + omega * A^S |
| ``` |
| |
| 如果 `omega=1`,而 A^E 的 group-normalized magnitude 也大约在 0-1 附近,A^S 可能显著改变更新方向。 |
| |
| 这既是能力,也是风险: |
| |
| ```text |
| 能力: |
| 能在失败轨迹里保留正确 step。 |
| |
| 风险: |
| 如果 A^S 判错,会直接反向更新局部动作。 |
| ``` |
| |
| StepOPSD 的 sign-preserving 设计更保守,当前 additive 设计更激进。 |
| |
| #### 风险 4:rule exact 可能太硬 |
| |
| `action_id` exact 对当前任务很干净,但也可能: |
|
|
| ```text |
| 对等价替代路径不宽容。 |
| 对多余 find / 顺序轻微变化不宽容。 |
| 对 object instance 探索不宽容。 |
| ``` |
|
|
| 代码已经提供 `prefix` 和 `lcs`,但默认 `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,不建议写: |
| |
| ```text |
| We propose to use OPSD-style teacher-student logp gap for step-level GRPO advantage shaping. |
| ``` |
| |
| 这个表述和 StepOPSD 撞得太正。 |
| |
| 也不建议写: |
| |
| ```text |
| Our method is fundamentally different from StepOPSD. |
| ``` |
| |
| 因为两者确实同属 step-aware OPSD advantage shaping。 |
| |
| ### 10.2 更准确的写法 |
| |
| 可以这样定位: |
| |
| ```text |
| 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. |
| ``` |
| |
| 中文表达: |
| |
| ```text |
| 我们不是提出“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. **任务形态差异**: |
| |
| ```text |
| one-shot embodied trajectory planning,不是一般 multi-turn agent transcript。 |
| ``` |
| |
| 2. **结构化 action span**: |
| |
| ```text |
| 显式区分 action_id 和 action_name。 |
| 可以 score semantic tokens,但只 credit action_id。 |
| ``` |
| |
| 3. **additive local advantage**: |
| |
| ```text |
| 允许局部 step 改变更新方向。 |
| 这和 StepOPSD 的 sign-preserving multiplicative reweighting 不同。 |
| ``` |
| |
| 4. **rule vs forward_proxy 的系统比较**: |
| |
| ```text |
| 当任务有干净离散 action_id GT 时,hard rule step reward 可能比 KL soft proxy 更可靠。 |
| forward_proxy 作为软信号和无 step verifier 场景的泛化对照。 |
| ``` |
| |
| 5. **gate 机制**: |
| |
| ```text |
| residual gate / prompt gate 用来控制 soft KL 信号进入 action tokens 的强度。 |
| ``` |
| |
| --- |
| |
| ## 11. 建议补的实验和分析 |
| |
| ### 11.1 必做:四个主 baseline |
| |
| 建议最小实验表: |
| |
| ```text |
| 1. GRPO |
| 2. GRPO + rule step reward |
| 3. GRPO + forward_proxy additive |
| 4. GRPO + forward_proxy + residual gate |
| ``` |
| |
| 如果资源允许,再加: |
| |
| ```text |
| 5. GRPO + forward_proxy + prompt gate |
| 6. GRPO + StepOPSD-style multiplicative reweighting |
| ``` |
| |
| ### 11.2 强烈建议实现 StepOPSD-style multiplicative baseline |
| |
| 为了和 StepOPSD 讲清楚,建议在当前代码里加一个模式: |
| |
| ```text |
| step_injection_mode: |
| additive # current |
| multiplicative_stepopsd |
| ``` |
| |
| multiplicative 公式: |
| |
| ```text |
| 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) |
| ``` |
| |
| 然后比较: |
| |
| ```text |
| additive 是否真的优于 sign-preserving? |
| additive 的 sign flip 是否带来收益? |
| ``` |
| |
| 这个实验对论文非常关键,因为它直接回应 StepOPSD。 |
| |
| ### 11.3 sign flip 统计 |
| |
| 当前 additive 最大差异是可能局部翻转方向。建议统计: |
| |
| ```text |
| sign_flip_ratio = |
| mean[ sign(A^E + omega*A^S) != sign(A^E) over action tokens ] |
| ``` |
| |
| 并分组看: |
| |
| ```text |
| 成功 trajectory vs 失败 trajectory |
| 正确 step vs 错误 step |
| Heat/Cool/Clean/PickTwo 等长程任务 |
| ``` |
| |
| 如果能证明: |
| |
| ```text |
| 失败轨迹中的正确前缀 step 被 additive 保留下来; |
| 成功轨迹中的错误/冗余 step 被局部压低; |
| ``` |
| |
| 那就是相对 StepOPSD 的强证据。 |
| |
| ### 11.4 forward_proxy 正确性分析 |
|
|
| 不要只看: |
|
|
| ```text |
| teacher-student diff std |
| ``` |
|
|
| 还要看: |
|
|
| ```text |
| rule match = 1 的 step,forward_proxy A^S 分布。 |
| rule match = 0 的 step,forward_proxy A^S 分布。 |
| ``` |
|
|
| 理想情况: |
|
|
| ```text |
| 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 消融 |
| |
| 比较: |
| |
| ```text |
| exact |
| prefix |
| lcs |
| ``` |
| |
| 可能结论: |
| |
| ```text |
| exact: |
| 信号干净但太硬。 |
| |
| prefix: |
| 更符合 embodied execution,一旦前面错了后面不该继续强判。 |
| |
| lcs: |
| 对顺序和多余 find 更宽容,但可能奖励不可执行的乱序计划。 |
| ``` |
| |
| ### 11.6 credit target 消融 |
| |
| 比较: |
| |
| ```text |
| score_source=action_name, credit_target=action_name |
| score_source=semantic, credit_target=action_id |
| score_source=semantic, credit_target=semantic |
| ``` |
| |
| 重点看: |
| |
| ```text |
| 只训练 action_id 是否比训练 action_name 更稳定。 |
| action_name token 是否引入措辞/格式梯度噪声。 |
| ``` |
| |
| --- |
| |
| ## 12. 当前代码路径速查 |
| |
| ### 12.1 数据和 prompt |
| |
| ```text |
| verl/utils/dataset.py |
| embodied prompt 构造 |
| image processor |
| multi_modal_data |
| ground_truth 包装 |
| ``` |
| |
| ### 12.2 trajectory reward |
| |
| ```text |
| 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 |
| |
| ```text |
| 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 |
| |
| ```text |
| 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 接线 |
| |
| ```text |
| 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 配置和脚本 |
| |
| ```text |
| 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. 最后总结 |
| |
| 如果用一句话串起来: |
| |
| ```text |
| 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”,而是: |
| |
| ```text |
| 我们如何把 embodied JSON plan 中的结构化动作字段变成可训练的 token-level step credit; |
| 以及 additive step advantage 相比 StepOPSD sign-preserving reweighting 的差异、收益和风险。 |
| ``` |
| |
| 当前代码已经具备一套完整实验框架: |
| |
| ```text |
| GRPO |
| GRPO + rule |
| GRPO + forward_proxy |
| GRPO + residual gate |
| GRPO + prompt gate |
| ``` |
| |
| 下一步最有价值的是补: |
| |
| ```text |
| StepOPSD-style multiplicative baseline |
| sign-flip analysis |
| forward_proxy correctness-vs-rule correlation |
| rule exact/prefix/lcs 消融 |
| credit target 消融 |
| ``` |
| |
| 这些实验能把“和两篇论文的关系”从文字解释变成可验证证据。 |
| |