Architecture v2: - Query branch: drone + text -> GatedFusion -> proj -> query_emb - Gallery branch: satellite -> proj -> gallery_emb - Single InfoNCE loss (asymmetric 0.6/0.4) - GatedFusion: learnable gated addition (sigma(alpha)*img + (1-sigma(alpha))*text) - Baseline mode: gate=1.0 (text ignored) Dataset: - UAV-GeoLoc loader with template captions from path metadata - 27 terrain types with predefined features - Random positive crop sampling per epoch Configs: balanced (gate=0.7), baseline (no text), text_heavy (gate=0.3) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
11 KiB
11 KiB
Caption Quality Test for Cross-View Geo-Localization
Архитектура системы (v2, 2026-04-17)
QUERY BRANCH (drone + caption):
drone_img --> GeoRSCLIP ViT-B/32 (frozen) --> drone_feat [B,512]
caption --> GeoRSCLIP Text (partial unfreeze) --> text_feat [B,512]
|
GatedFusion: q = sigma(alpha)*drone + (1-sigma(alpha))*text
|
proj_query (Linear 512->512) --> L2-norm --> query [B,512]
GALLERY BRANCH (satellite only):
sat_img --> GeoRSCLIP ViT-B/32 (frozen) --> sat_feat [B,512]
|
proj_gallery (Linear 512->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: ~1.2M из ~151M (proj_query + proj_gallery + fusion alpha + text last_block)
Ключевые файлы
| Файл | Назначение |
|---|---|
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) |
Backbone: GeoRSCLIP ViT-B/32
- Checkpoint:
checkpoints/RS5M_ViT-B-32.pt(скачать с github.com/om-ai-lab/RS5M) - Image encoder: ViT-B/32, 224x224, 512-dim, ~86M params — frozen
- Text encoder: CLIP text transformer, 77 tokens, 512-dim — partial unfreeze (last_block + text_projection)
- Throughput: ~4000 img/s на RTX 4090 (AMP, batch 128)
- Выбран вместо SigLIP 2 (ViT-SO400M, 384px, ~400M): в 7-10x быстрее, domain-specific (обучен на 5M RS-изображений), больше batch = больше негативов в InfoNCE
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 логируется каждую эпоху для интерпретации вклада текста
Датасет: UAV-GeoLoc
- Путь:
/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)
- Структура:
Terrain/{type}/{scene}/query/height{N}_rot{M}/footage/{file}.jpeg - Index:
Index/train_query.txt—query_path 0 pos_crop1 pos_crop2 ...
Template captions (из path metadata)
Формат: "Aerial view at {height}m facing {heading} over {terrain} terrain near {scene}. Plan-view features: {features}."
Пример: "Aerial view at 100m facing northwest over volcanic terrain near KilaueaVolcano. Plan-view features: lava flows, crater edges, volcanic rock."
Metadata извлекается из пути:
Terrain/Volcano/KilaueaVolcano/query/height100_rot315/...→ terrain=Volcano, scene=KilaueaVolcano, height=100, heading=northwest- 27 terrain типов с predefined features (Volcano, Mountain, Hill, Desert, Plain, ...)
- Country subset: features = "buildings, roads, urban blocks, rooftops, intersections"
Конфигурации (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 |
Общие параметры: 10 epochs, batch 128, lr=1e-4, AMP, cosine LR schedule, eval every 2 epochs.
Запуск
# 1. Baseline (no text)
python -m src.training.train --config conf/baseline_no_text.gin
# 2. With captions (primary test)
python -m src.training.train --config conf/balanced.gin
# 3. Text-heavy (stress test)
python -m src.training.train --config conf/text_heavy.gin
# 4. Compare
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 (query -> gallery)
| 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 |
Logged per epoch: loss, temperature (tau), gate value (sigma(alpha)), lr Eval metrics: R@1, R@5, R@10 для query->gallery и gallery->query
Бюджет времени (RTX 4090, 24 GB)
| Фаза | Время |
|---|---|
| Один 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 - Статус: код готов, ещё не запускался
- Задача: нарезка satellite кропов 512x512, stride 256 для UAV-VisLoc dataset
- Подробности: см. ниже
Датасеты (справочник)
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 - Статус: код готов, ещё не запускался
Запуск
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 256
Pipeline
- Resize drone -> 256x256 (JPEG, quality=95)
- Stitch satellite tiles для маршрута 09 (4 тайла -> 44800x33280)
- Нарезка satellite -> кропы 512x512, stride 256, resize -> 256x256 (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) |
Ожидаемые объёмы
- Drone: 6744 (без маршрута 07: 30 images excluded)
- Satellite кропов: ~74,807
- Память: до ~4.5 GB RAM (маршрут 09 stitched 44800x33280)
Ревью и исправления (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)
- Большие спутниковые карты загружаются целиком в RAM (до 4.5 GB для route 09)