11 KiB
Диагностика: коллапс 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 (приоритет)
- Несколько эпох mutex-only без MoCo и без hard_mining до R@1 хотя бы ~0.2 на train.
- Конкретно:
sampler_type="mutex",loss.use_memory_bank=False,loss.hard_mining_k=0.
- Конкретно:
- Затем включить MoCo queue с warm-up: либо momentum encoder, либо queue pre-fill 1 эпоху со
stop_gradна queue updates. - DSS включать только после того, как embeddings стали discriminative (R@1 на train > 0.2). Иначе «similar» = «random».
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), без освобождения места дальнейшее обучение и сохранение чекпойнтов невозможно.