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>
This commit is contained in:
@@ -25,6 +25,7 @@ LOGGER = logging.getLogger("caption_test.model")
|
||||
coloredlogs.install(level="INFO", logger=LOGGER, fmt="%(asctime)s %(name)s %(levelname)s %(message)s")
|
||||
from safetensors.torch import load_file as load_safetensors
|
||||
|
||||
from src.models.adapters import inject_lora_into_dgtrs, inject_mona_into_dinov3
|
||||
from src.models.dgtrs.model import DGTRSTextEncoder, load_dgtrs_text_encoder, tokenize_dgtrs
|
||||
from src.models.dual_encoder import GatedFusion, ProjectionHead
|
||||
|
||||
@@ -279,6 +280,8 @@ class AsymmetricEncoder(nn.Module):
|
||||
lrsclip_path: str = "nn_models/LRSCLIP/DGTRS-CLIP-ViT-L-14.pt",
|
||||
init_gate: float = 0.7,
|
||||
baseline_mode: bool = False,
|
||||
mona_bottleneck: int = 64,
|
||||
lora_rank: int = 4,
|
||||
device: str = "cuda",
|
||||
) -> None:
|
||||
super().__init__()
|
||||
@@ -286,17 +289,19 @@ class AsymmetricEncoder(nn.Module):
|
||||
self.baseline_mode = baseline_mode
|
||||
self.device = device
|
||||
|
||||
# Image encoders (frozen).
|
||||
# Image encoders (frozen + MONA adapters).
|
||||
self.drone_encoder = DINOv3ViT.from_pretrained(dino_web_path)
|
||||
self.sat_encoder = DINOv3ViT.from_pretrained(dino_sat_path)
|
||||
self._freeze(self.drone_encoder)
|
||||
self._freeze(self.sat_encoder)
|
||||
inject_mona_into_dinov3(self.drone_encoder, bottleneck=mona_bottleneck)
|
||||
inject_mona_into_dinov3(self.sat_encoder, bottleneck=mona_bottleneck)
|
||||
|
||||
# Text encoder — official DGTRS architecture (partial unfreeze).
|
||||
# Text encoder — official DGTRS architecture (frozen + LoRA).
|
||||
if not baseline_mode:
|
||||
self.text_encoder = load_dgtrs_text_encoder(lrsclip_path)
|
||||
self._freeze(self.text_encoder)
|
||||
self._unfreeze_text_last_block()
|
||||
inject_lora_into_dgtrs(self.text_encoder, rank=lora_rank)
|
||||
else:
|
||||
self.text_encoder = None
|
||||
|
||||
@@ -317,33 +322,13 @@ class AsymmetricEncoder(nn.Module):
|
||||
p.requires_grad = False
|
||||
module.eval()
|
||||
|
||||
def _unfreeze_text_last_block(self) -> None:
|
||||
"""Unfreeze last transformer block + text_projection + ln_final."""
|
||||
if self.text_encoder is None:
|
||||
return
|
||||
# Last resblock.
|
||||
for p in self.text_encoder.transformer.resblocks[-1].parameters():
|
||||
p.requires_grad = True
|
||||
# ln_final.
|
||||
for p in self.text_encoder.ln_final.parameters():
|
||||
p.requires_grad = True
|
||||
# text_projection.
|
||||
tp = self.text_encoder.text_projection
|
||||
if isinstance(tp, nn.Parameter):
|
||||
tp.requires_grad = True
|
||||
elif isinstance(tp, nn.Module):
|
||||
for p in tp.parameters():
|
||||
p.requires_grad = True
|
||||
|
||||
def encode_drone(self, images: torch.Tensor) -> torch.Tensor:
|
||||
"""Encode drone images. Returns [B, DINO_DIM]."""
|
||||
with torch.no_grad():
|
||||
return self.drone_encoder(images)
|
||||
"""Encode drone images with MONA adapters. Returns [B, DINO_DIM]."""
|
||||
return self.drone_encoder(images)
|
||||
|
||||
def encode_satellite(self, images: torch.Tensor) -> torch.Tensor:
|
||||
"""Encode satellite images. Returns [B, DINO_DIM]."""
|
||||
with torch.no_grad():
|
||||
return self.sat_encoder(images)
|
||||
"""Encode satellite images with MONA adapters. Returns [B, DINO_DIM]."""
|
||||
return self.sat_encoder(images)
|
||||
|
||||
def encode_text_levels(
|
||||
self,
|
||||
|
||||
Reference in New Issue
Block a user