Files
caption-test/DIAGNOSTIC_DSS_MoCo_2026-04-25.md
2026-04-27 11:57:29 +03:00

159 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Диагностика: коллапс recall на эпохе 1 — DSS + MoCo queue
**Дата:** 2026-04-25
**Конфиг:** `conf/gtauav_balanced.gin` (gate=0.7, with text)
**Запуск:** `python -m src.training.train_gtauav --config conf/gtauav_balanced.gin --filter-meta meta/seg_filter.json`
---
## 1. Симптомы из лога
### Метрики по эпохам
| Эпоха | Режим сэмплера | LR | train loss | train R@1 | val R@1 | val AP | eval loss |
|---|---|---|---|---|---|---|---|
| **ep0** | mutex-only (warmup) | 5.00e-05 | 4.2239 | **0.0742** | 0.0758 | 0.1511 | 2.0783 |
| **ep1** | DSS активирован, MoCo queue полная | 1.00e-04 | 4.4218 ↑ | **0.0120** ↓6× | 0.0107 | 0.0328 | 2.0786 |
### Ключевые наблюдения
- **Recall обвалился ровно на эпохе включения DSS** (warmup=1, поэтому DSS стартует на ep1)
- **Train loss выросла** (4.22 → 4.42), хотя должна падать
- **Eval loss практически стоит** (2.0783 → 2.0786) — модель не учится на чистом батч-сигнале
- **Разрыв train/eval loss > 2×** — большая часть train loss идёт от MoCo+hard-mining компонент, не от in-batch contrast
- Gate stable: `gate_q=0.70`, `gate_g=0.70` (текст не вытаскивает дополнительный градиент)
- `tau=0.07` (clamped, learnable, но почти не двигается → loss не учит температуру)
### Сбой при сохранении чекпойнта
```
RuntimeError: [enforce fail at inline_container.cc:668] .
unexpected pos 289048128 vs 289048016
```
**Причина:** диск переполнен (ENOSPC). Подтверждено независимо: `mkdir` в session-env тоже падает с ENOSPC. Это инфраструктурный баг, не алгоритмический — но он не даёт сохранить состояние и продолжить.
---
## 2. Почему DSS «не работает» на этом этапе
### 2.1. Mutex-constraint спасает от false negatives только формально
Mutex исключает дронов с пересекающимися `sat_candidates`. Но DSS специально пакует **визуально похожих** дронов — у которых sat-кандидаты могут быть *разными tiles*, но при этом сцены практически идентичные:
- один и тот же район Лос-Сантоса
- та же высота полёта
- та же time-of-day, та же погода (синтетика GTA-V)
Для холодного энкодера такие «negatives» неотличимы от позитива → softmax пытается развести то, что развести нечем → градиенты шумные и противоречивые.
### 2.2. Re-embed раз в эпоху — слишком редко в начале обучения
- На ep1 LR удваивается (5e-5 → 1e-4 после warmup)
- Encoder за эпоху смещается сильно (MONA-адаптеры учатся быстро)
- Батчи внутри эпохи собираются по embeddings, посчитанным **до** этих обновлений
- То есть «similarity» в DSS — это similarity **вчерашнего** энкодера
- Re-embed на ep1 занял 313s, но между двумя re-embed encoder успевает измениться существенно
### 2.3. Эпохи 0 на mutex-only недостаточно как warmup
- К моменту запуска DSS R@1=0.074 на полной gallery (~2684 уникальных тайлов)
- Random baseline ≈ 1/2684 ≈ 0.0004
- 0.074 — это «модель чуть-чуть оторвалась от случая», но качество embeddings ещё недостаточно
- DSS усиливает шум, а не сигнал: «визуально похожих» определяет *шумный* энкодер
---
## 3. Почему MoCo queue делает хуже
### 3.1. Representation drift в queue
- Queue хранит **4096 embeddings**, посчитанных в разные моменты времени разными версиями encoder
- На ep01, когда MONA-адаптеры учатся быстро, разница между «свежим» и «3 шага назад» embedding'ом — заметная
- Эти устаревшие негативы дают сигнал в направлении, в котором энкодер уже не находится
- Без momentum encoder (как в оригинальном MoCo) drift ничем не сглажен
### 3.2. `hard_mining_k=512` амплифицирует ошибку queue
- Из 4096 берутся 512 «самых трудных»
- «Трудность» меряется в **текущем** feature space
- Сами вектора лежали там в **старом** feature space
- На холодном энкодере это эквивалентно «учиться отличать себя-позитива от себя-вчерашнего»
- Loss растёт, recall падает
### 3.3. Тройная hard-negative композиция → collapse-режим
Все три механизма независимо подсовывают «трудные» негативы:
| Механизм | Источник трудности |
|---|---|
| DSS | визуально близкие в батче |
| Mutex sampler | in-batch contrast после фильтра |
| `hard_mining_k=512` | top-K из MoCo queue |
На warm encoder это polish; на cold encoder это **too-hard negatives problem** (известная failure mode у contrastive learning, Robinson et al., 2021):
- Модель не получает «лёгких» примеров, на которых формируется базовое embedding-пространство
- Градиенты толкают её в произвольных направлениях
- Embedding space коллапсирует или размывается
---
## 4. Подтверждения из истории коммитов
```
8f8cbb1 Diagnostic baseline v2: also disable MoCo queue
c25bd64 Diagnostic baseline: disable DSS + hard mining, fresh output dir
9a7fbff Fix plot_combined: fallback from 'total' to 'train_loss'
70f1617 Fix autograd in-place error: move memory-bank enqueue after backward
8197ab2 Fix training loop: only pass positive_weights to WeightedInfoNCELoss
```
Подозрение на DSS+MoCo уже было — текущие числа подтверждают его эмпирически.
---
## 5. Рекомендации
### 5.1. Cold-start curriculum (приоритет)
1. **Несколько эпох mutex-only без MoCo и без hard_mining** до R@1 хотя бы ~0.2 на train.
- Конкретно: `sampler_type="mutex"`, `loss.use_memory_bank=False`, `loss.hard_mining_k=0`.
2. Затем **включить MoCo queue с warm-up**: либо momentum encoder, либо queue pre-fill 1 эпоху со `stop_grad` на queue updates.
3. **DSS включать только после** того, как embeddings стали discriminative (R@1 на train > 0.2). Иначе «similar» = «random».
4. **`hard_mining_k` стартует с 0** и поднимается curriculum-схемой (например, 0 → 64 → 256 → 512 по эпохам).
### 5.2. Изменения по DSS
- Сократить интервал re-embed (раз в N шагов, не раз в эпоху) — минимум первые 2-3 эпохи
- Или временно фиксировать кеш embeddings из чистого baseline (без DSS) и использовать его как «референсный» для сэмплинга
### 5.3. Изменения по MoCo
- Добавить momentum encoder (EMA на ключи, как в оригинальном MoCo) — это решает drift
- Либо очищать queue на каждой эпохе (теряем эффект, но избегаем drift)
- Размер queue 4096 при batch 64 → 64 батча в очереди = слишком долгая история для холодного энкодера
### 5.4. Инфраструктура
- **Освободить диск** (ENOSPC блокирует чекпойнты и часть утилит)
- Возможно, перенести `out/gtauav/` на другой диск или почистить старые runs
- Добавить pre-flight disk check перед `torch.save`
---
## 6. План эксперимента для подтверждения
| Run | Sampler | MoCo queue | hard_mining_k | Цель |
|---|---|---|---|---|
| **A** (clean baseline) | mutex | off | 0 | Подтвердить, что без DSS/MoCo recall растёт нормально |
| **B** (only MoCo) | mutex | on | 0 | Изолировать вклад queue |
| **C** (only hard_mining) | mutex | on | 512 | Изолировать вклад top-K mining |
| **D** (only DSS) | dss | off | 0 | Изолировать вклад DSS |
| **E** (full, current) | dss | on | 512 | Reproduce coллапс |
Decision rule:
- Если A учится нормально (R@1 растёт монотонно), а E коллапсирует — это decisive ablation
- Если B/C/D по отдельности тоже коллапсируют — проблема в каждом компоненте
- Если только E коллапсирует — проблема в композиции
---
## 7. Альтернативная гипотеза (менее вероятная)
LR=1e-4 на проекциях после warmup может быть слишком большим для shared DINOv3 + MONA с малым trainable %. Симптомы похожи (резкий обвал на эпохе с полным LR), но не объясняют, почему mutex-only baseline (commit `c25bd64`) учится нормально без изменения LR. Так что это вторичный фактор.
---
## 8. Резюме
**Главный диагноз:** связка `DSS + MoCo queue + hard_mining_k=512` создаёт **too-hard negatives problem** на холодном энкодере. Каждый компонент по отдельности рассчитан на warm encoder; их композиция на ep1 (когда R@1 ещё ~0.07) делает задачу нерешаемой и приводит к коллапсу embedding space.
**Решение:** curriculum — сначала mutex+CE без queue/mining до R@1≈0.2, потом постепенно включать остальные механизмы.
**Блокер:** диск переполнен (ENOSPC), без освобождения места дальнейшее обучение и сохранение чекпойнтов невозможно.