LeRobot documentation
SARM: Modelagem de Recompensa Consciente de Etapas
SARM: Modelagem de Recompensa Consciente de Etapas
SARM (Stage-Aware Reward Modeling) é um framework de modelagem de recompensas baseado em vídeo para tarefas de manipulação robótica de longo horizonte. Este guia aborda como treinar modelos de recompensa SARM e, opcionalmente, como usá-los com Reward-Aligned Behavior Cloning (RA-BC).
Artigo: SARM: Stage-Aware Reward Modeling for Long Horizon Robot Manipulation
Por que Modelos de Recompensa?
O clonagem de comportamento padrão trata todos os quadros de demonstração de forma igual, mas os conjuntos de dados de robôs do mundo real são desorganizados. Eles contêm hesitações, correções e trajetórias de qualidade variável. Os modelos de recompensa resolvem isso aprendendo uma noção generalizável de progresso da tarefa a partir das demonstrações: dados os quadros de vídeo e uma descrição da tarefa, eles preveem o quão perto o robô está de concluir a tarefa (0→1). Esse “sinal de progresso” aprendido pode ser usado de várias maneiras, sendo duas aplicações promissoras: (1) aprendizado por imitação ponderado (RA-BC), em que os quadros com alto progresso recebem mais peso durante o treinamento da política, e (2) aprendizado por reforço, em que o modelo de recompensa fornece recompensas densas para o aprimoramento online ou offline da política.
Visão geral
O SARM possui os seguintes recursos:
- Arquitetura consciente de etapas: Prediz simultaneamente a etapa de alto nível da tarefa e o progresso detalhado dentro de cada etapa
- Anotações de subtarefas: Usa anotações de subtarefas em linguagem natural para derivar rótulos de progresso consistentes
- Proporções temporais: Calcula os priors no nível do conjunto de dados (α̅_k) para cada subtarefa, a fim de normalizar o progresso entre demonstrações de comprimento variável
O SARM treina em um alvo compacto stage+tau para cada quadro:
- stage: índice do estágio inteiro
k ∈ {0, ..., K-1} - τ (tau): progresso dentro do estágio
τ ∈ [0, 1] - codificação de alvo:
y = k + τ(é isso que o processador de conjunto de dados produz)
No momento da inferência (e no RA-BC de nível inferior), o SARM converte o valor bruto k + τ em um progresso normalizado em [0, 1] usando as proporções temporais de nível de conjunto de dados α̅_k (armazenadas em meta/temporal_proportions_*.json).
Isso corresponde à Fórmula (2) do artigo:
progress_t = P_{k-1} + α̅_k × τ_tOnde:
τ_t = (t - s_k) / (e_k - s_k)é o tempo normalizado dentro da subtarefaP_{k-1}é o anterior cumulativo (soma das proporções das subtarefas anteriores)α̅_ké a proporção temporal para a subtarefa k
Isso garante que estados de tarefa idênticos sejam mapeados para valores de progresso consistentes, mesmo em demonstrações de diferentes comprimentos.
Entradas e Alvos (O que o novo código espera)
O SARM é treinado por meio de seu processador (src/lerobot/rewards/sarm/processor_sarm.py), que:
- Codifica imagens e texto da tarefa com CLIP (ViT-B/32) em
video_featuresetext_features - Preenche/trunca o estado do robô em
state_features(atémax_state_dim) - Constrói os alvos como
sparse_targets(edense_targetsemdense_only/dual) usando a codificação stage+tauy = k + τ - Máscaras reprocessam quadros usando um tensor
lengthspor amostra (reprocessar é uma aumentação no momento do treinamento)
No mínimo, cada amostra de treinamento precisa de:
task(string): descrição da tarefa- imagens de
reward_model.image_keye estados dereward_model.state_keydo conjunto de dados
Modos de Anotação
Você pode escolher entre 3 modos de anotação que determinam como os rótulos de progresso são calculados:
| Modo | Anotações Necessárias | Cabeças | Caso de Uso |
|---|---|---|---|
single_stage | None | Apenas esparsos | Tarefas simples, experimentos rápidos, sem necessidade de VLM |
dense_only | Dense (VLM) | Dual (auto-gerado esparsamente) | Rastreamento detalhado de subtarefas sem definir estágios de alto nível |
dual | Esparsa + Densa (VLM) | Dual | Configuração completa do paper SARM com ambas as granularidades |
Detalhes do Modo
Sem anotações necessárias. O episódio inteiro é tratado como um único estágio chamado "task", e o progresso é linear de 0 a 1 ao longo da duração do episódio.
- Cabeça esparsa: 1 estágio (“task”), progresso linear
- Cabeça densa: Não utilizado
- Melhor para: Tarefas simples, experimentos rápidos ou quando a anotação de VLM não está disponível
Configure seu ambiente
- Instale o LeRobot seguindo nosso Guia de Instalação.
- Instale as dependências do SARM executando:
pip install -e ".[sarm]"Fluxo de trabalho:
1. Train SARM → 2. Visualize predictions → 3. (Optional) Train policy with RA-BCSomente anotações densas (de granularidade fina) de um VLM. A cabeça esparsa usa automaticamente um único estágio "task" que cobre o episódio completo, enquanto a cabeça densa aprende a progressão detalhada dos subtarefas.
- Cabeça esparsa: 1 estágio (“task”), progresso linear (gerado automaticamente)
- Cabeça densa: Múltiplos estágios de granularidade fina a partir de anotações de VLM
- Melhor para: Quando você deseja um rastreamento detalhado de subtarefas, mas não precisa definir etapas de alto nível
Fluxo de trabalho:
1. Annotate (dense) → 2. Verify → 3. Train SARM → 4. Visualize → 5. (Optional) Train policy with RA-BCAnotações esparsas e densas do VLM. Modo completo de duas cabeças, conforme descrito no paper SARM, com previsões de estágio de alto nível (esparsas) e de granulação fina (densas).
- Cabeça esparsa: Estágios de alto nível a partir de anotações de VLM
- Cabeça densa: Estágios de granularidade fina a partir de anotações de VLM
- Melhor para: Tarefas complexas de múltiplas etapas em que ambas as granularidades são úteis
Fluxo de trabalho:
1. Annotate (sparse+dense) → 2. Verify → 3. Train SARM → 4. Visualize → 5. (Optional) Train policy with RA-BCEtapa 1: Anotação de subtarefas
Nenhuma anotação necessária! Pule esta etapa por completo. O modelo usará a descrição da tarefa do episódio e calculará o progresso linear automaticamente.
Gere apenas anotações densas (de granularidade fina) usando um VLM. O estágio esparsa será gerado automaticamente.
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-hubO que é salvo:
meta/temporal_proportions_sparse.json- Proporções esparsas geradas automaticamente ({"task": 1.0})meta/temporal_proportions_dense.json- Proporções temporais densas- Colunas por episódio em
episodes/*.parquet:dense_subtask_names,dense_subtask_start_frames,dense_subtask_end_frames- (colunas baseadas em tempo também:
dense_subtask_start_times,dense_subtask_end_times)
Gere anotações tanto esparsas (de alto nível) quanto densas (de granularidade fina) usando um 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-hubO que é salvo:
meta/temporal_proportions_sparse.json- Proporções temporais esparsasmeta/temporal_proportions_dense.json- Proporções temporais densas- Colunas por episódio em
episodes/*.parquet:sparse_subtask_names,sparse_subtask_start_frames,sparse_subtask_end_framesdense_subtask_names,dense_subtask_start_frames,dense_subtask_end_frames- (além das colunas baseadas em tempo:
*_subtask_start_times,*_subtask_end_times)
Argumentos de Anotação
| Argumento | Descrição |
|---|---|
--repo-id | ID do repositório de dataset do HuggingFace |
--sparse-subtasks | Lista de nomes de subtarefas de alto nível separadas por vírgulas |
--dense-subtasks | Lista de nomes de subtarefas de alto nível separadas por vírgulas |
--dense-only | Gerar apenas anotações densas (cria automaticamente o estágio esparsos “task”) |
--video-key | Chave da câmera/vídeo a ser usada (por exemplo, observation.images.top) |
--num-workers | Número de workers de GPU em paralelo (padrão: 1) |
--episodes | Índices específicos de episódios para anotar (padrão: todos) |
--skip-existing | Pular episódios que já possuem anotações |
--model | Modelo VLM (padrão: Qwen/Qwen3-VL-30B-A3B-Instruct) |
--num-visualizations | Número de episódios a visualizar após a anotação (padrão: 5, defina como 0 para pular) |
Observação: Após a conclusão da anotação, 5 episódios são visualizados automaticamente por padrão. Use
--num-visualizations 0para pular esta etapa.
Etapa 2: Verificar Anotações
Nenhuma verificação necessária! Pule esta etapa.
Visualize as anotações usando a flag --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 as anotações usando a flag --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
Isso gera visualizações mostrando os quadros do vídeo com as fronteiras das subtarefas sobrepostas e a linha do tempo das subtarefas.
Argumentos de Visualização
| Argumento | Descrição |
|---|---|
--visualize-only | Visualizar apenas as anotações existentes (sem geração) |
--num-visualizations | Número de episódios a visualizar (padrão: 5) |
--visualize-type | Tipo de anotações a visualizar: sparse, dense ou both |
Dica: Se as anotações forem imprecisas, ajuste as descrições dos subtarefas para serem mais específicas e execute novamente.
Etapa 3: Treinar SARM
Treine com sem anotações - usa progresso linear de 0 a 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-nameTreine com apenas anotações densas (geradas automaticamente de forma esparsa):
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-nameTreine com anotações esparsas e densas:
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-nameTreinamento Multi-GPU
Adicione accelerate launch --multi_gpu --num_processes=4 para usar múltiplas GPUs para treinamento.
Argumentos de Treinamento
| Argumento | Descrição | Padrão |
|---|---|---|
--reward_model.annotation_mode | single_stage, dense_only ou dual | single_stage |
--reward_model.image_key | Chave da câmera para imagens | observation.images.top |
--reward_model.state_key | Chave para estados conjuntos | observation.state |
--reward_model.n_obs_steps | Etapas do histórico de observação (total de quadros de obs = n_obs_steps + 1) | 8 |
--reward_model.frame_gap | Intervalo (em quadros) entre as observações amostradas (a 30 fps: 30 ≈ 1s) | 30 |
Etapa 4: Visualizar Previsões
Use compute_rabc_weights.py com --visualize-only para visualizar as previsões do modelo (e, se disponíveis, os alvos derivados das anotações) sem gravar um arquivo 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
A visualização mostra:
- Gráfico de progresso: Progresso previsto (e anotação opcional derivada de “GT” quando disponível e
--stride 1) - Probabilidades de estágio: Gráfico de área empilhado das probabilidades de estágio previstas
- Quadros de amostra: Quadros-chave do episódio com rótulos de progresso/etapa
Argumentos de Visualização
| Argumento | Descrição |
|---|---|
--visualize-only | Visualizar apenas as previsões (sem o cálculo do RABC) |
--num-visualizations | Número de episódios a visualizar (padrão: 5) |
--head-mode | Cabeça SARM a ser usada: sparse, dense ou both |
--stride | Calcule a cada N quadros e interpole o restante (padrão: 1) |
Etapa 5 (Opcional): Treinar a Política com RA-BC
O Behavior Cloning Alinhado a Recompensas (RA-BC) usa o modelo SARM treinado para ponderar as amostras de treinamento com base na melhoria de progresso prevista. Isso requer duas etapas:
- Pré-compute progress values para todos os frames usando o modelo SARM treinado
- Treinar a política com ponderação RA-BC usando os valores pré-computados
Como o RA-BC funciona
Para cada amostra de treinamento, o RA-BC calcula o delta de progresso:
r_i = φ(o_{t+Δ}) - φ(o_t)Onde φ é a previsão de progresso do SARM e Δ é o chunk_size da política. Amostras com progresso positivo (boas demonstrações) recebem pesos maiores, enquanto amostras com progresso negativo ou zero têm seus pesos reduzidos.
A ponderação segue as Equações 8-9 do artigo:
- Peso suave:
w̃_i = clip((r_i − (μ − 2σ)) / (4σ + ε), 0, 1) - Peso final:
w_i = 𝟙{r_i > κ} + 𝟙{0 ≤ r_i ≤ κ} × w̃_i
Etapa 5a: Calcule os valores de progresso do SARM
Primeiro, execute o modelo SARM em todos os frames do seu conjunto de dados para calcular os valores de progresso:
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
Este script:
- Processa todos os quadros e calcula os valores de progresso
- Salva os valores de progresso em um arquivo parquet ao lado do conjunto de dados no disco (padrão:
<dataset_root>/sarm_progress.parquet) - Gera visualizações dos primeiros N episódios (padrão: 5)
Argumentos:
| Argumento | Descrição | Padrão |
|---|---|---|
--reward-model-path | Caminho para o modelo SARM treinado | (obrigatório) |
--head-mode | Cabeça SARM a ser usada: sparse, dense ou both | sparse |
--device | Dispositivo para inferência | cuda |
--visualize-only | Visualizar apenas as previsões (sem o cálculo de RA-BC) | false |
--num-visualizations | Número de episódios para visualizar (padrão: 5, defina como 0 para pular) | 5 |
Formato de saída (sarm_progress.parquet):
| Coluna | Descrição |
|---|---|
index | Índice global do quadro no conjunto de dados |
episode_index | Número do episódio |
frame_index | Índice do quadro local dentro do episódio |
progress_sparse | Valor de progresso da cabeça esparsa [0, 1] |
progress_dense | Valor de progresso da cabeça densa [0, 1] (se calculado) |
Etapa 5b: Treinar a Política com RA-BC
Depois de obter o arquivo de progresso, treine sua política com ponderação RA-BC. O arquivo de progresso é detectado automaticamente a partir do caminho do dataset (sarm_progress.parquet) se não for fornecido explicitamente. Atualmente, PI0, PI0.5, SmolVLA e FastWAM são suportados com RA-BC:
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
O script de treinamento faz automaticamente:
- Carrega os valores de progresso pré-computados do arquivo parquet
- Usa o
chunk_sizeda política para calcular os deltas de progresso (Δ) - Calcula pesos de amostras com base na melhoria do progresso
- Aplica perda ponderada durante o treinamento
Argumentos do RA-BC:
| Argumento | Descrição | Padrão |
|---|---|---|
--sample_weighting.type | Tipo de estratégia de ponderação (rabc ou uniform) | rabc |
--sample_weighting.progress_path | Caminho para o arquivo parquet de progresso | sarm_progress.parquet |
--sample_weighting.head_mode | Qual progresso da cabeça SARM usar: sparse ou dense | sparse |
--sample_weighting.kappa | Limiar κ para amostras de alta qualidade | 0.01 |
--sample_weighting.epsilon | Constante pequena para estabilidade numérica | 1e-6 |
Ajuste do Kappa RA-BC
O parâmetro kappa é o limiar que determina quais amostras recebem peso total (w=1). Compreender como ajustá-lo é fundamental para que o RA-BC funcione de forma eficaz.
Como a ponderação funciona:
| Condição | Peso |
|---|---|
delta > kappa | 1.0 (limiar rígido) |
0 ≤ delta ≤ kappa | Peso suave da Eq. 8 |
delta < 0 | 0.0 (progresso negativo) |
Diagnóstico de problemas de kappa:
Monitore estas métricas do Trackio durante o treinamento:
| Métrica | Faixa Saudável | Indicador de Problema |
|---|---|---|
sample_weight_mean_weight | 0.3 - 0.8 | ≈ 1.0 significa que o kappa é baixo demais |
sample_weighting/delta_mean | > 0 | Deve ser positivo |
sample_weighting/delta_std | > 0 | Variação na qualidade dos dados |
Se sample_weight_mean_weight ≈ 1.0: seu kappa está muito baixo. A maioria das amostras tem delta > kappa e ignora completamente o ponderamento suave. O RA-BC se torna equivalente ao BC vanilla.
Definindo kappa com base nos seus dados:
O valor padrão kappa=0.01 foi ajustado para a tarefa de dobrar camisetas do artigo (episódios de ~90s a 30fps). Para o seu conjunto de dados, verifique os valores registrados em sample_weighting/delta_mean e 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.07Quando o RA-BC pode não ajudar:
Se o seu conjunto de dados já for de alta qualidade (progresso consistente em todas as demonstrações), o RA-BC não trará muitos benefícios, pois não há nada para filtrar.
Treinamento Multi-GPU com 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
Dicas e Boas Práticas
Escolhendo um Modo
- Comece com
single_stagepara experimentos rápidos - sem sobrecarga de anotação - Use
dense_onlyquando você deseja um acompanhamento detalhado do progresso, mas as tarefas não possuem etapas de alto nível bem definidas - Use
dualpara tarefas complexas em que o progresso em nível grosseiro e fino é significativo
Qualidade da Anotação
- Seja específico com os nomes das subtarefas: em vez de “dobrar”, use “pegue o lado próximo e dobre em direção ao centro”
- Verifique com visualização: Sempre verifique alguns episódios antes do treinamento
- Nomenclatura consistente: Use os mesmos nomes de subtarefas em todos os episódios
RA-BC
- Treine o SARM primeiro: a qualidade do RA-BC depende inteiramente da qualidade do SARM
- Monitore
sample_weight_mean_weight: se for ≈ 1.0, aumente kappa (veja Ajuste do Kappa do RA-BC)
Citação
@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}
}