Commit Graph

47 Commits

Author SHA1 Message Date
pikaliov
66c83469ef Remove per-epoch train/val CSV files (redundant with cumulative CSVs)
epoch_000_train.csv and epoch_000_val.csv removed — all data is in
train.csv and val.csv which persist across resume.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 07:57:37 +03:00
pikaliov
1638b250a4 CSVLogger: deduplicate epochs on resume (replace, not append duplicates)
On resume, the same epoch may be re-run. Now log_train/log_val remove
existing entries for the same epoch before appending, preventing
duplicate rows in train.csv/val.csv.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 07:54:33 +03:00
pikaliov
f382325ac6 CSVLogger: load previous epochs on resume for continuous plots
On init, load existing train.csv/val.csv so that epoch-level metrics
and plots include the full training history after checkpoint resume.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 07:41:32 +03:00
pikaliov
a32cb8ac6c Fix Grad-CAM numpy() on tensor with grad (add detach)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 07:02:12 +03:00
pikaliov
b0e87c07ef Fix Grad-CAM crash in shared encoder mode
Access model.image_encoder instead of model.drone_encoder/sat_encoder
when shared_encoder=True. Caused AttributeError at eval epoch 4.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 00:36:06 +03:00
pikaliov
7cc068e18c Update README: full architecture refresh (shared DINOv3, MONA bf16 top-12, 512-dim)
- Rewrite architecture diagram: shared DINOv3 WEB, projection 1024→512,
  MONA bf16 last 12 blocks, gradient checkpointing
- Update VRAM table: bs=48 at 21.8 GB (was bs=24)
- Update model summary: 432M total, 5.6M trainable, retrieval dim=512
- Update workflow: bs=48, 30 epochs, eval every epoch
- Document bf16 vs fp16 NaN issue (gamma=1e-6 underflow)
- Document MONA vs LoRA rationale for CVGL
- Update optimizer section: gradient accumulation, mixed precision details

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 22:44:47 +03:00
pikaliov
e55734425c MONA bfloat16: safe low-precision (gamma=1e-6 needs bf16 exponent range)
- Switch MONA from fp32 to bfloat16 (same exponent range as fp32, no underflow)
- fp16 causes NaN: gamma=1e-6 falls into subnormal range (min normal ~6.1e-5)
- bf16 min normal ~1.2e-38, so 1e-6 is safe
- RTX 4090 supports bf16 natively
- Document bf16 vs fp16 vs fp32 comparison in README
- Update model summary: 3.5M MONA (last 12 blocks), 5.6M total trainable

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 22:13:35 +03:00
pikaliov
9200772bea Fix NaN loss: revert MONA to fp32, fix loss logging
- MONA fp16 causes NaN (gamma=1e-6 underflows in fp16 min subnormal ~6e-8)
- Revert MONA forward to fp32 with autocast(enabled=False), cast output back
- Fix loss CSV: save raw_loss before backward() (tensor consumed after backward)
- Verified: loss=3.78, no NaN, bs=48 peak=21.4 GB

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 22:09:49 +03:00
pikaliov
916854f124 MONA only on last 12 blocks (skip early low-level blocks)
- Add last_n_blocks parameter to inject_mona_into_dinov3 (default=12)
- Blocks 0-11: pure frozen DINOv3 (low-level features, domain-agnostic)
- Blocks 12-23: MONA adapted (high-level semantic features)
- MONA params: 3.5M (was 6.85M, -49%)
- Total trainable: ~5.7M with text (was ~9.0M)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 21:51:52 +03:00
pikaliov
4a0336e0ff Update README: MONA vs LoRA rationale, projection 1024→512, model summary
- Document why MONA (spatial inductive bias) over LoRA for DINOv3
- Add MONA vs LoRA comparison table for CVGL
- Document projection head (1024→512) and retrieval space change
- Update model summary: 436M total, 9.0M trainable (2.06%), dim=512
- Note MONA fp16, gradient checkpointing, shared encoder

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 21:50:47 +03:00
pikaliov
471bc6444a Restore conv7x7 in MONA (full 3x3+5x5+7x7 multi-scale)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 21:45:27 +03:00
pikaliov
11fc348ecd Add projection 1024→512, revert MONA bottleneck to 64
- Add image_projection: Linear(1024→512) after DINOv3 CLS output
- Retrieval space: 512-dim (was 1024-dim native DINOv3)
- TextFusionMLP: 3×768→512→512 (was →1024→1024)
- GatedFusion operates in 512-dim
- MONA bottleneck restored to 64 (works at native 1024 inside DINOv3)
- Trainable: 8.9M (projection 0.5M + MONA 6.8M + LoRA 0.1K + MLP 1.5M)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 21:44:10 +03:00
pikaliov
d279a6e745 Optimize MONA: fp16, remove conv7x7, bottleneck 64→32
- Remove forced fp32 cast in MONA forward (runs in AMP fp16 now)
- Remove conv7x7 from MonaOp (keep 3x3 + 5x5 only)
- Reduce default bottleneck from 64 to 32
- MONA params: 3.5M (was 7.0M, -50%)
- Total trainable: 7.0M (was 10.5M)
- Peak VRAM at bs=24: 18.6 GB (was 20.3 GB before fp16)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 21:40:23 +03:00
pikaliov
6b7bcae198 Add gradient checkpointing for DINOv3 and DGTRS-CLIP (bs 8→24)
- DINOv3: checkpoint each of 24 transformer blocks (recompute on backward)
- DGTRS-CLIP: checkpoint each of 12 transformer blocks
- Enables batch_size=24 on RTX 4090 (was 8 without checkpointing)
- Peak VRAM: 20.3 GB at bs=24 (was OOM at bs=16 before)
- ~20-30% slower per step, but 3x more in-batch negatives (23 vs 7)
- Enabled by default (gradient_checkpointing=True in config)
- Update README with VRAM benchmarks and checkpointing docs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 21:34:31 +03:00
pikaliov
da2d2ea90e Switch to shared DINOv3 WEB encoder (saves ~4-5 GB VRAM)
- Single DINOv3 WEB for both drone and satellite branches (shared_encoder=True default)
- One set of MONA adapters instead of two: 7M trainable vs 14M
- Total params: 438M (was 748M), trainable: 10.6M (was 17.6M)
- Asymmetric mode still available via shared_encoder=False
- Add gradient accumulation (grad_accum_steps, --grad-accum CLI flag)
- Update model summary in README

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 21:25:46 +03:00
pikaliov
46b1208891 Add gradient accumulation support
- New config field grad_accum_steps (default=1, no change in behavior)
- Loss scaled by 1/accum, optimizer step every N micro-batches
- Scheduler counts optimizer steps (not micro-batches)
- CLI flag --grad-accum for override
- Document gradient accumulation and in-batch negatives in README

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 21:21:56 +03:00
pikaliov
b5c8015616 Add pair formation and in-batch negative sampling docs to README
Explain that dataset stores only positive drone-satellite pairs,
negatives are formed automatically via InfoNCE similarity matrix
within each batch (B-1 in-batch negatives per query).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 21:14:07 +03:00
pikaliov
8d8d556093 Update README architecture diagram with adapters, loss, and diagnostics pipeline
- Show MONA adapters (7M) and LoRA (147K) in branch diagrams
- Add retrieval/loss block with temperature and CE weights
- Add diagnostics pipeline block (per-batch CSV, Grad-CAM, profiler, grad norms)
- Add gtauav_image_heavy.gin to structure
- Split CSV row in diagnostics table into per-batch and per-epoch

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 20:58:07 +03:00
pikaliov
7b13a4c4db Write per-batch CSV immediately (append mode, no buffering)
train_batches.csv and epoch_N_batches.csv now update after every batch
instead of flushing at epoch end. Uses file append mode for efficiency.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 20:54:48 +03:00
pikaliov
69857e0ade Fix gin-config ambiguity: pin module name, remove redundant import
- Use gin.configurable(module=...) to prevent __main__ vs module name clash
- Remove `import src.training.train_gtauav` from gin files (already loaded)
- Use short selector names (TrainConfigGTAUAV) in all gin configs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 20:45:32 +03:00
pikaliov
29a09349e7 Add ML diagnostics tooling (W&B, TensorBoard, Grad-CAM, profiler) and gin configs
- Add unified experiment tracker (W&B + TensorBoard) with graceful fallback
- Add gradient norm monitoring per param group (MONA, LoRA, MLP, gates, tau)
- Add Grad-CAM visualization for DINOv3 drone/satellite encoders
- Add PyTorch Profiler wrapper + torchinfo model summary
- Add gin-config support to train_gtauav.py with CLI overrides
- Add v3 gin configs: gtauav_balanced, gtauav_baseline, gtauav_text_heavy, gtauav_image_heavy
- Generate metric plots every epoch (not just on eval)
- Set default epochs to 10
- Update README and CLAUDE.md with new tooling and usage docs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 20:30:50 +03:00
pikaliov
83ce04150d Add seaborn/matplotlib metric plots, auto-generated after each eval
New: src/training/plot_metrics.py
  - train_metrics.png: loss, temperature, gates, lr
  - val_metrics.png: R@K q→g and g→q
  - overview.png: combined loss + R@1 + gates/tau

Auto-generates plots in {output_dir}/logs/ after each validation epoch.
Also callable standalone: python -m src.training.plot_metrics --log-dir ...

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 19:54:18 +03:00
pikaliov
aee8212454 Add CSV logging with pandas (train.csv, val.csv, per-epoch files)
Logs:
  {output_dir}/logs/train.csv — cumulative train metrics (all epochs)
  {output_dir}/logs/val.csv — cumulative val metrics (eval epochs)
  {output_dir}/logs/epoch_NNN_train.csv — per-epoch train
  {output_dir}/logs/epoch_NNN_val.csv — per-epoch val

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 19:46:07 +03:00
pikaliov
2db3dff819 Set default epochs to 20
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 19:40:00 +03:00
pikaliov
b72c433870 Reduce default batch_size 64→8 (MONA adapters need ~17GB at bs=8)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 19:38:08 +03:00
pikaliov
e3ecb09687 Add VRAM cleanup (gc + empty_cache) before training start
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 19:36:25 +03:00
pikaliov
858718431b Increase default epochs from 10 to 50
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 19:32:35 +03:00
pikaliov
4c862b6ec7 Fix README formulas: use ```math code fence for Gitea rendering
Gitea 1.25 renders math blocks via ```math fence reliably.
Replaced $$...$$ with ```math blocks, inline math kept as backtick code.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 19:28:08 +03:00
pikaliov
082f82f138 Update docs: MONA/LoRA architecture, LaTeX formulas, param summary
README: LaTeX math formulas for text fusion, gated fusion, MONA adapter,
LoRA, and InfoNCE loss. Added adaptation methods table (MONA + LoRA).
Updated model summary to 17.6M/748M (2.35%).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 19:26:01 +03:00
pikaliov
a39f8a9655 Add MONA adapters for DINOv3 + LoRA for DGTRS-CLIP text encoder
MONA (Multi-Cognitive One-Shot Nested Adaptation, CVPR 2025):
- 2 adapters per DINOv3 block (after MSA, after MLP) × 24 layers × 2 encoders
- MonaOp: parallel DWConv 3×3/5×5/7×7 + 1×1 projector
- ScaledLayerNorm + down(1024→64) + MonaOp + GELU + up(64→1024)
- 7M params per encoder, 14M total for drone+sat

LoRA (Low-Rank Adaptation):
- Q and V projections in all 12 DGTRS-CLIP transformer blocks
- rank=4, ~147K params total
- Replaces partial unfreeze (was ~7.6M for last block)

Both adapters run in fp32 (torch.amp.autocast disabled) to avoid
AMP gradient overflow. Frozen backbone layers still run in fp16.

Total trainable: 17.6M / 748M (2.35%)
  MONA (2×DINOv3): 14.0M
  LoRA (DGTRS):    147K
  TextFusionMLP:   3.4M
  Gates + logit:   3

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 19:24:01 +03:00
pikaliov
0c41c1f017 Remove projections (1024 native), add satellite text, dual GatedFusion
Architecture changes:
- Removed proj_drone/proj_sat (1024→512): retrieval space is now
  DINOv3 native 1024-dim, no information loss from projection
- TextFusionMLP: 2304→1024→1024 (was 2304→768→512), shared between branches
- Gallery branch now uses satellite captions (L1/L2/L3) via shared TextFusionMLP
- Two separate GatedFusion gates: α_q (query) and α_g (gallery)
- For sat images without captions (~57%): gate passes image features through

Dataset changes:
- GTAUAVDataset now loads satellite captions from caption index
- collate_gtauav_batch includes sat_caption_l1/l2/l3

Training loop:
- Passes satellite captions to model forward
- Logs both gate_q and gate_g values

11.1M trainable / 734M total (1.51%)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 19:01:30 +03:00
pikaliov
219bb779eb Update docs: full architecture with tensor shapes, formulas, optimizer details
README: architecture diagram with tensor dimensions, L1/L2/L3 text hierarchy
description, text fusion formula, InfoNCE loss formula with learnable
temperature, metrics table, optimizer/scheduler details with per-group LR,
augmentation table, model parameter summary.

CLAUDE.md: updated to DGTRS-CLIP (official architecture), loss formula,
optimizer/scheduler details, text encoder architecture notes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 18:47:38 +03:00
pikaliov
bcb01bcb6d Fix NaN: compute loss in fp32 outside AMP autocast
Root cause: GradScaler scales gradients by ~65536 in fp16, causing
logit_scale.exp() gradient to overflow. The learnable temperature
and similarity logits must stay in fp32.

Fix: model forward runs inside autocast(fp16), but loss computation
(similarity @ temperature + cross_entropy) runs outside in fp32.

Also: clamp logit_scale in logit-space before exp() and force
similarity computation to fp32.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 18:42:29 +03:00
pikaliov
f41a0f27fe Fix epoch display: show 1/10 instead of 0/9
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 18:37:05 +03:00
pikaliov
a47dd6308e Add architecture diagram to README, update docs for DGTRS text encoder
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 18:36:24 +03:00
pikaliov
433fa40ed6 Replace broken LRSCLIPTextEncoder with official DGTRS architecture
Root cause of NaN: our open_clip wrapper had 3 bugs:
1. Positional embeddings summed for all positions instead of masked
   (official: mask1 for pos 0-19, mask2 for pos 20-247)
2. open_clip uses batch-first transformer, DGTRS uses sequence-first
   (LND format with nn.MultiheadAttention)
3. open_clip tokenizer truncates to 77 tokens, DGTRS needs 248

Fix: copied official DGTRS text encoder architecture from
github.com/MitsuiChen14/DGTRS (Apache-2.0):
- src/models/dgtrs/model.py: DGTRSTextEncoder, build_model,
  load_dgtrs_text_encoder, tokenize_dgtrs
- src/models/dgtrs/simple_tokenizer.py: BPE tokenizer (248 tokens)
- src/models/dgtrs/bpe_simple_vocab_16e6.txt.gz: vocabulary

Removed: LRSCLIPTextEncoder class, open_clip dependency for text encoding

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 18:35:41 +03:00
pikaliov
a214320d81 Suppress open_clip 'no pretrained weights' warning for LRSCLIP init
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 18:26:59 +03:00
pikaliov
fa32b2e67f Suppress spurious lr_scheduler.step() warning from PyTorch
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 18:24:54 +03:00
pikaliov
517b87d3d8 Fix scheduler warning: use last_epoch instead of step() loop on resume
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 18:23:17 +03:00
pikaliov
905b9867c8 Add 80/20 random split (replaces cross-area 46/54 split)
- 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>
2026-04-21 18:19:37 +03:00
pikaliov
44bce3096c Add model save/load and --resume for training continuation
- AsymmetricEncoder.save_checkpoint(): saves model_state + metadata
- AsymmetricEncoder.load_checkpoint(): rebuilds model with frozen backbones,
  then loads trainable weights from checkpoint
- --resume flag restores optimizer, loss (learnable tau), and scheduler state
- Training continues from the saved epoch

Usage:
  python -m src.training.train_gtauav --resume out/gtauav/with_text/ckpt_epoch004.pt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 18:14:54 +03:00
pikaliov
3014a5def8 Update docs: training improvements (learnable temp, augmentations, warmup)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 18:08:06 +03:00
pikaliov
998d52cb57 Improve training: learnable temperature, per-group LR, warmup, augmentations
Loss:
- Learnable temperature (CLIP-style logit_scale) with clamp [0.01, 0.5]
- Replaces fixed cosine schedule (still available via --no-learnable-temp)
- Default tau_init=0.07

Optimizer:
- Per-group LR: projections 1e-4, text encoder 1e-5 (10x lower)
- Learnable temperature included in projection param group

Scheduler:
- Linear warmup (2 epochs default) + cosine annealing
- Per-step scheduling (not per-epoch)

Augmentations (separate drone/satellite):
- Drone: RandomResizedCrop(0.7-1.0), HFlip, Rotation(15), ColorJitter,
  RandomGrayscale(0.05), GaussianBlur
- Satellite: RandomResizedCrop(0.7-1.0), HFlip, ColorJitter, RandomGrayscale
- Eval: clean Resize+CenterCrop (no augmentation)

Dataset: supports separate drone_transform/sat_transform args

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 18:07:17 +03:00
pikaliov
6ad9c4d149 Add GTA-UAV experiment: asymmetric DINOv3 + LRSCLIP text encoder
V3 architecture for CVGL caption validation on GTA-UAV-LR dataset:
- AsymmetricEncoder: DINOv3 ViT-L/16 (LVD drone + SAT satellite, frozen)
  + LRSCLIP/DGTRS-CLIP ViT-L-14 text encoder (248 tok, partial unfreeze)
- L1/L2/L3 hierarchical captions from VLM-generated descriptions
- TextFusionMLP (concat 3x768 -> MLP -> 512) + GatedFusion
- Segmentation filter: exclude images with >=90% background+water
- 10.9M trainable / 733M total params, 256x256 input
- coloredlogs + tqdm + emoji for training UX
- Baseline mode (--baseline): image-only, no text encoder loaded

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 17:54:27 +03:00
pikaliov
5da791801c Update docs: target-size 512, dataset verification results
- UAV-VisLoc processed at 512x512 (for segmentation/depth/normals)
- Dataset verified: 6744 drone, 74807 crops, median match 25.9m
- Known issue: 6 drones in route 06 outside satellite coverage
- Resize to model input size (224/256) in dataloader

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 02:41:31 +03:00
pikaliov
abb3337f8d Rewrite: GatedFusion architecture + UAV-GeoLoc dataset
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>
2026-04-17 17:13:00 +03:00
2ce4017ebd Initial commit: caption quality test on UAV-VisLoc
Self-contained experimental track validating generated text captions
via retrieval R@1 lift on UAV-VisLoc.

Architecture: GeoRSCLIP ViT-B/32 dual encoder, 512-dim shared space.
Loss: 4-term InfoNCE (img-img + sat-cap + drone-cap + cap-cap)
      with cosine temperature decay, PALW-like curriculum.
Metric: delta R@1 (with text - without text) >= +3% => PASS.

Gin-configured (balanced / baseline_no_text / text_heavy variants).
Follows NADEZHDA code style.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-17 00:04:46 +03:00