159 lines
11 KiB
Markdown
159 lines
11 KiB
Markdown
# Диагностика: коллапс 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
|
||
- На ep0–1, когда 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), без освобождения места дальнейшее обучение и сохранение чекпойнтов невозможно.
|