SARM(阶段感知奖励建模,Stage-Aware Reward Modeling)是一个面向长时程机器人操作任务、基于视频的奖励建模框架。本指南介绍如何训练 SARM 奖励模型,以及如何选择性地将其与奖励对齐行为克隆(RA-BC)配合使用。
标准的行为克隆会将所有 demonstration 帧一视同仁,但现实世界的机器人 dataset 往往良莠不齐,其中包含迟疑、纠错以及质量参差不齐的轨迹。奖励模型通过从 demonstration 中学习一种可泛化的任务进度概念来解决这一问题:给定视频帧和任务描述,它们预测机器人距离完成任务还有多近(0→1)。这种学习到的“进度信号”可以有多种用途,其中两个有前景的应用是:(1)加权模仿学习(RA-BC),即高进度帧在 policy 训练期间获得更高权重;(2)强化学习,即奖励模型为在线或离线的 policy 改进提供稠密奖励。
SARM 具有以下特性:
SARM 在每一帧上针对紧凑的 stage+tau 目标进行训练:
k ∈ {0, ..., K-1}τ ∈ [0, 1]y = k + τ(这是 dataset 处理器生成的)在 inference 时(以及下游的 RA-BC 中),SARM 使用 dataset 级别的时间比例 α̅_k(存储在 meta/temporal_proportions_*.json 中)将原始的 k + τ 值转换为 [0, 1] 范围内的归一化进度。
这与论文中的公式(2)一致:
progress_t = P_{k-1} + α̅_k × τ_t其中:
τ_t = (t - s_k) / (e_k - s_k) 是子任务内的归一化时间P_{k-1} 是累积先验(前序子任务比例之和)α̅_k 是子任务 k 的时间比例这确保了无论 demonstration 长度如何,相同的任务 state 都映射到一致的进度值。
SARM 通过其处理器(src/lerobot/rewards/sarm/processor_sarm.py)进行训练,该处理器负责:
video_features 和 text_featuresstate_features(最多 max_state_dim)y = k + τ 构建 sparse_targets(以及 dense_only/dual 模式下的 dense_targets)lengths 张量(回退是一种训练时的数据增强)每个训练样本至少需要:
task(字符串):任务描述policy.image_key 图像和 policy.state_key state你可以从 3 种标注模式中选择,它们决定了进度标签的计算方式:
| 模式 | 所需标注 | 预测头 | 适用场景 |
|---|---|---|---|
single_stage | 无 | 仅稀疏 | 简单任务、快速实验,无需 VLM |
dense_only | 稠密(VLM) | 双头(稀疏自动生成) | 无需定义高层级阶段即可进行详细的子任务跟踪 |
dual | 稀疏 + 稠密(VLM) | 双头 | SARM 论文中同时包含两种粒度的完整设置 |
无需任何标注。 整个 episode 被视为一个称为 "task" 的单一阶段,进度在 episode 时长内从 0 线性增长到 1。
pip install -e ".[sarm]"工作流程:
1. Train SARM → 2. Visualize predictions → 3. (Optional) Train policy with RA-BC无需标注! 完全跳过此步骤。模型将使用 episode 的任务描述并自动计算线性进度。
| 参数 | 描述 |
|---|---|
--repo-id | HuggingFace dataset 仓库 ID |
--sparse-subtasks | 逗号分隔的高层级子任务名称列表 |
--dense-subtasks | 逗号分隔的细粒度子任务名称列表 |
--dense-only | 仅生成稠密标注(自动创建稀疏 “task” 阶段) |
--video-key | 要使用的相机/视频键(例如 observation.images.top) |
--num-workers | 并行 GPU 工作进程数(默认:1) |
--episodes | 要标注的特定 episode index(默认:全部) |
--skip-existing | 跳过已有标注的 episode |
--model | VLM 模型(默认:Qwen/Qwen3-VL-30B-A3B-Instruct) |
--num-visualizations | 标注后要可视化的 episode 数(默认:5,设为 0 跳过) |
注意:标注完成后,默认会自动可视化 5 个 episode。使用
--num-visualizations 0可跳过此步骤。
无需验证! 跳过此步骤。
这会生成可视化,显示叠加了子任务边界的视频帧以及子任务时间线。
| 参数 | 描述 |
|---|---|
--visualize-only | 仅可视化现有标注(不进行生成) |
--num-visualizations | 要可视化的 episode 数(默认:5) |
--visualize-type | 要可视化的标注类型:sparse、dense 或 both |
提示:如果标注不准确,请将子任务描述调整得更具体,然后重新运行。
在无标注的情况下进行训练——使用从 0 到 1 的线性进度:
lerobot-train \
--dataset.repo_id=your-username/your-dataset \
--policy.type=sarm \
--policy.annotation_mode=single_stage \
--policy.image_key=observation.images.base \
--output_dir=outputs/train/sarm_single \
--batch_size=32 \
--steps=5000 \
--wandb.enable=true \
--wandb.project=sarm \
--policy.repo_id=your-username/your-model-name添加 accelerate launch --multi_gpu --num_processes=4 以使用多个 GPU 进行训练。
| 参数 | 描述 | 默认值 |
|---|---|---|
--policy.annotation_mode | single_stage、dense_only 或 dual | single_stage |
--policy.image_key | 图像的相机键 | observation.images.top |
--policy.state_key | 关节 state 的键 | observation.state |
--policy.n_obs_steps | observation 历史步数(总 observation 帧数 = n_obs_steps + 1) | 8 |
--policy.frame_gap | 采样 observation 之间的帧间隔(30 fps 时:30 ≈ 1 秒) | 30 |
使用 compute_rabc_weights.py 搭配 --visualize-only 可视化模型预测(以及可用的、由标注推导出的目标),而无需写入 parquet 文件。
python -m lerobot.rewards.sarm.compute_rabc_weights \ --dataset-repo-id your-username/your-dataset \ --reward-model-path your-username/sarm-model \ --visualize-only \ --num-visualizations 5 \ --head-mode sparse \ --output-dir ./sarm_viz
可视化会显示:
--stride 1 时,还包括由标注推导出的“GT”)| 参数 | 描述 |
|---|---|
--visualize-only | 仅可视化预测(不进行 RABC 计算) |
--num-visualizations | 要可视化的 episode 数(默认:5) |
--head-mode | 要使用的 SARM 预测头:sparse、dense 或 both |
--stride | 每 N 帧计算一次,其余帧插值(默认:1) |
奖励对齐行为克隆(RA-BC)使用训练好的 SARM 模型,根据预测的进度提升程度对训练样本进行加权。这需要两个步骤:
对于每个训练样本,RA-BC 计算进度增量:
r_i = φ(o_{t+Δ}) - φ(o_t)其中 φ 是 SARM 的进度预测,Δ 是 policy 的 chunk_size。进度为正(即好的 demonstration)的样本获得更高权重,而进度为负或零的样本则被降低权重。
权重计算遵循论文中的公式 8-9:
w̃_i = clip((r_i − (μ − 2σ)) / (4σ + ε), 0, 1)w_i = 𝟙{r_i > κ} + 𝟙{0 ≤ r_i ≤ κ} × w̃_i首先,在 dataset 的所有帧上运行 SARM 模型以计算进度值:
python -m lerobot.rewards.sarm.compute_rabc_weights \ --dataset-repo-id your-username/your-dataset \ --reward-model-path your-username/sarm-model \ --head-mode sparse \ --num-visualizations 5 \ --push-to-hub
该脚本会:
<dataset_root>/sarm_progress.parquet)参数:
| 参数 | 描述 | 默认值 |
|---|---|---|
--reward-model-path | 训练好的 SARM 模型的路径 | (必填) |
--head-mode | 要使用的 SARM 预测头:sparse、dense 或 both | sparse |
--device | inference 设备 | cuda |
--visualize-only | 仅可视化预测(不进行 RA-BC 计算) | false |
--num-visualizations | 要可视化的 episode 数(默认:5,设为 0 跳过) | 5 |
输出格式(sarm_progress.parquet):
| 列 | 描述 |
|---|---|
index | dataset 中的全局帧索引 |
episode_index | episode 编号 |
frame_index | episode 内的局部帧索引 |
progress_sparse | 稀疏头进度值 [0, 1] |
progress_dense | 稠密头进度值 [0, 1](如已计算) |
获得进度文件后,即可使用 RA-BC 加权训练 policy。如果未明确指定,系统会从 dataset 路径自动检测进度文件(sarm_progress.parquet)。目前 RA-BC 支持 PI0、PI0.5 和 SmolVLA:
lerobot-train \ --dataset.repo_id=your-username/your-dataset \ --policy.type=pi0 \ --sample_weighting.type=rabc \ --sample_weighting.head_mode=sparse \ --sample_weighting.kappa=0.01 \ --output_dir=outputs/train/policy_rabc \ --batch_size=32 \ --steps=40000
训练脚本会自动:
chunk_size 计算进度增量(Δ)RA-BC 参数:
| 参数 | 描述 | 默认值 |
|---|---|---|
--sample_weighting.type | 加权 policy 类型(rabc 或 uniform) | rabc |
--sample_weighting.progress_path | 进度 parquet 文件的路径 | sarm_progress.parquet |
--sample_weighting.head_mode | 使用哪个 SARM 预测头的进度:sparse 或 dense | sparse |
--sample_weighting.kappa | 高质量样本的阈值 κ | 0.01 |
--sample_weighting.epsilon | 用于数值稳定性的小常数 | 1e-6 |
kappa 参数是决定哪些样本获得完整权重(w=1)的阈值。了解如何调整它对于 RA-BC 的有效工作至关重要。
加权是如何工作的:
| 条件 | 权重 |
|---|---|
delta > kappa | 1.0(硬阈值) |
0 ≤ delta ≤ kappa | 来自公式 8 的软权重 |
delta < 0 | 0.0(负进度) |
诊断 kappa 问题:
在训练期间监控这些 WandB 指标:
| 指标 | 健康范围 | 问题的指示 |
|---|---|---|
sample_weight_mean_weight | 0.3 - 0.8 | ≈ 1.0 说明 kappa 过低 |
sample_weighting/delta_mean | > 0 | 应为正值 |
sample_weighting/delta_std | > 0 | 数据质量的方差 |
如果 sample_weight_mean_weight ≈ 1.0: 你的 kappa 过低。大多数样本的 delta > kappa,完全绕过了软加权。RA-BC 将等同于普通的 BC。
根据你的数据设置 kappa:
默认的 kappa=0.01 是针对论文中 T 恤折叠任务(30fps 下约 90 秒的 episode)调优的。对于你的 dataset,请检查记录的 sample_weighting/delta_mean 和 sample_weighting/delta_std:
# If delta_mean ≈ 0.03 and delta_std ≈ 0.02:
# Most deltas fall in range [0.01, 0.05]
# Option 1: Set kappa = delta_mean (medium selectivity)
--sample_weighting.kappa=0.03
# Option 2: Set kappa = delta_mean + delta_std (high selectivity)
--sample_weighting.kappa=0.05
# Option 3: Set kappa = delta_mean + 2*delta_std (very selective)
--sample_weighting.kappa=0.07RA-BC 可能无法提供帮助的情况:
如果你的 dataset 已经是高质量的(所有 demonstration 的进度一致),RA-BC 不会带来太多好处,因为没有需要过滤的内容。
accelerate launch \ --multi_gpu \ --num_processes=4 \ src/lerobot/scripts/lerobot_train.py \ --dataset.repo_id=your-username/your-dataset \ --policy.type=pi0 \ --sample_weighting.type=rabc \ --sample_weighting.kappa=0.01 \ --output_dir=outputs/train/policy_rabc \ --batch_size=32 \ --steps=40000
single_stage 开始进行快速实验——无标注开销dense_onlydualsample_weight_mean_weight:如果它 ≈ 1.0,请增大 kappa(参见调整 RA-BC 的 Kappa)@article{chen2025sarm,
title={SARM: Stage-Aware Reward Modeling for Long Horizon Robot Manipulation},
author={Chen, Qianzhong and Yu, Justin and Schwager, Mac and Abbeel, Pieter and Shentu, Yide and Wu, Philipp},
journal={arXiv preprint arXiv:2509.25358},
year={2025}
}