File size: 18,576 Bytes
f5b36ab
 
 
 
 
d4a42b4
f5b36ab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d4a42b4
 
 
f5b36ab
 
 
d4a42b4
 
 
 
 
 
 
 
f5b36ab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d4a42b4
 
 
f5b36ab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d4a42b4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f5b36ab
 
 
d4a42b4
f5b36ab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
# Experiment log: Dense Visual Memory for OAT (LIBERO)

Живой журнал эксперимента для научрука / статьи (ablation section).  
Репозиторий: `BLT-OAT` (fork OAT). Бенчмарк: **LIBERO-10**, `N500` demos.

**Последнее обновление:** 2026-06-12 (UTC+3)

---

## 1. Гипотеза и смысл изменения

**Исходный OAT** подаёт в cross-attention короткую memory: pooled RGB-векторы (+ state в fused obs), длина порядка `n_obs_steps` (обычно **2 токена**). Пространственная структура кадра сжата в один вектор на timestep.

**Наша ветка (`use_dense_visual_memory=true`)** подаёт в тот же cross-attention decoder **длинную spatial memory**:
- patch-токены с каждой RGB-камеры на каждый obs-step;
- `time_embed` / `camera_embed`;
- опционально state-токены через MLP `state_to_memory`.

Мы **буквально льём в трансформер больше conditioning-токенов** (сотни вместо единиц). Это не «больше пикселей на входе», а **меньше сжатое** представление для attention.

**Ожидание:** модель может точнее локализовать объекты/гриппер → потенциально выше `success_rate`.  
**Риск:** оптимизация сложнее; в начале обучению «может поплохеть» (loss выше, медленнее сходимость, SR ниже), пока cross-attn и encoders не научатся отбирать релевантные patch’и.

---

## 2. Что изменили в архитектуре (vs default OAT)

| Компонент | Default OAT | Dense OAT (наш) |
|-----------|-------------|-----------------|
| RGB encoder | `RobomimicRgbEncoder` + global pool | `DenseRgbEncoder`: ResNet18 до `layer3`, **без** SpatialSoftmax/pool |
| Проекция в `d_model` | внутри pooled pipeline | `1×1 Conv` + `LayerNorm` на feature map |
| Memory length | `To` (≈2) | `2 камеры × To × L_patch + state_tokens` (L≈64 при 128×128) |
| State | в fused obs vector | отдельные memory-токены (`state_to_memory`) |
| Позиции memory | `cond_pos_emb` + `cond_emb` | dense: `memory_pos_emb` (`max_memory_len=1024`), `memory_is_embedded=True` |
| Cross-attn blocks | есть | **не меняли** |
| Action tokenizer (`OATTok`) | frozen | **не меняли** |
| Loss | CE по action tokens | **тот же** |

Ключевые файлы:
- `oat/perception/robomimic_vision_encoder.py``DenseRgbEncoder`
- `oat/policy/oatpolicy.py``get_dense_memory`, флаги абляций
- `oat/model/autoregressive/transformer_cache.py``_encode_memory`, `memory_pos_emb`
- `oat/config/train_oatpolicy.yaml` — hydra-флаги

Флаги абляций:
- `policy.use_dense_visual_memory` (alias: `use_cross_attn`)
- `policy.use_state_memory_tokens`
- `policy.use_task_uid_in_state_tokens`

---

## 3. Почему больший loss / MSE — не всегда «хуже»

Замечание научрука (A. Malinin): для dense-ветки **абсолютный train/val loss может быть выше**, чем у pooled OAT — это не обязательно регрессия.

Причины:
1. Другая размерность и статистика входа в decoder (длинная memory, другие pos emb).
2. Более богатое представление ≠ тот же CE-ландшафт, что у 2-токенного baseline.
3. **Целевая метрика проекта — `mean_success_rate` (rollout), не `reconst_mse` / `val_loss`.**  
   MSE/loss — sanity-check («учится / не взрывается»), финальное решение — SR на LIBERO eval.

---

## 4. Протокол валидации (короткий)

1. **Smoke / arch:** forward, backward, KV-cache dense vs legacy, shapes.
2. **Short A/B (N32, 1k steps):** legacy vs dense-варианты, одинаковый lr/batch.
3. **Long-run (N500):** полный датасет, `rollout_every=200`, best ckpt по `mean_success_rate`.

GO для long-run (short-run): dense **не хуже** legacy по val/reconst на стабильной конфигурации (допуск ~3–5%) **или** явно лучше по раннему mini-rollout.

---

## 5. Short A/B результаты (`libero10_N32_smoke`, 1k steps, 5 epochs)

| Конфиг | `val_loss` (final) | `test_reconst_mse` (final) | Комментарий |
|--------|-------------------:|---------------------------:|-------------|
| **legacy** (pooled) | 6.477 | 0.0132 | baseline |
| **dense + state + task_uid** | 7.531 | 0.1648 | сильная деградация |
| **dense_visual_only** (state off) | 6.848 | 0.0297 | лучше full dense+uid, хуже legacy |
| **dense + state, `task_uid` off** | **6.369** | **0.0133** | ≈ legacy по short metrics |

**Вывод short A/B:** проблема локализована в **`task_uid` в state memory**, не в dense visual tokens как таковых.

Run dirs (cluster): `output/smoke/ab_legacy_1k_r2`, `ab_dense_visual_only_1k`, `ab_dense_state_no_uid_1k`, `ab_dense_state_uid_1k_r2`.

---

## 6. Long-run (основной)

**Run (dense + task_uid):** `oat_dense_with_uid_long_0530_220204`

| Поле | Значение |
|------|----------|
| Train | ep 0→700 (ladder), resume ep 700→1000 |
| HF checkpoints | [300](https://huggingface.co/hackhackhack66666/OAT-BLT-LIBERO-300), [500](https://huggingface.co/hackhackhack66666/OAT-BLT-LIBERO-500), [700](https://huggingface.co/hackhackhack66666/OAT-BLT-Libero-700) |
| Лучший in-loop SR (resume) | **52.67%** @ ep-950 (`ep-0950_sr-0.527.ckpt`) |
| Phase B confirm (ep-0700) | **47.60% ± 1.75%** (50 ep/task, 3 seeds) |
| Артефакты в репо | [`docs/results/`](../results/) |

| Поле (старый run без uid) | Значение |
|------|----------|
| Сессия tmux | `oat_dense_no_uid_long_0530_213112` |
| Конфиг | `dense + state`, `use_task_uid_in_state_tokens=false` |
| Датасет | `libero10_N500.zarr` (`training.num_demo=500`) |
| WandB | `logging.mode=disabled` (первый запуск упал: `No API key configured`) |
| Лог | `output/long/oat_dense_no_uid_long_0530_213112.log` |
| Hydra run | `output/long/oat_dense_no_uid_long_0530_213112/` |

**Снимок метрик (2026-05-30, во время обучения):**
- `global_step` ≈ 3136, `epoch` ≈ 6
- `train_loss` ≈ 4.96 (снижается с ~7 на старте)
- `val_loss` (epoch 0): 6.595 — ранняя точка, не финал
- `mean_success_rate`: **ещё нет** (первый rollout на `epoch % 200 == 0`)

Предыдущая tmux-сессия `oat_dense_no_uid_long_0530_212603` — упала на init wandb.

---

## 7. Если `success_rate` сильно просядет — шаблон для ablation (статья / тикет)

Ниже — **готовые причины**, которые можно вставить в Discussion/Ablation, если dense < baseline OAT по SR после полного обучения.

### 7.1. Усложнение optimization (главная гипотеза)

- Cross-attention получает **~200–300+ memory tokens** вместо 2; effective capacity растёт, но gradient signal размазывается по patch’ам.
- Новые модули (`DenseRgbEncoder`, `memory_pos_emb`, time/camera embed) учатся с нуля при frozen tokenizer → дольше «сходимость» closed-loop политики.
- На ранних эпохах политика может вести себя хуже baseline при нормальном или даже падающем train loss.

### 7.2. Несопоставимость offline-метрик

- `reconst_mse` / `val_loss` слабо коррелируют с `success_rate` (особенно при смене conditioning).
- Возможен сценарий: loss ≈ baseline, SR ниже — или loss выше, SR сопоставим (как отмечал научрук).

### 7.3. `task_uid` в memory (подтверждено абляцией)

- Включение `task_uid` embedding в state tokens резко ухудшило short-run (`mse` 0.013 → 0.165).
- Возможные механизмы: неверный id range/семантика, конфликт с state MLP, переобучение на spurious task signal.
- Long-run идёт **без** `task_uid`; если SR всё равно низкий — причина не только в uid.

### 7.4. Inductive bias pooled OAT

- Global pooling даёт компактный, устойчивый вектор — для некоторых LIBERO задач этого может хватать.
- Dense memory полезен, когда нужна пространственная привязка; если baseline уже «выдавливает» SR из pool, выигрыш может быть < overhead.

### 7.5. Compute / eval budget

- Меньше эффективных «эпох» на единицу wall-clock (тяжелее forward).
- Редкий rollout (`rollout_every=200`) → поздняя обратная связь по SR; best checkpoint по SR может не совпасть с best по loss.

### 7.6. Гиперпараметры не перенастроены

- Те же `policy_lr`, `obs_enc_lr`, batch=256, что у pooled baseline — для длинной memory может быть suboptimal (нужен отдельный sweep).

**Формулировка для тикета (кратко):**  
> Dense memory увеличивает информационную ёмкость conditioning, но усложняет обучение cross-attention. Наблюдаемый/ожидаемый провал SR не обязательно означает ошибку реализации: он может следовать из optimization difficulty, слабой корреляции offline-метрик с SR, и подтверждённой деградации от `task_uid` (исключена в production run). Финальная оценка — только paired rollout на LIBERO-10 с теми же seeds и eval protocol, что baseline.

---

## 8. Git / деплой

| Commit | Описание |
|--------|----------|
| (ранние) | DenseRgbEncoder, OATPolicy memory, decoder `memory_pos_emb` |
| `9c305cd` | fix `lr_scheduler` imports (diffusers) |
| `020f63d` | `use_state_memory_tokens` |
| `9481ae0` | `use_task_uid_in_state_tokens` |

Синк на кластер: `rsync` в `~/OAT-RoboMimic-Fine-tune/BLT-OAT/`, **exclude `data/`**.

Docker: `oat_robomimic_askhabaliev_gs`.  
Tokenizer ckpt: `tokenizer_ep-0950_mse-0.002.ckpt` (HF Mirageinv/oat) — **без изменений**.

---

## 9. Как обновлять этот лог и анализировать все run'ы

Каждый Hydra-run пишет в свою папку:
- `logs.json` — train_loss, val_loss, test_reconst_mse, mean_success_rate (на rollout-эпохах)
- `.hydra/overrides.yaml` — конфиг (embed_dim, task_uid, …)
- `checkpoints/` — ckpt для eval и attention viz
- `*.log` — tee-лог в `output/long/`

**Манифест всех run'ов:** `output/long/RUN_MANIFEST.md` (append при sweep)

**Сводка по всем run'ам:**
```bash
python scripts/summarize_training_runs.py --root output/long
# -> output/long/summary/all_runs_summary.csv + .md
```

**Графики loss / MSE / SR:**
```bash
python scripts/plot_training_runs.py \
  output/long/oat_dense_with_uid_long_0530_220204 \
  output/long/dense_emb128_with_uid_* \
  --out output/long/summary/plots
```

**Rollout eval (success rate) из checkpoint:**
```bash
python scripts/eval_policy_sim.py \
  -c output/long/<run>/checkpoints/latest.ckpt \
  -o eval/<run_name>

# Ladder 300/500/700 (paired SR): docs/plans/libero_sr_eval_ladder_300_500_700.md
# PHASE=A bash scripts/cluster/run_ladder_sr_eval.sh
```

**Cross-attention heatmaps:**
```python
from oat.common.attention_viz import visualize_cross_attention
# policy from checkpoint + sample batch -> overlay on RGB
```

Команды мониторинга (cluster):

```bash
tmux ls
tmux attach -t oat_dense_no_uid_long_0530_213112
tail -f ~/OAT-RoboMimic-Fine-tune/BLT-OAT/output/long/oat_dense_no_uid_long_0530_213112.log
python3 -c "
import json; from pathlib import Path
p=Path('~/OAT-RoboMimic-Fine-tune/BLT-OAT/output/long/oat_dense_no_uid_long_0530_213112/logs.json').expanduser()
rows=[json.loads(l) for l in p.read_text().splitlines() if l.strip()]
for k in ['mean_success_rate','val_loss','train_loss']:
    r=[x for x in rows if k in x]
    if r: print(k, r[-1])
"
```

---

## 10. Changelog

### 2026-06-13 — Phase B confirm ep-0950 завершён
- Cluster tmux `oat_confirm_ep0950`: 50 ep/task × 3 seeds, `ep-0950_sr-0.527.ckpt`.
- **Mean SR: 50.60% ± 0.76%** (per-seed: 52.0% / 49.4% / 50.4%).
- Артефакты: `docs/results/phase_b_confirm_ep0950/`, dashboard `phase_b_confirm_ep-0950_dashboard.png`.

### 2026-06-12 — Документация и зеркала артефактов
- README: полный research timeline, HF-ссылки, визуализации из `docs/results/`.
- Resume train/eval: cluster-скрипты (`run_resume_train_sim_eval.sh`, lazy LiberoRunner, `mp_context=spawn`).
- Phase B confirm: 47.60% ± 1.75%; resume in-loop best 52.67% @ ep-950.

### 2026-05-31 — HF checkpoint upload watcher (epochs 300 & 500, train continues)
- Script: `scripts/watch_hf_checkpoint_upload.py`
- Launcher: `scripts/cluster/launch_hf_upload_tmux.sh`
- Default repo: `hackhackhack66666/OAT-BLT-LIBERO-300` (hub API; git-xet fallback)
- Checkpoint ladder HF repos:
  - 300 → `hackhackhack66666/OAT-BLT-LIBERO-300`  - 500 → `hackhackhack66666/OAT-BLT-LIBERO-500`  - 700 → `hackhackhack66666/OAT-BLT-Libero-700`  - 900, 1100 → TBD (`OAT-BLT-Libero-900`, `OAT-BLT-Libero-1100`)
- Each HF repo: `README.md` (EN) + `training_metrics_dashboard.png` + this experiment log
- SR eval plan: `docs/plans/libero_sr_eval_ladder_300_500_700.md` (sim eval; ckpts kept on disk until done)
- `--target-epochs` + `--epoch-repo EPOCH=REPO` per snapshot; train continues
- Trigger when `epoch > target` (so ep-N ckpt exists); train not stopped
- Requires `HF_TOKEN`; logs: `hf_upload_report.jsonl`, `hf_upload_watch.log`

### 2026-05-31 — Counterfactual early-stop watcher (no real stop)
- Добавлен CPU-only watcher: `scripts/watch_early_stop_report.py`.
- Добавлен launcher: `scripts/cluster/launch_early_stop_watch_tmux.sh`.
- Watcher проверяет с `epoch >= 0` (ранние ep помечаются как noisy в отчёте), каждый час, пишет:
  - `early_stop_report.jsonl` (история проверок),
  - `early_stop_report.md` (сводка для статьи).
- Важно: watcher **не останавливает** обучение, только пишет где **был бы** стоп.

### 2026-05-30 — Sweep relaunch + post-hoc tooling
- Скрипты: `summarize_training_runs.py`, `plot_training_runs.py`, `RUN_MANIFEST.md`.
- Sweep на GPU1 в tmux, `USE_TASK_UID=true` (как основной run).

### 2026-05-30 — Перезапуск с `task_uid`
- Остановлен long-run без uid (~epoch 15) и embed-dim sweep.
- Новый long-run: `dense + state + task_uid=true`, N500, tmux `oat_dense_with_uid_long_*`.

### 2026-05-30 — Overnight embed-dim sweep (GPU1)
- Запущен последовательный sweep `embed_dim` ∈ {128, 384, 512} на свободной GPU1.
- Baseline 256 остаётся на GPU0 (`oat_dense_no_uid_long_0530_213112`).
- Скрипт: `scripts/cluster/overnight_dense_embed_dim_sweep.sh`, tmux: `oat_dense_embed_sweep_night`.

### 2026-05-30 — Short A/B завершён, long-run запущен
- Реализованы dense memory + абляционные флаги.
- Short A/B: стабильная конфигурация **`dense + state, без task_uid`**.
- Long-run N500 в tmux `oat_dense_no_uid_long_0530_213112`, wandb off.
- Train идёт: к ~epoch 6 `train_loss` ~4.96; SR rollout ещё не было.
- Создан этот experiment log для статьи/тикета.

### 2026-05-30 — Первый long-run упал
- tmux `oat_dense_no_uid_long_0530_212603`: `wandb.errors.UsageError: No API key`.
- Перезапуск с `logging.mode=disabled`.

---

## 11. Overnight sweep: `embed_dim` / `dense_feature_dim`

**Мотивация:** при фиксированной spatial resolution (L patch’ей) меняется только ширина token embedding — влияет на ёмкость visual memory и нагрузку на cross-attn.

**Ограничение кода:** `dense_feature_dim` должен совпадать с `policy.embed_dim` (иначе `dense memory expects cond dim {n_emb}`).

| GPU | Задача | `embed_dim` | batch |
|-----|--------|------------:|------:|
| 0 | основной long-run | 256 | 256 |
| 1 | sweep (последовательно) | 128 → 384 → 512 | 256 / 192 / 128 |

Скрипт: `scripts/cluster/overnight_dense_embed_dim_sweep.sh`  
tmux (cluster): `oat_dense_embed_sweep_night`  
Конфиг как у основного run: dense + state, **без** `task_uid`, `logging.mode=disabled`.

---

## 12. Открытые вопросы

1. Финальный **SR dense vs legacy** на N500 после `rollout_every`.
2. Какой `embed_dim` лучше по SR / sample efficiency (sweep 128/256/384/512)?
3. Нужен ли отдельный sweep lr / warmup для dense encoders?
4. Как корректно вернуть `task_uid` (mapping id, gating, не в state MLP)?
5. Attention viz на лучшем ckpt — куда смотрит policy на успехе/провале?

---

## 13. Ссылки

- План реализации: [`docs/plans/cross-attn_dense_oat.md`](plans/cross-attn_dense_oat.md)
- Attention viz: `oat/common/attention_viz.py`