- _evaluate: compute R@K + AP for both directions (q2g and g2q) via inverted ground truth; g2q denominator counts only sat-tiles with at least one positive drone in the (sub)sampled query set. Surfaces in train.csv, val.csv, train_recall.csv, W&B summary, and final log. - conf/gtauav_balanced_asym.gin: asymmetric WEB+SAT encoders, MONA in all 24 ViT blocks (~17.6M trainable / ~733M total). - conf/gtauav_baseline_asym.gin: same architecture, baseline_mode=True for Δ R@1 against balanced_asym. - CLAUDE.md / README.md: document new configs, clarify that g2q is now computed (was claimed but missing). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
448 lines
26 KiB
Markdown
448 lines
26 KiB
Markdown
# Caption Quality Test for Cross-View Geo-Localization
|
||
|
||
## Архитектура системы (v3, 2026-04-24) — GTA-UAV эксперимент
|
||
|
||
```
|
||
Shared DINOv3 ViT-L/16 (LVD-1689M, frozen + MONA in last 12/24 blocks)
|
||
для обеих веток — drone и satellite кодируются одним encoder.
|
||
|
||
QUERY BRANCH (drone + L1/L2/L3 captions):
|
||
drone_img [B,3,256,256] --> DINOv3 ViT-L/16 (shared) --> d_img [B,1024]
|
||
|
|
||
L1 --> DGTRS-CLIP (248 tok) --> z₁ [768] --\ |
|
||
L2 --> DGTRS-CLIP (248 tok) --> z₂ [768] ---+-- cat --> MLP(2304→1024→1024) --> d_txt [B,1024]
|
||
L3 --> DGTRS-CLIP (248 tok) --> z₃ [768] --/ |
|
||
|
|
||
q = σ(α_q)·d_img + (1−σ(α_q))·d_txt GatedFusion_q
|
||
|
|
||
q̂ = q/‖q‖₂ --> query [B,1024]
|
||
|
||
GALLERY BRANCH (satellite + satellite captions):
|
||
sat_img [B,3,256,256] --> DINOv3 ViT-L/16 (shared, same weights) --> s_img [B,1024]
|
||
|
|
||
sat_L1 --> DGTRS-CLIP --> z₁ --\ |
|
||
sat_L2 --> DGTRS-CLIP --> z₂ ---+-- cat --> MLP (shared) --> s_txt [B,1024]
|
||
sat_L3 --> DGTRS-CLIP --> z₃ --/ |
|
||
|
|
||
g = σ(α_g)·s_img + (1−σ(α_g))·s_txt GatedFusion_g
|
||
|
|
||
ĝ = g/‖g‖₂ --> gallery [B,1024]
|
||
|
||
Retrieval space: 1024-dim (DINOv3 native, без projection layers)
|
||
TextFusionMLP shared между query и gallery (одинаковый формат captions)
|
||
Для sat images без captions: s_txt=None → g = s_img (gate passthrough)
|
||
|
||
LOSS: L = 0.6·CE(q̂·ĝᵀ/τ, targets) + 0.4·CE(ĝ·q̂ᵀ/τ, targets)
|
||
τ = 1/exp(logit_scale), learnable, clamped [0.01, 0.1], init=0.07
|
||
label_smoothing=0.1
|
||
|
||
BATCH SAMPLING: MutuallyExclusiveSampler — в одном батче нет двух drone'ов
|
||
с пересекающимися sat_candidates (исключает false negatives, которые
|
||
иначе появляются из-за multi-positive структуры GTA-UAV).
|
||
|
||
BASELINE: σ(α_q)=σ(α_g)=1.0, text disabled, DGTRS not loaded
|
||
```
|
||
|
||
### Text hierarchy (L1/L2/L3)
|
||
- **L1 overview:** первое предложение P1 — краткое описание land-cover (15-30 tok)
|
||
- **L2 full:** полные P1 + P2 — inventory + spatial layout (100-200 tok)
|
||
- **L3 fingerprint:** P3 — уникальные landmarks для matching (20-50 tok)
|
||
- **Fusion:** z_text = MLP([z₁; z₂; z₃]) — concat 3×768 → Linear(2304,1024) → GELU → Linear(1024,1024)
|
||
- **Shared MLP** между query и gallery ветками (одинаковый формат captions)
|
||
- **Satellite captions:** 6,546 из 14,640 sat images имеют captions. Для остальных gate passthrough (g = s_img) — **per-sample mask** в `_fuse_with_mask` возвращает чистые image features для samples без caption (без шума от пустых строк)
|
||
|
||
### Text encoder: DGTRS-CLIP (official architecture)
|
||
- Код: `src/models/dgtrs/` — из github.com/MitsuiChen14/DGTRS (Apache-2.0)
|
||
- KPS positional embedding: mask1 (pos 0-19, frozen) + mask2 (pos 20-247, trainable)
|
||
- Transformer: sequence-first (LND), nn.MultiheadAttention, 12 layers
|
||
- Tokenizer: BPE SimpleTokenizer (248 tokens, vocab 49408)
|
||
|
||
### Trainable parameters: 7.06M из 434M (1.63%)
|
||
- **MONA adapters** (shared DINOv3): 3.5M (2 per block × 12 last blocks, bottleneck=64)
|
||
- **LoRA** (DGTRS-CLIP): 147K (Q+V, rank=4, 12 blocks)
|
||
- TextFusionMLP (shared): Linear(2304,1024)+GELU+Linear(1024,1024) = ~3.4M
|
||
- gate α_q + α_g: 2 scalars
|
||
- logit_scale: 1 scalar (learnable temperature)
|
||
- DINOv3 (1 encoder) + DGTRS: frozen backbone weights
|
||
- **Без projection layers** — retrieval space = DINOv3 native 1024-dim
|
||
- **AMP:** frozen layers fp16, adapters + loss fp32
|
||
- **Примечание:** ранее была asymmetric setup (2×DINOv3 WEB+SAT, MONA во всех 24 блоках) с 17.6M trainable / 733M total. Упростили до shared + last-12 MONA.
|
||
|
||
### Optimizer & Scheduler
|
||
- **AdamW** с per-group LR: projections lr=1e-4, text encoder lr=1e-5
|
||
- **Linear warmup** (2 epochs) + **cosine annealing** (per-step)
|
||
- **Gradient clipping:** max_norm=1.0
|
||
- **AMP:** fp16 для model forward, fp32 для loss (learnable temperature overflow fix)
|
||
|
||
### Image input: 256x256
|
||
DINOv3 ViT-L/16 с patch_size=16 → 16x16=256 patches на 256x256.
|
||
Train: augmentations (drone: crop+flip+rot+jitter+blur, sat: crop+flip+jitter).
|
||
Eval: Resize(256) + CenterCrop(256) + ImageNet normalization.
|
||
|
||
### Предыдущая архитектура (v2) — UAV-GeoLoc эксперимент
|
||
|
||
Использовала GeoRSCLIP ViT-B/32 (512-dim) для обеих веток + template captions.
|
||
Код в `src/models/dual_encoder.py`, `src/datasets/visloc_with_captions.py`.
|
||
|
||
## Ключевые файлы
|
||
|
||
### V2 (UAV-GeoLoc, GeoRSCLIP)
|
||
| Файл | Назначение |
|
||
|------|-----------|
|
||
| `src/models/dual_encoder.py` | GeoRSCLIP + GatedFusion + projection heads |
|
||
| `src/losses/multi_infonce.py` | InfoNCE с cosine temperature schedule |
|
||
| `src/datasets/visloc_with_captions.py` | UAV-GeoLoc loader + template captions из path metadata |
|
||
| `src/training/train.py` | Training loop, логирование loss/gate/tau |
|
||
| `src/eval/evaluate.py` | R@K metrics, delta_r_at_1 |
|
||
| `scripts/compare_runs.py` | Markdown/JSON сравнение baseline vs caption runs |
|
||
| `scripts/generate_captions.py` | Offline caption generation (template/VLM/hybrid) |
|
||
|
||
### V3 (GTA-UAV, DINOv3 + DGTRS-CLIP) — DONE
|
||
| Файл | Назначение |
|
||
|------|-----------|
|
||
| `src/models/dgtrs/model.py` | Официальная архитектура DGTRS-CLIP text encoder (Apache-2.0) |
|
||
| `src/models/dgtrs/simple_tokenizer.py` | BPE tokenizer (248 tokens, vocab 49408) |
|
||
| `src/models/asymmetric_encoder.py` | DINOv3ViT + TextFusionMLP + AsymmetricEncoder + GatedFusion + encode_query/encode_gallery (per-sample caption mask) |
|
||
| `src/datasets/gtauav_dataset.py` | GTA-UAV-LR loader + L1/L2/L3 captions + GTAUAVSatGallery/GTAUAVDroneQuery (full retrieval eval) |
|
||
| `src/datasets/mutually_exclusive_sampler.py` | BatchSampler: drone'ы в батче не делят sat_candidates (no false negatives) |
|
||
| `src/datasets/dynamic_similarity_sampler.py` | DSS: embedding-kNN + mutex — батчи из визуально похожих drone'ов (GPU/CPU, опциональный LSH) |
|
||
| `src/datasets/lsh_index.py` | Random-projection cosine-LSH для approximate kNN (opt-in; `dss_use_lsh=True`) |
|
||
| `src/datasets/embedding_cache.py` | Дисковый кеш для drone embeddings — skip re-embed на resume |
|
||
| `src/losses/multi_infonce.py` | **Primary:** SymmetricInfoNCE + MoCo queue, learnable τ clamp [0.01, 0.1], weights q2g=0.6 g2q=0.4, `hard_mining_k` для top-K hardest negatives |
|
||
| `src/losses/weighted_infonce.py` | Alternative: per-sample adaptive label smoothing (активируется `loss_type="weighted"`) |
|
||
| `src/losses/hard_negatives.py` | NegativeMemoryBank (MoCo-style FIFO queue 4096 × 1024) |
|
||
| `src/training/train_gtauav.py` | Training loop: full-gallery `_evaluate`, mutex sampler wiring, loss_type switch |
|
||
| `scripts/smoke_eval.py` / `scripts/smoke_train.py` | Регрессионные smoke-тесты для eval и train pipeline |
|
||
| `src/training/trackers.py` | Unified experiment tracker: W&B + TensorBoard + CSV |
|
||
| `src/training/grad_monitor.py` | Gradient norm monitoring per param group |
|
||
| `src/training/gradcam.py` | Grad-CAM visualization для DINOv3 encoders |
|
||
| `src/training/profiling.py` | PyTorch Profiler wrapper + torchinfo model summary |
|
||
| `src/training/plot_metrics.py` | Seaborn/matplotlib plots (каждую эпоху) |
|
||
| `conf/gtauav_balanced.gin` | Shared encoder, MONA 12/24, with text, gate=0.7, 10 epochs |
|
||
| `conf/gtauav_baseline.gin` | Shared encoder, MONA 12/24, no text, gate=1.0 |
|
||
| `conf/gtauav_balanced_asym.gin` | Asymmetric (WEB+SAT), MONA 24/24, with text — overrides gtauav_balanced.gin |
|
||
| `conf/gtauav_baseline_asym.gin` | Asymmetric (WEB+SAT), MONA 24/24, no text |
|
||
| `conf/gtauav_text_heavy.gin` | Text-heavy, gate=0.3 |
|
||
| `conf/gtauav_image_heavy.gin` | Image-heavy, gate=0.9 |
|
||
| `scripts/make_split.py` | 80/20 random split из всех пар |
|
||
| `scripts/filter_segmentation.py` | Scan segm masks, output meta JSON (exclude >=90% bg+water) |
|
||
|
||
## Backbones (v3)
|
||
|
||
### DINOv3 ViT-L/16 — Shared (web pretrained)
|
||
- **Checkpoint:** `nn_models/DINO_WEB/dinov3-vitl16-pretrain-lvd1689m.pth`
|
||
- **Arch:** ViT-L/16, 24 layers, 16 heads, hidden=1024, MLP=4096, 303M params
|
||
- **Input:** 256x256, ImageNet normalization, patch=16 → 256 patches
|
||
- **Register tokens:** 4, RoPE theta=100.0
|
||
- **MONA:** 24 адаптера в последних 12 блоках (blocks 12-23), bottleneck=64, 3.5M trainable
|
||
- **Status:** frozen кроме MONA
|
||
- **Примечание:** ранее asymmetric — использовался отдельно `nn_models/DINO_SAT/model.safetensors` (sat493m pretrain) для satellite ветки. Упростили до shared WEB-энкодера.
|
||
|
||
### DGTRS-CLIP ViT-L-14 (LRSCLIP) — Text encoder
|
||
- **Checkpoint:** `nn_models/LRSCLIP/DGTRS-CLIP-ViT-L-14.pt`
|
||
- **Код:** `src/models/dgtrs/` — официальная архитектура из github.com/MitsuiChen14/DGTRS
|
||
- **Text dim:** 768, max tokens: 248 (KPS: mask1 pos 0-19 frozen + mask2 pos 20-247 trainable)
|
||
- **Transformer:** 12 layers, 12 heads, sequence-first (LND), QuickGELU
|
||
- **Tokenizer:** BPE SimpleTokenizer (vocab 49408), 248 token context
|
||
- **Содержит:** полную CLIP модель (visual + text), используем только text encoder (124M params)
|
||
- **Status:** partial unfreeze (last resblock + ln_final + text_projection, ~7.6M trainable)
|
||
|
||
### GeoRSCLIP ViT-B/32 (v2, legacy)
|
||
- **Checkpoint:** `checkpoints/RS5M_ViT-B-32.pt`
|
||
- **Image encoder:** ViT-B/32, 224x224, 512-dim, ~86M params — frozen
|
||
- **Text encoder:** 77 tokens, 512-dim — partial unfreeze
|
||
|
||
## GatedFusion
|
||
|
||
- `query = sigma(alpha) * drone_feat + (1 - sigma(alpha)) * text_feat`
|
||
- `alpha` — один learnable scalar в logit-space
|
||
- `init_gate = 0.7` → начальный вес image = 70%, text = 30%
|
||
- `baseline_mode = True` → gate = 1.0, text полностью игнорируется
|
||
- Gate value логируется каждую эпоху для интерпретации вклада текста
|
||
|
||
## Text Hierarchy (L1/L2/L3)
|
||
|
||
Три уровня описаний из VLM-generated captions:
|
||
|
||
| Уровень | Контент | Длина | Источник |
|
||
|---|---|---|---|
|
||
| L1 overview | Краткое описание сцены | <=30 tok | Конденсация P1 |
|
||
| L2 full description | Детальное описание через Qwen3-VL | <=200 tok | Полный P1+P2 |
|
||
| L3 fingerprint | Ключевые landmark'ы | <=30 tok | Конденсация P3 |
|
||
|
||
Все три уровня кодируются одним LRSCLIP (248 tok max).
|
||
Альтернатива (Stage 2): RemoteCLIP для L1/L3 + LRSCLIP для L2.
|
||
|
||
## Датасет: GTA-UAV-LR
|
||
|
||
- **RGB:** `/home/servml/Документы/datasets/GTA-UAV-LR/`
|
||
- Drone: 33,763 PNG (512x384), altitudes 100-600m
|
||
- Satellite: 14,640 PNG (256x256 RGBA)
|
||
- Pairs: `cross-area-drone2sate-{train,test}.json` (primary split)
|
||
- Metadata: `*_drone_meta.csv` (height, yaw, roll, pitch)
|
||
- Origin: GTA V simulation (Los Santos)
|
||
|
||
- **Captions:** `/home/servml/Документы/datasets/GTA-UAV-LR-captions/`
|
||
- Drone: 33,411 JSON (32,635 multi-paragraph P1/P2/P3 + 776 short water-only)
|
||
- Satellite: 6,546 JSON (все multi-paragraph)
|
||
- Формат: 3 абзаца (P1 Inventory + P2 Spatial + P3 Fingerprint)
|
||
- Token counts: ~430 output tokens per caption
|
||
|
||
- **Segmentation:** `/home/servml/Документы/datasets/GTA-UAV-LR-aug/`
|
||
- 48,403 images, 17 классов (background, building, road, vegetation, water, ...)
|
||
- Modalities: segm/, depth/, edge/, chm/, safetensors/
|
||
- Query: 512x512, DB: 256x256
|
||
|
||
### Фильтрация сегментации
|
||
Meta-файл `meta/seg_filter.json`: исключение изображений с >=90% background(class 0) + water(class 4).
|
||
- **Total:** 48,403 → **Passed:** 37,498 (77.5%) / **Excluded:** 10,905 (22.5%)
|
||
- Drone: 31,188 passed / 2,575 excluded
|
||
- Satellite: 6,310 passed / 8,330 excluded (преимущественно open water tiles)
|
||
|
||
## Датасет: UAV-GeoLoc (v2, legacy)
|
||
|
||
- **Путь:** `/mnt/data1tb/cvgl_datasets/UAV-GeoLoc/`
|
||
- **Train:** 206,108 queries, 94,709 DB crops (140 scenes, Terrain split)
|
||
- **Val:** 62,368 queries, 26,597 DB crops (40 scenes)
|
||
- **Test:** 33,472 queries, 11,684 DB crops (20 scenes)
|
||
- **Index:** `Index/train_query.txt` — `query_path 0 pos_crop1 pos_crop2 ...`
|
||
|
||
## Конфигурации
|
||
|
||
### V3 (GTA-UAV)
|
||
Параметры:
|
||
- 10 epochs, batch 64, AMP, image 256x256
|
||
- **Optimizer:** AdamW, per-group LR: proj=1e-4, text=1e-5 (10x lower)
|
||
- **Scheduler:** linear warmup (2 epochs) + cosine annealing (per-step)
|
||
- **Loss:** SymmetricInfoNCE (q2g=0.6, g2q=0.4) с learnable τ (init=0.07, clamp [0.01, 0.1])
|
||
- **Hard mining:** top-K=512 hardest negatives per query из MoCo queue (размер 4096); `hard_mining_k=0` отключает
|
||
- **Batch sampler:** `sampler_type="dss"` (default) — DynamicSimilaritySampler с re-embedding каждую эпоху: пакует визуально похожих drone'ов в один батч (+hardness) с mutex-constraint (no false negatives). Первая эпоха warmup mutex-only. Средний in-batch cosine sim ~0.71 vs 0.26 у mutex. kNN на GPU (`dss_knn_device="cuda"`) — 1.6s vs 17s на CPU. Опциональный LSH (`dss_use_lsh=True`) для scale 100K+. Embedding cache (`dss_cache_dir`) — skip re-embed на resume.
|
||
- **Eval:** full satellite gallery (~2684 unique tiles для test_20) с multi-match R@K (учитывает все positive/semi-positive)
|
||
- **Augmentations:**
|
||
- Drone: RandomResizedCrop(0.7-1.0), HFlip, Rotation(15°), ColorJitter, Grayscale(5%), GaussianBlur
|
||
- Satellite: RandomResizedCrop(0.7-1.0), HFlip, ColorJitter, Grayscale(5%)
|
||
- Eval: Resize+CenterCrop (clean, no augmentation)
|
||
- **Split:** 80/20 random из всех 33,708 пар (`meta/train_80.json` / `meta/test_20.json`)
|
||
- Train: 26,966 → 24,891 after seg filter
|
||
- Test: 6,742 → 6,252 after seg filter
|
||
- Скрипт: `python -m scripts.make_split --ratio 0.8 --seed 42`
|
||
|
||
### V3 (GTA-UAV, gin)
|
||
| Конфиг | Gate init | Описание |
|
||
|--------|-----------|----------|
|
||
| `conf/gtauav_balanced.gin` | 0.7 (30% text) | **Primary test** — shared DINOv3 WEB, MONA 12/24 |
|
||
| `conf/gtauav_baseline.gin` | 1.0 (no text) | Reference baseline (shared, MONA 12/24) |
|
||
| `conf/gtauav_balanced_asym.gin` | 0.7 (30% text) | Asymmetric (WEB+SAT), MONA 24/24 — full-capacity variant |
|
||
| `conf/gtauav_baseline_asym.gin` | 1.0 (no text) | Asymmetric baseline for Δ R@1 vs balanced_asym |
|
||
| `conf/gtauav_text_heavy.gin` | 0.3 (70% text) | Stress test |
|
||
| `conf/gtauav_image_heavy.gin` | 0.9 (10% text) | Image-dominant |
|
||
|
||
### V2 (UAV-GeoLoc, gin)
|
||
| Конфиг | Gate init | Описание |
|
||
|--------|-----------|----------|
|
||
| `conf/balanced.gin` | 0.7 (30% text) | **Primary test** |
|
||
| `conf/baseline_no_text.gin` | 1.0 (no text) | Reference baseline |
|
||
| `conf/text_heavy.gin` | 0.3 (70% text) | Stress test |
|
||
|
||
## Запуск
|
||
|
||
### V3 (GTA-UAV)
|
||
```bash
|
||
# 1. Filter segmentation (exclude 90%+ background/water)
|
||
python -m scripts.filter_segmentation --output meta/seg_filter.json
|
||
|
||
# 2. Train with gin config (recommended)
|
||
python -m src.training.train_gtauav --config conf/gtauav_balanced.gin \
|
||
--filter-meta meta/seg_filter.json
|
||
|
||
# 3. Baseline (no text)
|
||
python -m src.training.train_gtauav --config conf/gtauav_baseline.gin \
|
||
--filter-meta meta/seg_filter.json
|
||
|
||
# 4. With diagnostics (W&B + Grad-CAM + Profiler)
|
||
python -m src.training.train_gtauav --config conf/gtauav_balanced.gin \
|
||
--filter-meta meta/seg_filter.json --wandb --gradcam --profile
|
||
|
||
# 5. CLI overrides (gin params take priority)
|
||
python -m src.training.train_gtauav --config conf/gtauav_balanced.gin \
|
||
--filter-meta meta/seg_filter.json \
|
||
--gin-param 'TrainConfigGTAUAV.batch_size=16'
|
||
|
||
# 6. Compare
|
||
python -m scripts.compare_runs \
|
||
--baseline_report out/gtauav/baseline/eval_report.json \
|
||
--full_report out/gtauav/with_text/eval_report.json \
|
||
--output out/gtauav/comparison.md
|
||
|
||
# 7. TensorBoard
|
||
tensorboard --logdir out/gtauav/with_text/tb_logs
|
||
```
|
||
|
||
### V2 (UAV-GeoLoc)
|
||
```bash
|
||
python -m src.training.train --config conf/baseline_no_text.gin
|
||
python -m src.training.train --config conf/balanced.gin
|
||
python -m scripts.compare_runs \
|
||
--baseline_report out/caption_test/baseline_no_text/eval_report.json \
|
||
--full_report out/caption_test/balanced/eval_report.json \
|
||
--output out/caption_test/comparison.md
|
||
```
|
||
|
||
## Метрики и Decision rule
|
||
|
||
**Primary metric:** Delta R@1 (drone -> satellite)
|
||
|
||
| Delta R@1 | Verdict |
|
||
|-----------|---------|
|
||
| >= +3% | PASS — captions informative, proceed to production |
|
||
| +1% to +3% | MARGINAL — add VLM refinement, re-run |
|
||
| 0 to +1% | WEAK — redesign caption pipeline |
|
||
| < 0 | HARMFUL — critical bug |
|
||
|
||
**Eval metrics:** R@1, R@5, R@10 + AP (MRR) для обоих направлений: q2g (drone→satellite) и g2q (satellite→drone). g2q считается через инвертированный GT (для каждого sat-tile собираются drone-индексы из `valid_idx_per_query`); знаменатель — sat-tiles, у которых есть хотя бы один positive drone в (под)выборке.
|
||
**Splits (GTA-UAV):** cross-area (primary, harder) и same-area (sanity check)
|
||
**Logged per epoch:** loss, temperature (tau), gate value (sigma(alpha)), lr
|
||
|
||
## Бюджет времени (RTX 4090, 24 GB)
|
||
|
||
### V3 (GTA-UAV, DINOv3 ViT-L/16, 256x256)
|
||
| Фаза | Оценка |
|
||
|------|--------|
|
||
| VRAM: DINOv3-L (shared) + LRSCLIP + batch 64 | ~10-14 GB (было ~18-22 с 2× DINOv3) |
|
||
| GPU mem (smoke test, batch 4) | 3.1 GB |
|
||
| Batch size | 64 (default) |
|
||
| Total params | 434M (7.06M trainable, 1.63%) — shared encoder + MONA в last 12/24 blocks |
|
||
|
||
### V2 (UAV-GeoLoc, GeoRSCLIP)
|
||
| Фаза | Время |
|
||
|------|-------|
|
||
| Один training run (10 epochs, 206K queries, batch 128) | ~15-30 мин |
|
||
| Full test (3 варианта) | ~1-1.5 ч |
|
||
| Evaluation per run | ~2-5 мин |
|
||
|
||
## Связанные проекты
|
||
|
||
### Text Annotation Pipeline
|
||
- **Путь:** `/home/servml/Документы/pikaliov_obsidian/(Полякова ВЕ_Система для генерации текстовых описаний для БПЛА)/2_work/2_text_annotation/code/`
|
||
- **VLM:** Qwen3-VL-8B AWQ (1.68 s/img)
|
||
- **Scoring:** SigLIP 2 (drone P3) + CLIP-RSICD (satellite P1+P2)
|
||
- **Формат описаний:** 3 абзаца (P1 Inventory + P2 Spatial Map + P3 Fingerprint)
|
||
- **Метрики:** FDR, FNR, NumAcc, LLaVA-Critic C1-C6
|
||
|
||
### UAV-VisLoc Prepare
|
||
- **Путь:** `/home/servml/Документы/code/Yaroslav/UAV-VisLoc-prepare/scripts/prepare_dataset.py`
|
||
- **Статус:** выполнен (2026-04-17), данные в `/home/servml/Документы/datasets/UAV_VisLoc_processed/` (25 GB)
|
||
- **Задача:** нарезка satellite кропов 512x512, stride 256 + resize drone -> 512x512
|
||
- **Подробности:** см. ниже
|
||
|
||
---
|
||
|
||
## Downstream: NADEZHDA Teacher (DINOv3 + Multi-FiLM)
|
||
|
||
caption-test — первый этап валидации. При Δ R@1 >= +3% переход к полному teacher'у:
|
||
|
||
```
|
||
Этап 1 (caption-test): GeoRSCLIP + GatedFusion(text) → валидация текста
|
||
Этап 2 (teacher): DINOv3-L + Multi-FiLM(depth, seg, CHM, normals, text)
|
||
Этап 3 (distillation): teacher ~300M → student ~5M → Jetson Orin NX
|
||
```
|
||
|
||
### Auxiliary modalities (предвычисляются из 512x512 офлайн)
|
||
|
||
| Модальность | Модель | Формат для teacher | Каналы |
|
||
|---|---|---|---|
|
||
| Depth | DepthAnything V2 | continuous, log(1+d) | 1 |
|
||
| Normals | Sobel от depth | continuous | 3 |
|
||
| Segmentation | SegFormer-B5 | binary per-class masks (top-K) | 16-17 |
|
||
| Canopy Height | Meta HRCH | binary bins (1-5m, 5-15m, >15m) + occupancy | 4-5 |
|
||
| Text | Qwen3-VL-8B / MobileCLIP2 | embedding | - |
|
||
|
||
### Асимметрия sat/drone
|
||
- CHM: только satellite (модель обучена на nadir, на oblique drone не работает)
|
||
- Satellite: ~27 aux каналов, Drone: ~21 aux каналов
|
||
|
||
### Fusion: Multi-FiLM
|
||
```
|
||
aux_features → FiLM(γ, β) → γ * DINOv3_tokens + β
|
||
```
|
||
Binary masks — natural FiLM gates. Modality dropout: text 0.3, CHM 0.5, seg 0.15, depth 0.1.
|
||
|
||
### Планируемый эксперимент H5
|
||
Сравнение T_bin (binary masks) vs T_pyr (native feature pyramids) vs T_hybrid.
|
||
Прогноз: binary masks лучше на cross-domain из-за робастности к aux-model artifacts.
|
||
|
||
---
|
||
|
||
## Датасеты (справочник)
|
||
|
||
### UAV-VisLoc
|
||
- **Путь:** `/home/servml/Документы/datasets/UAV_VisLoc_dataset/`
|
||
- **Структура:** 11 маршрутов (папки `01`-`11`), каждая содержит:
|
||
- `drone/` — drone-снимки (`XX_NNNN.JPG`)
|
||
- `satelliteXX.tif` — спутниковая карта
|
||
- `XX.csv` — GPS-метаданные drone (num, filename, date, lat, lon, height, Omega, Kappa, Phi1, Phi2)
|
||
- **Исключение:** маршрут `09` — спутник разбит на 4 тайла (`satellite09_01-01.tif` и т.д.)
|
||
- **Satellite coordinates:** `satellite_ coordinates_range.csv` — bbox каждой карты (LT_lat_map, LT_lon_map, RB_lat_map, RB_lon_map)
|
||
- **Splits:** `visloc_train.csv`, `visloc_test.csv` — списки drone-снимков (TSV, full absolute paths)
|
||
- **Размеры:** Drone 3976x2652 / 3000x2000 (6774 снимков), Satellite от 3000x170 до 43421x38408
|
||
- **GSD спутника:** ~0.30 м/px (единый zoom level Google Earth для всех карт). GSD по долготе варьируется 0.23-0.27 м/px из-за косинусного эффекта широты (40°N vs 25°N), но это не разная высота съёмки. Кроп 512x512 покрывает ~154x154 м везде.
|
||
|
||
### UAV-GeoLoc
|
||
- **Путь:** `/mnt/data1tb/cvgl_datasets/UAV-GeoLoc/`
|
||
- **Подмножества:** Country (171 scene), Terrain (200 scenes), Rot (1 scene)
|
||
- **Формат пар:** `positive.json`, `semi_positive.json`, `db_postion.txt`
|
||
- **Index:** `train_query.txt` — `query_path label pos_crop1 pos_crop2 ...`
|
||
- **Drone:** синтетика Google Earth Studio 3D, 512x512, FOV 30deg, heights 100/125/150m
|
||
- **Satellite:** кропы из merge.tif, доминирующий размер 200x200
|
||
|
||
## Скрипт подготовки UAV-VisLoc
|
||
|
||
- **Путь:** `/home/servml/Документы/code/Yaroslav/UAV-VisLoc-prepare/scripts/prepare_dataset.py`
|
||
- **Статус:** выполнен (2026-04-17)
|
||
|
||
### Запуск
|
||
```bash
|
||
python scripts/prepare_dataset.py \
|
||
--src /home/servml/Документы/datasets/UAV_VisLoc_dataset \
|
||
--dst /home/servml/Документы/datasets/UAV_VisLoc_processed \
|
||
--crop-size 512 --stride 256 --target-size 512
|
||
```
|
||
|
||
### Pipeline
|
||
1. Resize drone -> 512x512 (JPEG, quality=95)
|
||
2. Stitch satellite tiles для маршрута 09 (4 тайла -> 44800x33280)
|
||
3. Нарезка satellite -> кропы 512x512, stride 256, сохранение без downscale (PNG)
|
||
4. GPS для каждого кропа из bbox карты + позиция в grid
|
||
5. Match drone->crop через vectorized haversine (positive = ближайший, semi-positive = +/-1 в grid)
|
||
6. Metadata: `positive.json`, `semi_positive.json`, `db_postion.txt` (per route)
|
||
7. Index: `train_query.txt`, `test_query.txt`, `train_db.txt`, `test_db.txt`, `all_db.txt`
|
||
|
||
### Форматы выходных файлов (совместимость с UAV-GeoLoc)
|
||
| Файл | Формат |
|
||
|------|--------|
|
||
| `positive.json` | `{frame_id: [crop_name]}`, ключ = frame ID без route prefix (`"0001"`) |
|
||
| `semi_positive.json` | `{frame_id: [crop1, crop2, ...]}`, соседи +/-1 в grid |
|
||
| `db_postion.txt` | tab-separated: `name\tlon\tlat\tscale_lon\tscale_lat` |
|
||
| `train_query.txt` | `route/drone/file.JPG 0 route/DB/img/crop1.png ...` |
|
||
| `train_db.txt` / `test_db.txt` | все кропы всех маршрутов (gallery одинаковая, split по query) |
|
||
|
||
### Результаты (target-size 512)
|
||
- Drone: 6,744 images 512x512 (без маршрута 07: 30 excluded)
|
||
- Satellite кропов: 74,807 (512x512, без downscale)
|
||
- Размер на диске: 25 GB
|
||
- Median distance drone->crop: 25.9m, P99: 45.7m
|
||
- Память при генерации: до ~8.7 GB RAM (маршрут 09 stitched 44800x33280)
|
||
- Разрешение 512 для downstream задач (сегментация, depth, normals); resize до 256x256 в dataloader
|
||
|
||
### Ревью и исправления (2026-04-17)
|
||
1. `train_db.txt`/`test_db.txt` содержали только matched кропы -> теперь все ~74K (полная gallery)
|
||
2. `db_position.txt` -> `db_postion.txt` (совместимость с UAV-GeoLoc), добавлены scale_lon/scale_lat, tab-separator
|
||
3. `positive.json` ключи были filename (`01_0001.JPG`) -> теперь frame_id (`0001`)
|
||
4. Semi-positive поиск O(n) -> O(1) через dict lookup по (x,y) grid
|
||
5. Удален мёртвый код (`haversine_m`, `defaultdict` import)
|
||
|
||
### Известные ограничения
|
||
- Нет val split (только train/test, как в оригинальном UAV-VisLoc)
|
||
- 6 drone в маршруте 06 (06_0093-06_0098) за пределами спутниковой карты (distance >1000m)
|
||
- Большие спутниковые карты загружаются целиком в RAM при генерации (до 8.7 GB для route 09)
|