- scripts/make_split.py: merges cross-area train+test (33,708 pairs), shuffles with seed=42, splits 80/20 - meta/train_80.json (26,966) + meta/test_20.json (6,742) - After seg filter: 24,891 train / 6,252 test - Default paths in train_gtauav.py updated to use new split Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
18 KiB
Caption Quality Test for Cross-View Geo-Localization
Архитектура системы (v3, 2026-04-21) — GTA-UAV эксперимент
QUERY BRANCH (drone + L1/L2/L3 captions):
drone_img --> DINOv3 ViT-L/16 LVD-1689M (frozen) --> CLS [B,1024]
|
proj_drone (1024->512)
|
L1 (overview) --> LRSCLIP (248 tok) --> z_L1 [768] --\
L2 (full desc) --> LRSCLIP (248 tok) --> z_L2 [768] ---+-- concat [B,2304]
L3 (fingerprint) --> LRSCLIP (248 tok) --> z_L3 [768] --/ |
MLP(2304->768->512)
|
GatedFusion(drone_512, text_512) --> L2-norm --> query [B,512]
GALLERY BRANCH (satellite only):
sat_img --> DINOv3 ViT-L/16 SAT-493M (frozen) --> CLS [B,1024]
|
proj_sat (1024->512) --> L2-norm --> gallery [B,512]
LOSS: InfoNCE(query, gallery) — symmetric, asymmetric weights (0.6 q->g, 0.4 g->q)
BASELINE: gate = 1.0 (text ignored)
Trainable parameters: 10.9M из 733M (1.49%)
- proj_drone: 1024x512 = ~524K
- proj_sat: 1024x512 = ~524K
- TextFusionMLP: 2304->768->512 = ~2.2M
- gate alpha: 1 scalar
- LRSCLIP partial unfreeze (last block + ln_final + text_projection): ~7.6M
- Backbones DINOv3 x2 (303M each): frozen
Image input: 256x256
DINOv3 ViT-L/16 с patch_size=16 → 16x16=256 patches на 256x256. 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 + LRSCLIP) — DONE
| Файл | Назначение |
|---|---|
src/models/asymmetric_encoder.py |
DINOv3ViT + LRSCLIPTextEncoder + TextFusionMLP + AsymmetricEncoder + GatedFusion |
src/datasets/gtauav_dataset.py |
GTA-UAV-LR loader + L1/L2/L3 caption parsing из VLM JSON |
src/training/train_gtauav.py |
Training loop с eval, AMP, CLI args (--baseline, --filter-meta) |
scripts/filter_segmentation.py |
Scan segm masks, output meta JSON (exclude >=90% bg+water) |
Backbones (v3)
DINOv3 ViT-L/16 — Drone (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
- Status: frozen
DINOv3 ViT-L/16 — Satellite (sat pretrained)
- Checkpoint:
nn_models/DINO_SAT/model.safetensors - HuggingFace:
facebook/dinov3-vitl16-pretrain-sat493m - Arch: идентична DINO_WEB (ViT-L/16, hidden=1024, 303M params)
- Input: 256x256
- Config:
nn_models/DINO_SAT/config.json— BROKEN (auth error), используем конфиг от DINO_WEB - Status: frozen
DGTRS-CLIP ViT-L-14 (LRSCLIP) — Text encoder
- Checkpoint:
nn_models/LRSCLIP/DGTRS-CLIP-ViT-L-14.pt - Text dim: 768, max tokens: 248 (KPS stretched from CLIP's 77)
- Содержит: полную CLIP модель (visual + text), используем только text encoder
- GitHub:
github.com/MitsuiChen14/DGTRS - Status: partial unfreeze (last block + text_projection)
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_featalpha— один learnable scalar в logit-spaceinit_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: InfoNCE с learnable temperature (CLIP logit_scale), init=0.07, clamp [0.01, 0.5]
- 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
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)
# 1. Filter segmentation (exclude 90%+ background/water)
python -m scripts.filter_segmentation --output meta/seg_filter.json
# 2. Baseline (no text)
python -m src.training.train_gtauav --baseline --filter-meta meta/seg_filter.json
# 3. With captions (L1/L2/L3)
python -m src.training.train_gtauav --filter-meta meta/seg_filter.json
# 4. 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
V2 (UAV-GeoLoc)
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 для drone->satellite и satellite->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: 2x DINOv3-L + LRSCLIP + batch 64 | ~18-22 GB |
| GPU mem (smoke test, batch 4) | 3.1 GB |
| Batch size | 64 (default) |
| Total params | 733M (10.9M trainable, 1.49%) |
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)
Запуск
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
- Resize drone -> 512x512 (JPEG, quality=95)
- Stitch satellite tiles для маршрута 09 (4 тайла -> 44800x33280)
- Нарезка satellite -> кропы 512x512, stride 256, сохранение без downscale (PNG)
- GPS для каждого кропа из bbox карты + позиция в grid
- Match drone->crop через vectorized haversine (positive = ближайший, semi-positive = +/-1 в grid)
- Metadata:
positive.json,semi_positive.json,db_postion.txt(per route) - 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)
train_db.txt/test_db.txtсодержали только matched кропы -> теперь все ~74K (полная gallery)db_position.txt->db_postion.txt(совместимость с UAV-GeoLoc), добавлены scale_lon/scale_lat, tab-separatorpositive.jsonключи были filename (01_0001.JPG) -> теперь frame_id (0001)- Semi-positive поиск O(n) -> O(1) через dict lookup по (x,y) grid
- Удален мёртвый код (
haversine_m,defaultdictimport)
Известные ограничения
- Нет val split (только train/test, как в оригинальном UAV-VisLoc)
- 6 drone в маршруте 06 (06_0093-06_0098) за пределами спутниковой карты (distance >1000m)
- Большие спутниковые карты загружаются целиком в RAM при генерации (до 8.7 GB для route 09)