# VLA-JEPA

Это порт **VLA-JEPA** для LeRobot — модели Vision-Language-Action, которая объединяет языковую основу Qwen3-VL, самообучаемую модель мира для видео (V-JEPA2) и головку действий flow-matching DiT.

---

## Обзор архитектуры

VLA-JEPA состоит из трёх основных компонентов:

| Компонент               | Модуль                            | Роль                                                    |
| ----------------------- | --------------------------------- | ------------------------------------------------------- |
| **Базовая модель Qwen3-VL**   | `Qwen3VLInterface`                | Объединяет изображения и языковую инструкцию в контекстные токены |
| **Голова действий DiT-B**   | `VLAJEPAActionHead`               | Диффузия с потоковым согласованием для чанка действий           |
| **Мировая модель V-JEPA2** | `ActionConditionedVideoPredictor` | Потери самообучающегося предсказания видео (только для обучения)   |

### Поток данных

**Обучение:**

1. Видеоклип из `num_video_frames` кадров кодируется V-JEPA2 в токены патчей для каждого кадра.
2. Базовая модель Qwen3-VL обрабатывает изображения с нескольких ракурсов + инструкцию к задаче и формирует последовательность контекстных токенов, включающую специальные токены действий (для настройки мировой модели) и токены воплощения.
3. Голова действий получает эти токены контекста в качестве ключей и значений для кросс-внимания и предсказывает очищенный от шума блок действий с помощью flow matching.
4. Предиктор мировой модели использует токены действий, извлечённые из Qwen, для предсказания будущих эмбеддингов кадров V-JEPA2; к потере действий добавляется регрессионная потеря для этих предсказаний.

**Вывод:** используются только Qwen и головка действий. Модель мира на этапе вывода не требуется.

### Детали action head

Доступные пресеты через `action_model_type`:

| Пресет  | Головы | Размер головы |
| ------- | ----- | -------- |
| `DiT-B` | 12    | 64       |
| `DiT-L` | 32    | 48       |

Пресет задаёт только геометрию внимания, и каждую запись можно переопределить с помощью `action_num_heads` / `action_attention_head_dim`. Из неё следуют две ширины:

- внутренняя ширина **DiT** равна `heads x head_dim` (768 для `DiT-B`), она вычисляется, а не настраивается;
- Ширина **выхода** DiT, а также ширина MLP-блоков декодера действий и энкодера состояния, задается параметром `action_hidden_size` (по умолчанию 1024).

Так `DiT-B` использует трансформер шириной 768, который проецируется в 1024. Эти два значения независимы.

### Детали модели мира

Видео-предиктор представляет собой трансформер в стиле ViT (`ActionConditionedVideoPredictor`), который принимает:

- **Токены кадров**: эмбеддинги патчей V-JEPA2, спроецированные в `predictor_embed_dim`
- **Токены действий**: эмбеддинги токенов действий Qwen, спроецированные в `predictor_embed_dim`

Он использует блочно-каузальное внимание, благодаря которому каждый временной шаг может обращать внимание на все предыдущие шаги. Входное значение `embed_dim` предсказателя равно `num_views × video_encoder_hidden_size` (например, 2 вида × 1024 = 2048 для предобученных контрольных точек).

---

## Предобученные контрольные точки

Внутри организации LeRobot здесь доступны три контрольные точки: [`lerobot/VLA-JEPA`](https://huggingface.co/collections/lerobot/vla-jepa), сконвертированные из [ginwind/VLA-JEPA](https://huggingface.co/ginwind/VLA-JEPA):

| Чекпоинт                    | Набор данных           | Камеры                 | Мировая модель | Размерность действия |
| ----------------------------- | ----------------- | ----------------------- | ----------- | ---------- |
| `lerobot/VLA-JEPA-LIBERO`     | LIBERO-10         | 2 (agentview + wrist)   | Включено     | 7          |
| `lerobot/VLA-JEPA-Pretrain`   | DROID 1.0.1       | 2 (виды с левой стороны) | Включено     | 7          |
| `lerobot/VLA-JEPA-SimplerEnv` | OXE Bridge / RT-1 | 1 (дублируется ×2)  | Включено     | 7          |

Все контрольные точки используют `Qwen/Qwen3-VL-2B-Instruct` в качестве языковой основы.

---

## Конфигурация

Ключевые параметры в `VLAJEPAConfig`:

| Параметр                    | По умолчанию       | Описание                                                                                                                                                                                                                                                |
| ---------------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `chunk_size`                 | 7             | Количество действий, предсказанных за один вызов инференса                                                                                                                                                                                                             |
| `n_action_steps`             | 7             | Шаги, выполненные из предсказанного фрагмента до повторного планирования                                                                                                                                                                                                 |
| `num_video_frames`           | 8             | Длина видеоклипа, передаваемая в мировую модель                                                                                                                                                                                                                   |
| `enable_world_model`         | `True`        | Загружать ли и обучать предиктор V-JEPA2                                                                                                                                                                                                            |
| `world_model_loss_weight`    | 0.1           | Вес функции потерь предсказания JEPA относительно функции потерь действия                                                                                                                                                                                             |
| `causal_world_model_context` | `False`       | Кодируйте контекст мировой модели каузально (один проход V-JEPA2 только с префиксом на каждую позицию контекста), чтобы двунаправленное внимание не могло «утечь» будущими кадрами во вход предиктора. Стоимость — `t_enc_ctx` дополнительных вызовов энкодера                                            |
| `num_inference_timesteps`    | 4             | Шаги интегрирования Эйлера для удаления шума из действия                                                                                                                                                                                                               |
| `freeze_qwen`                | `False`       | Заморозьте базовую модель Qwen3-VL и обучайте только головку действий                                                                                                                                                                                                |
| `reinit_modules`             | `None`        | Ключевые префиксы, которые разрешено случайно переинициализировать при загрузке (для переноса между разными воплощениями, см. [Доработка на другом воплощении](#fine-tuning-on-a-different-embodiment))                                                                            |
| `resize_images_to`           | `None`        | `(height, width)` — размер, к которому каждый кадр камеры приводится перед передачей в визуальную башню Qwen3-VL. Значение `None` сохраняет исходное разрешение, при этом количество патчей в Qwen3-VL растёт вместе с ним, поэтому камера с разрешением 720x1280 может исчерпать память GPU. В опубликованных контрольных точках используется `[224, 224]` |
| `gripper_dim`                | 6             | Индекс измерения захвата в векторе действия. Игнорируется, если `gripper_joint_names` совпадает с именем действия в наборе данных                                                                                                                                      |
| `gripper_joint_names`        | `["gripper"]` | Имена измерений действий, идентифицирующие захват; совпавший индекс имеет приоритет над `gripper_dim`                                                                                                                                                                  |
| `gripper_threshold`          | 0.5           | Порог, используемый в `pre_snap_gripper_action` и `binarize_gripper_action`. Обратите внимание, что `binarize` выполняется _после_ денормализации, поэтому сравнение происходит с физическим значением захвата                                                                          |
| `pre_snap_gripper_action`    | `False`       | Приведите значение gripper dim к {0, 1} перед денормализацией. Специфично для LIBERO, см. ниже                                                                                                                                                                          |
| `binarize_gripper_action`    | `False`       | Бинаризируйте размер захвата в {-1, 1} после денормализации. Специфично для LIBERO, см. ниже                                                                                                                                                                      |
| `clip_normalized_actions`    | `True`        | Обрезка нормализованных действий до диапазона [-1, 1] перед денормализацией. Применяется только в том случае, если `ACTION` использует `MIN_MAX`; игнорируется (с предупреждением) при использовании `MEAN_STD`, где это привело бы к обрезке на уровне 1 сигма                                                                          |
| `world_model_num_views`      | `None`        | Камера, для которой предназначен предиктор world-model. Закодирована в формах контрольных точек. `None` переключается на `jepa_tubelet_size`, что соответствует значениям, закодированным в опубликованных контрольных точках                                                                                |

`pre_snap_gripper_action` и `binarize_gripper_action` являются портом цикла оценки LIBERO из starVLA и корректны только для конвенции действий LIBERO. `pre_snap` записывает {0, 1} в пространство _normalized_, денормализатор отображает эти значения на середину и максимум диапазона, а затем `binarize` сравнивает это значение **physical** с `gripper_threshold` (0.5). Если захват измеряется в градусах, мм или в диапазоне [0, 100], оба значения окажутся выше порога, и управляющий сигнал захвата станет константой. По этой причине по умолчанию они установлены в `False`; включайте их только для конфигураций в стиле LIBERO, и при этом задавайте `gripper_threshold` в собственных единицах измерения захвата. Фабрика процессоров выводит предупреждение, если статистика набора данных показывает, что диапазон не может работать.

---

## Обучение

Количество шагов обучения может варьироваться в зависимости от размера набора данных и вычислительного бюджета. В исходной статье модель предварительно обучалась на 50k шагов на ssv2 + droid совместно, затем на дополнительные 30k шагов на LIBERO, однако при дообучении из предоставленных предварительно обученных контрольных точек хорошее качество может быть достигнуто за меньшее количество шагов.

### Полная обучение с нуля

```bash
lerobot-train \
  --policy.type=vla_jepa \
  --policy.repo_id=your_org/your_repo \
  --dataset.repo_id=your_org/your_dataset
```

### Доработка из предварительно обученной контрольной точки

```bash
lerobot-train \
  --policy.path=lerobot/VLA-JEPA-Pretrain \
  --policy.repo_id=your_org/your_repo \
  --dataset.repo_id=your_org/your_dataset
```

Если вы хотите заморозить базовую модель Qwen и обучать только головку действий, установите `policy.freeze_qwen=True`:

```bash
lerobot-train \
  --policy.path=lerobot/VLA-JEPA-Pretrain \
  --policy.repo_id=your_org/your_repo \
  --policy.freeze_qwen=true \
  --dataset.repo_id=your_org/your_dataset
```

### Доподнастройка на другом воплощении

Если целевой робот имеет другую размерность действий или состояний, чем предобученная контрольная точка, слои проекции входа/выхода в action head будут иметь несовпадающие формы и не смогут быть загружены напрямую. `reinit_modules` позволяет указать префиксы ключей, для которых допустимо несовпадение форм — эти слои будут случайным образом переинициализированы, тогда как все остальные веса будут переиспользованы из контрольной точки. Любое несовпадение форм вне указанных префиксов приводит к ошибке.

Слои, зависящие от `action_dim` и `state_dim`:

| Слой                                     | Префикс ключа                          |
| ----------------------------------------- | ----------------------------------- |
| Кодировщик действий (action_dim → inner_dim)   | `model.action_model.action_encoder` |
| Декодер действий (hidden_size → action_dim) | `model.action_model.action_decoder` |
| Кодировщик состояния (state_dim → inner_dim)     | `model.action_model.state_encoder`  |

```bash
lerobot-train \
  --policy.path=lerobot/VLA-JEPA-Pretrain \
  --policy.repo_id=your_org/your_repo \
  --policy.freeze_qwen=true \
  --policy.reinit_modules='["model.action_model.action_encoder", "model.action_model.action_decoder", "model.action_model.state_encoder"]' \
  --dataset.repo_id=your_org/your_dataset
```

Если у вашего робота нет проприоцептивного состояния, опустите `model.action_model.state_encoder` из списка.

### Воспроизведение результатов LIBERO

**Обучение на LIBERO:** запускает обучение с контрольной точки Pretrain, обучает модель в течение 30k шагов на наборе данных LIBERO. В оригинальной статье упоминается обучение на 8 GPU с размером батча 32, что означает глобальный размер батча 256.

```bash
lerobot-train \
  --policy.path=lerobot/VLA-JEPA-Pretrain \
  --policy.repo_id=your_org/your_repo \
  --dataset.repo_id=HuggingFaceVLA/libero \
  --steps=30000
```

**Оценка предобученного контрольного файла LIBERO-10:**

```bash
lerobot-eval \
  --policy.path=lerobot/VLA-JEPA-LIBERO \
  --env.type=libero \
  --env.task=libero_spatial,libero_object,libero_goal,libero_10 \
  --eval.n_episodes=10 \
  --eval.batch_size=5
```

Для оценки только подмножества задач:

```bash
lerobot-eval \
  --policy.path=lerobot/VLA-JEPA-LIBERO \
  --env.type=libero \
  --env.task=libero_10 \
  --env.task_ids='[0,1,2]' \
  --eval.n_episodes=10 \
  --eval.batch_size=5
```

**Ожидаемые результаты:**

| Suite          | Эпизоды | Успехи | Уровень успеха |
| -------------- | -------- | --------- | ------------ |
| libero_spatial | 100      | 93        | **95.0%**    |
| libero_object  | 100      | 100       | **100.0%**   |
| libero_goal    | 100      | 98        | **98.0%**    |
| libero_10      | 100      | 96        | **93.0%**    |
| **Общее**    | **400**  | **387**   | **96.5%**    |

---

## Допобучение на наборах данных с разным количеством камер

Предобученный предиктор мировой модели обучался с `embed_dim = world_model_num_views × 1024`, то есть с двумя камерными видами.

Количество кадров в этом представлении ранее читалось из `jepa_tubelet_size`, который также задавал размер _временного_ tubelet в кодере JEPA. Теперь для этого используется поле `world_model_num_views`; если оставить его равным `None`, значение будет взято из `jepa_tubelet_size`, чтобы опубликованные контрольные точки продолжали загружаться без изменений.

**Поведение по умолчанию — просмотр отступов / обрезки (действия не требуются)**

При дообучении из `VLA-JEPA-Pretrain` модель автоматически настраивает количество представлений, подаваемых в мировую модель, в соответствии с `world_model_num_views`:

- **Наборы данных с одним видом (например, BridgeV2):** латентное представление одного вида дублируется для создания входных данных модели мира с двумя видами, что сохраняет самонастраивающийся сигнал JEPA без какого-либо рассогласования весов.
- **>Датасеты с более чем 2 видами (например, DROID с 3 видами):** все виды передаются в Qwen backbone (для более богатого контекста), но для world model используются только первые `world_model_num_views` виды (один вид с запястья и один вид от третьего лица, в соответствии с заданным порядком видов).

**Вариант 1 — отключить world model**

Установите `enable_world_model=False`, чтобы полностью пропустить потерю JEPA. Загружаются и обучаются только базовая модель Qwen и головная часть для действий. Этого достаточно для хорошей производительности действий.

```bash
lerobot-train \
  --policy.path=lerobot/VLA-JEPA-Pretrain \
  --policy.enable_world_model=false \
  --policy.repo_id=your_org/your_repo \
  --dataset.repo_id=your_org/single_camera_dataset
```

**Вариант 2 — Переинициализация проекции входа предсказателя**

Если вы хотите изменить `world_model_num_views` на значение, отличное от 2, загрузите контрольную точку с `strict=False` и заново инициализируйте `model.video_predictor.predictor_embed` для нового `embed_dim`. Все остальные веса блоков предсказателя (внимание, MLP, нормализация, выходная проекция) не зависят от количества камер и могут быть переиспользованы из предобученной контрольной точки.

---

## Цитирование

```bibtex
@misc{sun2026vlajepaenhancingvisionlanguageactionmodel,
  title         = {VLA-JEPA: Enhancing Vision-Language-Action Model with Latent World Model},
  author        = {Jingwen Sun and Wenyao Zhang and Zekun Qi and Shaojie Ren and Zezhi Liu and Hanxin Zhu and Guangzhong Sun and Xin Jin and Zhibo Chen},
  year          = {2026},
  eprint        = {2602.10098},
  archivePrefix = {arXiv},
  primaryClass  = {cs.RO},
  url           = {https://arxiv.org/abs/2602.10098},
}
```

---

## Лицензия

Веса распространяются на условиях лицензии исходного репозитория [ginwind/VLA-JEPA](https://huggingface.co/ginwind/VLA-JEPA) (**Apache 2.0 License**). Код интеграции LeRobot распространяется по лицензии **Apache 2.0 License**.

