Asymmetric encoders + MONA all 24 blocks + 1024-dim + hard negatives
Architecture changes: - Asymmetric DINOv3: WEB (drone) + SAT (satellite) with separate MONA - MONA on all 24 blocks per encoder (was last 12) - Remove projection, native 1024-dim retrieval space (was 512) - Total: 748M params, 17.6M trainable (2.35%) Hard negative memory bank: - MoCo-style FIFO queue of 4096 detached gallery embeddings - Each batch: B in-batch + Q queue negatives in InfoNCE - Queue updated after each forward pass Training config: - batch_size=8, grad_accum=8, effective_batch=64 - eval_every=1 (eval + train recall every epoch) - Max bs=24 with grad checkpointing on RTX 4090 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -31,6 +31,7 @@ from tqdm import tqdm
|
||||
|
||||
from src.datasets.gtauav_dataset import GTAUAVDataset, collate_gtauav_batch
|
||||
from src.losses.multi_infonce import InfoNCELoss
|
||||
from src.losses.hard_negatives import NegativeMemoryBank
|
||||
from src.training.plot_metrics import generate_plots
|
||||
from src.training.trackers import ExperimentTracker
|
||||
from src.training.grad_monitor import compute_gradient_norms, log_gradient_summary
|
||||
@@ -73,7 +74,7 @@ class TrainConfigGTAUAV:
|
||||
lrsclip_path: str = _LRSCLIP
|
||||
init_gate: float = 0.7
|
||||
baseline_mode: bool = False
|
||||
shared_encoder: bool = True # single DINOv3 WEB for both branches (saves ~4-5 GB)
|
||||
shared_encoder: bool = False # asymmetric: WEB (drone) + SAT (satellite)
|
||||
gradient_checkpointing: bool = True # trade compute for VRAM (allows larger batch)
|
||||
|
||||
# Training.
|
||||
@@ -99,6 +100,7 @@ class TrainConfigGTAUAV:
|
||||
weight_q2g: float = 0.6
|
||||
weight_g2q: float = 0.4
|
||||
learnable_temperature: bool = True
|
||||
neg_bank_size: int = 4096 # hard negative memory bank size (0 = disabled)
|
||||
|
||||
# Tracking & diagnostics.
|
||||
use_wandb: bool = False
|
||||
@@ -405,6 +407,7 @@ def train(cfg: TrainConfigGTAUAV) -> None:
|
||||
shared_encoder=cfg.shared_encoder,
|
||||
device=cfg.device,
|
||||
).to(cfg.device)
|
||||
LOGGER.info("embed_dim=%d", model.embed_dim)
|
||||
|
||||
# --- Gradient checkpointing (trade compute for VRAM) ---
|
||||
if cfg.gradient_checkpointing:
|
||||
@@ -446,6 +449,12 @@ def train(cfg: TrainConfigGTAUAV) -> None:
|
||||
cfg.tau_init,
|
||||
)
|
||||
|
||||
# Hard negative memory bank.
|
||||
neg_bank = None
|
||||
if cfg.neg_bank_size > 0:
|
||||
neg_bank = NegativeMemoryBank(size=cfg.neg_bank_size, dim=model.embed_dim).to(cfg.device)
|
||||
LOGGER.info("Negative memory bank: size=%d, dim=%d", cfg.neg_bank_size, model.embed_dim)
|
||||
|
||||
# Data — separate transforms for train (augmented) and eval (clean).
|
||||
drone_train_tf = get_drone_train_transform(image_size=256)
|
||||
sat_train_tf = get_satellite_train_transform(image_size=256)
|
||||
@@ -599,13 +608,19 @@ def train(cfg: TrainConfigGTAUAV) -> None:
|
||||
sat_caption_l2=batch["sat_caption_l2"],
|
||||
sat_caption_l3=batch["sat_caption_l3"],
|
||||
)
|
||||
# Loss in fp32 (learnable temperature gradient overflows in fp16).
|
||||
# Loss in fp32 with optional hard negative queue.
|
||||
queue_neg = neg_bank.get_queue() if neg_bank is not None else None
|
||||
loss_dict = loss_fn(
|
||||
embeddings=embeddings,
|
||||
epoch=epoch,
|
||||
total_epochs=cfg.epochs,
|
||||
queue_negatives=queue_neg,
|
||||
)
|
||||
|
||||
# Enqueue current gallery embeddings (detached).
|
||||
if neg_bank is not None:
|
||||
neg_bank.enqueue(embeddings["gallery"].detach())
|
||||
|
||||
# Scale loss by accumulation steps so gradients average correctly.
|
||||
raw_loss = float(loss_dict["total"].item()) # save before backward
|
||||
total_loss = loss_dict["total"] / accum
|
||||
|
||||
Reference in New Issue
Block a user