LeRobot documentation
SARM: 단계 인식 보상 모델링
SARM: 단계 인식 보상 모델링
SARM(Stage-Aware Reward Modeling)은 장기 로봇 조작 작업을 위한 비디오 기반 보상 모델링 프레임워크입니다. 이 가이드에서는 SARM 보상 모델을 학습하는 방법과 선택적으로 Reward-Aligned Behavior Cloning(RA-BC)과 함께 사용하는 방법을 다룹니다.
논문: SARM: Stage-Aware Reward Modeling for Long Horizon Robot Manipulation
왜 보상 모델인가?
표준 행동 클로닝은 모든 데모스트레이션 프레임을 동일하게 취급하지만, 실제 로봇 데이터셋은 불완전합니다. 망설임, 수정, 품질이 상이한 궤적이 포함되어 있기 때문입니다. 보상 모델은 데모스트레이션에서 작업 진행도라는 일반화 가능한 개념을 학습하여 이 문제를 해결합니다. 비디오 프레임과 작업 설명을 입력으로 받아 로봇이 작업을 완료하기까지 얼마나 가까운지(0→1)를 예측합니다. 이렇게 학습된 “진행 신호”는 여러 방식으로 활용될 수 있으며, 두 가지 유망한 응용 분야는 다음과 같습니다. (1) 가중치 모방 학습(RA-BC)은 정책 학습 시 높은 진행도를 가진 프레임에 더 큰 가중치를 부여하고, (2) 강화 학습에서는 보상 모델이 온라인 또는 오프라인 정책 개선에 대해 밀도 높은 보상을 제공합니다.
개요
SARM에는 다음과 같은 기능이 있습니다:
- 단계 인식 아키텍처: 고수준 작업 단계와 각 단계 내의 세밀한 진행도를 함께 예측합니다
- 하위 작업 주석: 자연어 하위 작업 주석을 사용하여 일관된 진행 라벨을 유도
- 시간 비율: 가변 길이의 데모스트레이션에 걸쳐 진행도를 정규화하기 위해 각 하위 작업에 대한 데이터셋 수준의 사전 분포(α̅_k)를 계산합니다
SARM은 각 프레임마다 컴팩트한 stage+tau 타겟을 학습합니다:
- stage: 정수 단계 인덱스
k ∈ {0, ..., K-1} - τ (tau): 단계 내 진행률
τ ∈ [0, 1] - target encoding:
y = k + τ(데이터셋 프로세서가 생성하는 값)
추론 시(및 하위 RA-BC에서) SARM은 데이터셋 수준의 시간 비율 α̅_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의 시간 비율입니다
이로 인해 서로 다른 길이의 데모에서도 동일한 작업 상태가 일관된 진행 값으로 매핑됩니다.
입력과 대상(새로운 코드가 기대하는 것)
SARM은 프로세서(src/lerobot/rewards/sarm/processor_sarm.py)를 통해 학습되며, 이 프로세서는 다음과 같은 역할을 수행합니다:
- CLIP(ViT-B/32)로 이미지와 작업 텍스트를
video_features와text_features로 인코딩합니다 - 패딩/잘라 로봇 상태를
state_features로 (max_state_dim까지) - Builds targets를
sparse_targets로(그리고dense_only/dual의 경우dense_targets로) stage+tau 인코딩y = k + τ를 사용하여 생성합니다 - 마스크가 프레임 되감기를 샘플별
lengths텐서를 사용하여 수행합니다(되감기는 학습 시 증강 기법입니다)
최소한, 각 학습 샘플에는 다음이 필요합니다:
task(문자열): 작업 설명- 데이터셋의
reward_model.image_key이미지와reward_model.state_key상태
주석 모드
진행 라벨이 계산되는 방식을 결정하는 3가지 어노테이션 모드 중 하나를 선택할 수 있습니다:
| 모드 | 필요한 어노테이션 | 헤드 | 사용 사례 |
|---|---|---|---|
single_stage | None | Sparse만 | 단순한 작업, 빠른 실험, VLM 불필요 |
dense_only | Dense (VLM) | 듀얼(희소 자동 생성) | 고급 단계 정의 없이 상세한 하위 작업 추적 |
dual | Sparse + Dense (VLM) | 듀얼 | 두 가지 세밀도를 모두 갖춘 SARM 논문 전체 설정 |
모드 세부 정보
주석이 필요하지 않습니다. 에피소드 전체가 "task"라고 불리는 단일 단계로 처리되며, 에피소드 지속 시간 동안 진행도가 0에서 1까지 선형으로 증가합니다.
- Sparse head: 1단계(“task”), 선형 진행
- Dense head: 사용되지 않음
- 적합한 경우: 단순한 작업, 빠른 실험, 또는 VLM 주석이 사용 불가능한 경우
환경 설정
- LeRobot은 설치 가이드를 참고하여 설치하세요.
- 다음 명령을 실행하여 SARM의 종속성을 설치합니다:
pip install -e ".[sarm]"워크플로:
1. Train SARM → 2. Visualize predictions → 3. (Optional) Train policy with RA-BCVLM의 밀집(세분화)된 어노테이션만 사용. 희소 헤드(sparse head)는 에피소드 전체를 포괄하는 단일 "task" 단계를 자동으로 사용하는 반면, 밀집 헤드(dense head)는 상세한 서브태스크 진행을 학습합니다.
- Sparse head: 1단계(“task”), 선형 진행(자동 생성)
- Dense head: VLM 어노테이션의 여러 세밀한 단계
- 적합한 경우: 상세한 하위 작업 추적이 필요하지만 상위 단계 정의를 원하지 않는 경우
워크플로:
1. Annotate (dense) → 2. Verify → 3. Train SARM → 4. Visualize → 5. (Optional) Train policy with RA-BCVLM의 희소 및 밀집 어노테이션 모두. SARM 논문에서 설명한 완전한 이중 헤드 모드로, 고수준(희소) 및 세밀한(밀집) 단계 예측을 모두 수행합니다.
- Sparse head: VLM 어노테이션의 고수준 단계
- Dense head: VLM 어노테이션의 세분화된 단계
- 적합한 경우: 세밀한 단계별 제어와 전체적인 흐름 관리가 모두 필요한 복잡한 다단계 작업
워크플로:
1. Annotate (sparse+dense) → 2. Verify → 3. Train SARM → 4. Visualize → 5. (Optional) Train policy with RA-BC1단계: 하위 작업 주석
주석이 필요하지 않습니다! 이 단계를 완전히 건너뛰세요. 모델은 에피소드의 작업 설명을 사용하고 선형 진행도를 자동으로 계산합니다.
VLM을 사용하여 dense (fine-grained) annotations만 생성합니다. sparse 단계는 자동으로 생성됩니다.
python src/lerobot/data_processing/sarm_annotations/subtask_annotation.py \
--repo-id your-username/your-dataset \
--dense-only \
--dense-subtasks "Bring robot arms up from starting position,Grab near side and do 1st fold,Grab side and do 2nd fold,Grab side and do 3rd fold to finish folding" \
--video-key observation.images.base \
--num-workers 4 \
--push-to-hub저장되는 항목:
meta/temporal_proportions_sparse.json- 자동 생성된 희소 비율({"task": 1.0})meta/temporal_proportions_dense.json- Dense 시간 비율- 에피소드별 컬럼은
episodes/*.parquet:dense_subtask_names,dense_subtask_start_frames,dense_subtask_end_frames- (또한 시간 기반 열:
dense_subtask_start_times,dense_subtask_end_times)
VLM을 사용하여 희소(고수준) 및 밀집(세부) 어노테이션을 모두 생성합니다.
python src/lerobot/data_processing/sarm_annotations/subtask_annotation.py \
--repo-id your-username/your-dataset \
--sparse-subtasks "Bring arms up from starting position,Fold the towel (3 folds in total)" \
--dense-subtasks "Bring robot arms up from starting position,Grab near side and do 1st fold,Grab side and do 2nd fold,Grab side and do 3rd fold to finish folding" \
--video-key observation.images.base \
--num-workers 4 \
--push-to-hub저장되는 항목:
meta/temporal_proportions_sparse.json- 희소 시간 비율meta/temporal_proportions_dense.json- Dense 시간 비율- 에피소드별 컬럼은
episodes/*.parquet:sparse_subtask_names,sparse_subtask_start_frames,sparse_subtask_end_framesdense_subtask_names,dense_subtask_start_frames,dense_subtask_end_frames- (또한 시간 기반 열:
*_subtask_start_times,*_subtask_end_times)
Annotation Arguments
| 인수 | 설명 |
|---|---|
--repo-id | HuggingFace 데이터셋 저장소 ID |
--sparse-subtasks | 고급 서브태스크 이름의 쉼표로 구분된 목록 |
--dense-subtasks | 쉼표로 구분된 세분화된 서브태스크 이름 목록 |
--dense-only | Generate only dense annotations (auto-creates sparse “task” stage) |
--video-key | 사용할 카메라/비디오 키 (예: observation.images.top) |
--num-workers | 병렬 GPU 워커 수 (기본값: 1) |
--episodes | 주석 처리할 특정 에피소드 인덱스 (기본값: 전체) |
--skip-existing | 이미 주석이 있는 에피소드를 건너뜁니다 |
--model | VLM 모델 (기본값: Qwen/Qwen3-VL-30B-A3B-Instruct) |
--num-visualizations | 아노테이션 후 시각화할 에피소드 수 (기본값: 5, 0으로 설정하면 건너뜁니다) |
참고: 주석이 완료되면 기본적으로 5개의 에피소드가 자동으로 시각화됩니다. 이 단계를 건너뛰려면
--num-visualizations 0을 사용하세요.
2단계: 주석 확인
검증 불필요! 이 단계를 건너뛰세요.
--visualize-only 플래그를 사용하여 어노테이션을 시각화합니다:
python src/lerobot/data_processing/sarm_annotations/subtask_annotation.py \ --repo-id your-username/your-dataset \ --visualize-only \ --visualize-type dense \ --num-visualizations 5 \ --video-key observation.images.base \ --output-dir ./subtask_viz
--visualize-only 플래그를 사용하여 어노테이션을 시각화합니다:
python src/lerobot/data_processing/sarm_annotations/subtask_annotation.py \ --repo-id your-username/your-dataset \ --visualize-only \ --visualize-type both \ --num-visualizations 5 \ --video-key observation.images.base \ --output-dir ./subtask_viz
…
시각화 인자
| 인수 | 설명 |
|---|---|
--visualize-only | Only visualize existing annotations (no generation) |
--num-visualizations | 시각화할 에피소드 수 (기본값: 5) |
--visualize-type | 시각화할 어노테이션 유형: sparse, dense 또는 both |
팁: 주석이 부정확한 경우, 하위 작업 설명을 더 구체적으로 조정하고 다시 실행하세요.
3단계: SARM 학습
아무 anotations도 없이 학습 - 0에서 1까지의 선형 진행을 사용합니다:
lerobot-train \
--dataset.repo_id=your-username/your-dataset \
--reward_model.type=sarm \
--reward_model.annotation_mode=single_stage \
--reward_model.image_key=observation.images.base \
--output_dir=outputs/train/sarm_single \
--batch_size=32 \
--steps=5000 \
--trackio.enable=true \
--trackio.project=sarm \
--reward_model.repo_id=your-username/your-model-nameTrain with dense annotations only (sparse auto-generated):
lerobot-train \
--dataset.repo_id=your-username/your-dataset \
--reward_model.type=sarm \
--reward_model.annotation_mode=dense_only \
--reward_model.image_key=observation.images.base \
--output_dir=outputs/train/sarm_dense \
--batch_size=32 \
--steps=5000 \
--trackio.enable=true \
--trackio.project=sarm \
--reward_model.repo_id=your-username/your-model-name희소 및 밀집 어노테이션을 모두 사용하여 학습:
lerobot-train \
--dataset.repo_id=your-username/your-dataset \
--reward_model.type=sarm \
--reward_model.annotation_mode=dual \
--reward_model.image_key=observation.images.base \
--output_dir=outputs/train/sarm_dual \
--batch_size=32 \
--steps=5000 \
--trackio.enable=true \
--trackio.project=sarm \
--reward_model.repo_id=your-username/your-model-name멀티-GPU 학습
여러 GPU를 사용하여 학습하려면 accelerate launch --multi_gpu --num_processes=4를 추가하세요.
학습 인자
| 인수 | 설명 | 기본값 |
|---|---|---|
--reward_model.annotation_mode | single_stage, dense_only 또는 dual | single_stage |
--reward_model.image_key | 이미지의 카메라 키 | observation.images.top |
--reward_model.state_key | Key for joint states | observation.state |
--reward_model.n_obs_steps | 관측 히스토리 단계 (총 관측 프레임 = n_obs_steps + 1) | 8 |
--reward_model.frame_gap | 샘플링된 관찰 사이의 간격(프레임 단위) (30 fps 기준: 30 ≈ 1초) | 30 |
4단계: 예측 시각화
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
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 dense \ --output-dir ./sarm_viz
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 both \ --output-dir ./sarm_viz
시각화는 다음을 보여줍니다:
- 진행도 플롯: 예측된 진행도(사용 가능한 경우 및
--stride 1일 때 선택적으로 어노테이션에서 파생된 “GT”) - 단계별 확률: 예측된 단계별 확률의 누적 영역 플롯
- 샘플 프레임: 에피소드의 주요 프레임으로 진행/단계 라벨이 포함됩니다
시각화 인자
| 인수 | 설명 |
|---|---|
--visualize-only | 예측 결과만 시각화 (RABC 계산 없음) |
--num-visualizations | 시각화할 에피소드 수 (기본값: 5) |
--head-mode | 사용할 SARM 헤드: sparse, dense 또는 both |
--stride | N프레임마다 계산하고 나머지는 보간합니다 (기본값: 1) |
5단계 (선택 사항): RA-BC로 정책 학습
Reward-Aligned Behavior Cloning (RA-BC)는 학습된 SARM 모델을 사용하여 예측된 진행도 개선에 따라 학습 샘플의 가중치를 설정합니다. 이를 위해 두 단계가 필요합니다:
- 학습된 SARM 모델을 사용하여 모든 프레임에 대해 진행 값을 사전 계산합니다
- 정책 학습을 사전 계산된 값을 사용하여 RA-BC 가중치와 함께 수행합니다
RA-BC 작동 방식
각 학습 샘플에 대해 RA-BC는 진행 델타를 계산합니다:
r_i = φ(o_{t+Δ}) - φ(o_t)φ는 SARM 진행도 예측값이며, Δ는 정책의 chunk_size입니다. 진행도가 양수인 샘플(좋은 데모)은 더 높은 가중치를, 진행도가 음수 또는 0인 샘플은 가중치가 낮아집니다.
가중치는 논문에서 식 8-9를 따릅니다:
- 소프트 가중치:
w̃_i = clip((r_i − (μ − 2σ)) / (4σ + ε), 0, 1) - 최종 가중치:
w_i = 𝟙{r_i > κ} + 𝟙{0 ≤ r_i ≤ κ} × w̃_i
5a단계: SARM 진행 값 계산
먼저, 데이터셋의 모든 프레임에서 SARM 모델을 실행하여 진행 값(progress values)을 계산합니다:
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
이 스크립트는:
- 모든 프레임을 처리하고 진행 값을 계산합니다
- 디스크의 데이터셋 옆에 parquet 파일로 진행 값을 저장합니다(기본값은
<dataset_root>/sarm_progress.parquet) - 첫 N개의 에피소드(기본값: 5)의 시각화를 생성합니다
인수:
| 인수 | 설명 | 기본값 |
|---|---|---|
--reward-model-path | 학습된 SARM 모델 경로 | (필수) |
--head-mode | 사용할 SARM 헤드: sparse, dense 또는 both | sparse |
--device | 추론용 장치 | cuda |
--visualize-only | 예측 결과만 시각화 (RA-BC 계산 없음) | false |
--num-visualizations | 시각화할 에피소드 수 (기본값: 5, 0으로 설정하면 건너뜁니다) | 5 |
출력 형식 (sarm_progress.parquet):
| 열 | 설명 |
|---|---|
index | 데이터셋의 전역 프레임 인덱스 |
episode_index | 에피소드 번호 |
frame_index | 에피소드 내 로컬 프레임 인덱스 |
progress_sparse | Sparse head 진행 값 [0, 1] |
progress_dense | Dense head 진행 값 [0, 1] (계산된 경우) |
5b단계: RA-BC로 정책 학습
진행 파일이 준비되면 RA-BC 가중치를 사용하여 정책을 학습합니다. 명시적으로 제공되지 않으면 데이터셋 경로(sarm_progress.parquet)에서 진행 파일이 자동으로 감지됩니다. 현재 RA-BC는 PI0, PI0.5, SmolVLA, FastWAM을 지원합니다:
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
학습 스크립트는 자동으로 다음을 수행합니다:
- parquet 파일에서 사전 계산된 진행 값을 로드합니다
- 정책의
chunk_size를 사용하여 진행 증분(Δ)을 계산합니다 - 진행도 개선에 기반하여 샘플 가중치를 계산합니다
- 학습 중 가중 손실 적용
RA-BC 인자:
| 인수 | 설명 | 기본값 |
|---|---|---|
--sample_weighting.type | 가중치 전략 유형(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 |
RA-BC Kappa 조정
kappa 매개변수는 어떤 샘플이 전체 가중치(w=1)를 부여받는지 결정하는 임계값입니다. 이를 어떻게 조정하는지 이해하는 것은 RA-BC가 효과적으로 작동하는 데 매우 중요합니다.
가중치가 작동하는 방식:
| 조건 | 가중치 |
|---|---|
delta > kappa | 1.0 (경계값) |
0 ≤ delta ≤ kappa | 식 8의 소프트 가중치 |
delta < 0 | 0.0 (음의 진행률) |
카파 문제 진단:
학습 중에 다음 Trackio 메트릭을 모니터링하세요:
| 메트릭 | 적정 범위 | 문제 지표 |
|---|---|---|
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초 에피소드)에 맞게 조정되었습니다. 사용자의 데이터셋에 대해서는 기록된 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가 도움이 되지 않을 수 있는 경우:
데이터셋이 이미 고품질(모든 데모에서 일관된 진행)이라면, 필터링할 대상이 없으므로 RA-BC는 큰 이점을 제공하지 않습니다.
RA-BC를 사용한 멀티 GPU 학습
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_only를 사용하세요 - 와 의 세밀한 진행 상황 모두 의미 있는 복잡한 작업에는
dual을 사용하세요
주석 품질
- 하위 작업 이름을 구체적으로 지정: “fold” 대신 “grab near side and fold toward center”를 사용
- 시각화로 검증: 학습 전에 몇 에피소드를 항상 확인하세요
- 일관된 이름: 모든 에피소드에서 동일한 하위 작업 이름을 사용합니다
RA-BC
- 먼저 SARM을 학습합니다: RA-BC의 품질은 SARM의 품질에 전적으로 의존합니다
sample_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}
}