# TOPReward

TOPReward es un **modelo de recompensa zero-shot** que extrae las log-probabilidades de los tokens de un modelo de visión-lenguaje (VLM) listo para usar como señal de recompensa robótica. Dada una trayectoria en video y una instrucción de tarea, devuelve la verosimilitud logarítmica del VLM de que la instrucción sea cierta, sin necesidad de ajuste fino.

**Artículo**: [TOPReward: Token Probabilities as Hidden Zero-Shot Rewards for Robotics](https://arxiv.org/abs/2602.19313) **Proyecto**: [topreward.github.io](https://topreward.github.io/webpage/) **Código original**: [github.com/TOPReward/TOPReward](https://github.com/TOPReward/TOPReward) **Backbone predeterminado**: [Qwen/Qwen3-VL-8B-Instruct](https://huggingface.co/Qwen/Qwen3-VL-8B-Instruct)

## Resumen

TOPReward le pregunta a un VLM genérico qué tan probable es una instrucción de tarea, **condicionada en el video** de un robot que intenta completar esa tarea. Concretamente, dado:

- Un video de trayectoria (una secuencia de fotogramas).
- Una instrucción de tarea (p. ej., _"open the drawer"_).

construye un prompt de chat de la forma

```text
<video>
"The above video shows a robot manipulation trajectory that completes the
 following task: <instruction> Decide whether the above statement is True
 or not. The answer is: True"
```

lo envía a través del VLM, aplica una máscara de etiquetas a todo excepto al último token y lee la log-probabilidad de ese token, que por defecto es el literal `"True"` que cierra la plantilla del sufijo. El resultado, `log P("True" | video + prompt + instruction)`, es la recompensa.

Dado que el método solo depende de un VLM congelado, TOPReward es **zero-shot**: no hay pesos afinados que alojar. El "modelo" en LeRobot es un pequeño envoltorio alrededor de `Qwen3VLForConditionalGeneration` de `transformers` más la lógica de enmascarado de etiquetas. El procesador posee el tokenizador y construye el prompt de chat completo (patrón EO-1/Robometer).

## Lo que abarca la integración de LeRobot

- Configuración estándar `reward_model.type=topreward` a través de LeRobot.
- Carga de VLM a través de la API `Qwen3VLForConditionalGeneration` de `transformers`.
- Ensamblado del prompt y tokenización en el procesador (coincide con `QwenClient.compute_instruction_reward` de la versión original).
- `compute_reward()` devuelve un logaritmo de probabilidad escalar por muestra.
- Guardado y carga del modelo de recompensa de LeRobot — `save_pretrained` solo escribe `config.json` (el VLM se identifica mediante `vlm_name`).
- Un script de etiquetado sin conexión que escribe un `topreward_progress.parquet` (esquema compatible con SARM) para RA-BC y superposición.

La versión actual de LeRobot solo admite el cliente **Qwen3-VL**. Otros clientes de origen (Gemini, OpenAI, Gemma, Molmo) pueden añadirse como complementos posteriores.

## Requisitos de instalación

1. Instala LeRobot siguiendo la [Guía de instalación](./installation).
2. Instale el extra opcional TOPReward:

```bash
pip install -e ".[topreward]"
```

o, con `uv` desde un checkout de código fuente:

```bash
uv sync --extra topreward
```

Esto incorpora `transformers`. La primera vez que ejecutes TOPReward, Hugging Face también descargará los pesos del VLM desde el Hub (~16 GB para Qwen3-VL-8B-Instruct). Se recomienda encarecidamente el uso de una GPU.

## Entradas y salidas del modelo

TOPReward espera:

- Un video de trayectoria o una secuencia de fotogramas.
- Una descripción de la tarea en lenguaje natural.

En los conjuntos de datos de LeRobot, el preprocesador lee:

| Campo de configuración              | Predeterminado                     | Significado                                       |
| ------------------------- | --------------------------- | --------------------------------------------- |
| `reward_model.image_key`  | `observation.images.top`    | Observación de cámara utilizada por TOPReward          |
| `reward_model.task_key`   | `task`                      | Introduce datos complementarios para la cadena de la tarea |
| `reward_model.max_frames` | `16`                        | Límite de fotogramas por muestra                      |
| `reward_model.fps`        | `2.0`                       | Metadatos pasados al procesador de video de Qwen   |
| `reward_model.vlm_name`   | `Qwen/Qwen3-VL-8B-Instruct` | id del Hugging Face Hub del VLM subyacente     |

El modelo devuelve:

- `compute_reward(batch)`: una log-probabilidad por muestra. Mayor = mejor alineación tarea-video. Cuando `success_threshold` es finito, devuelve el valor binario umbralizado en su lugar.

## Uso

### Cargar el modelo de recompensa directamente

```python
from lerobot.rewards.topreward import TOPRewardConfig, TOPRewardModel

cfg = TOPRewardConfig(
    vlm_name="Qwen/Qwen3-VL-8B-Instruct",
    device="cuda",
)
reward_model = TOPRewardModel(cfg)
```

### Usar la fábrica de recompensas

```python
from lerobot.rewards import make_reward_model, make_reward_model_config, make_reward_pre_post_processors

cfg = make_reward_model_config(
    "topreward",
    vlm_name="Qwen/Qwen3-VL-8B-Instruct",
    device="cuda",
    image_key="observation.images.top",
)
reward_model = make_reward_model(cfg)
preprocessor, postprocessor = make_reward_pre_post_processors(cfg)
```

El preprocesador tokeniza el prompt completo (video + prefijo + sufijo de instrucción) y escribe los tensores de Qwen-VL + `prompt_length` bajo `observation.topreward.*`. El modelo lee esos tensores, aplica la máscara de etiquetas en función de `prompt_length` y extrae la recompensa de log-prob.

### Etiquetado de conjuntos de datos sin conexión

Escribe un `topreward_progress.parquet` para el entrenamiento de RA-BC y los videos superpuestos:

```bash
# Sparse-dense (15 anchors per episode, matches upstream)
uv run python -m lerobot.rewards.topreward.compute_rabc_weights \
    --dataset-repo-id lerobot/libero_10_image \
    --num-samples 15 \
    --device cuda
```

Luego, renderiza la superposición de progreso para cualquier episodio:

```bash
uv run examples/dataset/create_progress_videos.py \
    --repo-id lerobot/libero_10_image \
    --episode 0 \
    --progress-file topreward_progress.parquet \
    --gif
```

## Notas de configuración

### Ajustes del prompt

El prompt por defecto replica el del artículo original:

```text
prompt_prefix = "The above video shows a robot manipulation trajectory that completes the following task: "
prompt_suffix_template = "{instruction} Decide whether the above statement is True or not. The answer is: True"
```

Ambos se exponen en `TOPRewardConfig` para ablation. La plantilla de sufijo **debe** contener `{instruction}`.

### Plantilla de chat

`add_chat_template=True` envuelve el prompt completo (incluida la instrucción) con la plantilla de chat del tokenizador antes de la tokenización. El valor predeterminado es `False`, lo que coincide con los experimentos principales del artículo original.

## Limitaciones

- El port de LeRobot actual es **solo de inferencia y zero-shot**; `forward()` no se sobreescribe y `is_trainable` devuelve `False`.
- Solo se admite la **familia Qwen3-VL**; los demás clientes upstream están fuera de alcance.
- TOPReward hereda los sesgos del VLM subyacente.

## Referencias

- [Página del proyecto TOPReward](https://topreward.github.io/webpage/)
- [artículo de TOPReward](https://arxiv.org/abs/2602.19313)
- [Código original de TOPReward](https://github.com/TOPReward/TOPReward)
- [Qwen3-VL-8B-Instruct](https://huggingface.co/Qwen/Qwen3-VL-8B-Instruct)

## Cita

```bibtex
@article{chen2026topreward,
  title={TOPReward: Token Probabilities as Hidden Zero-Shot Rewards for Robotics},
  author={Chen, Shirui and Harrison, Cole and Lee, Ying-Chun and Yang, Angela Jin and
          Ren, Zhongzheng and Ratliff, Lillian J and Duan, Jiafei and Fox, Dieter and
          Krishna, Ranjay},
  journal={arXiv preprint arXiv:2602.19313},
  year={2026}
}
```

## Licencia

El código original de TOPReward está bajo licencia MIT. La versión de LeRobot sigue la licencia Apache 2.0 de LeRobot; los pesos de Qwen3-VL envueltos están sujetos a la licencia original de Qwen.

