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>
This commit is contained in:
21
README.md
21
README.md
@@ -130,7 +130,22 @@ where `x̂ = γ · LN(x) + γₓ · x` (scaled LayerNorm, `γ` init `10⁻⁶`,
|
|||||||
\text{MonaOp}(\mathbf{x}) = \frac{\text{DWConv}_{3 \times 3}(\mathbf{x}) + \text{DWConv}_{5 \times 5}(\mathbf{x}) + \text{DWConv}_{7 \times 7}(\mathbf{x})}{3} + \mathbf{x}
|
\text{MonaOp}(\mathbf{x}) = \frac{\text{DWConv}_{3 \times 3}(\mathbf{x}) + \text{DWConv}_{5 \times 5}(\mathbf{x}) + \text{DWConv}_{7 \times 7}(\mathbf{x})}{3} + \mathbf{x}
|
||||||
```
|
```
|
||||||
|
|
||||||
MONA runs in **fp16** (AMP-native) with gradient checkpointing to save VRAM.
|
MONA runs in **bfloat16** with gradient checkpointing to save VRAM.
|
||||||
|
Applied only to the **last 12 blocks** (out of 24) — early blocks extract low-level
|
||||||
|
features (edges, textures) that are domain-agnostic and don't need spatial adaptation.
|
||||||
|
|
||||||
|
**Why bfloat16, not fp16:**
|
||||||
|
|
||||||
|
MONA's scaled LayerNorm uses `gamma` initialized at `1e-6` for near-identity output at start.
|
||||||
|
fp16 has min normal ~6.1e-5, so `1e-6` falls into the subnormal range where precision collapses,
|
||||||
|
causing NaN after a few blocks. bfloat16 has the same exponent range as fp32 (min ~1.2e-38),
|
||||||
|
so `1e-6` is safely representable. RTX 4090 supports bf16 natively with comparable throughput.
|
||||||
|
|
||||||
|
| Precision | `1e-6` representable | MONA stable | VRAM (bs=48) |
|
||||||
|
|-----------|:---:|:---:|:---:|
|
||||||
|
| fp32 | yes | yes | 21.4 GB |
|
||||||
|
| **bfloat16** | **yes** | **yes** | **21.8 GB** |
|
||||||
|
| fp16 | subnormal (lossy) | **NaN** | — |
|
||||||
|
|
||||||
**Why MONA over LoRA for DINOv3:**
|
**Why MONA over LoRA for DINOv3:**
|
||||||
|
|
||||||
@@ -305,14 +320,14 @@ Mixed precision: AMP fp16 for model forward, fp32 for loss
|
|||||||
| Component | Params | Trainable | Notes |
|
| Component | Params | Trainable | Notes |
|
||||||
|-----------|--------|-----------|-------|
|
|-----------|--------|-----------|-------|
|
||||||
| DINOv3 ViT-L/16 WEB (shared) | 303M | frozen | single encoder for drone + satellite |
|
| DINOv3 ViT-L/16 WEB (shared) | 303M | frozen | single encoder for drone + satellite |
|
||||||
| MONA adapters (shared, fp16) | 6.85M | 6.85M | 2 per block × 24 blocks, bottleneck=64 |
|
| MONA adapters (shared, bf16) | 3.5M | 3.5M | 2 per block × last 12 blocks, bottleneck=64 |
|
||||||
| Image projection | 525K | 525K | Linear(1024→512) after DINOv3 CLS |
|
| Image projection | 525K | 525K | Linear(1024→512) after DINOv3 CLS |
|
||||||
| DGTRS-CLIP ViT-L-14 (text) | 124M | frozen | backbone weights frozen |
|
| DGTRS-CLIP ViT-L-14 (text) | 124M | frozen | backbone weights frozen |
|
||||||
| LoRA adapters (text) | 147K | 147K | Q+V, rank=4, 12 blocks |
|
| LoRA adapters (text) | 147K | 147K | Q+V, rank=4, 12 blocks |
|
||||||
| TextFusionMLP (shared) | 1.5M | 1.5M | Linear(2304,512) + GELU + Linear(512,512) |
|
| TextFusionMLP (shared) | 1.5M | 1.5M | Linear(2304,512) + GELU + Linear(512,512) |
|
||||||
| GatedFusion α_q + α_g | 2 | 2 | separate gate scalars |
|
| GatedFusion α_q + α_g | 2 | 2 | separate gate scalars |
|
||||||
| logit_scale | 1 | 1 | learnable temperature |
|
| logit_scale | 1 | 1 | learnable temperature |
|
||||||
| **Total (shared)** | **436M** | **9.0M (2.06%)** | retrieval dim = 512 |
|
| **Total (shared)** | **432M** | **5.6M (1.30%)** | retrieval dim = 512 |
|
||||||
|
|
||||||
> **Asymmetric mode** (`--shared-encoder false`): uses separate DINOv3 WEB (drone) + DINOv3 SAT
|
> **Asymmetric mode** (`--shared-encoder false`): uses separate DINOv3 WEB (drone) + DINOv3 SAT
|
||||||
> (satellite) encoders with independent MONA adapters. Requires ~4-5 GB more VRAM.
|
> (satellite) encoders with independent MONA adapters. Requires ~4-5 GB more VRAM.
|
||||||
|
|||||||
@@ -68,7 +68,11 @@ class MonaAdapter(nn.Module):
|
|||||||
self.gammax = nn.Parameter(torch.ones(dim))
|
self.gammax = nn.Parameter(torch.ones(dim))
|
||||||
|
|
||||||
def forward(self, x: torch.Tensor, hw: tuple[int, int]) -> torch.Tensor:
|
def forward(self, x: torch.Tensor, hw: tuple[int, int]) -> torch.Tensor:
|
||||||
"""Apply MONA adapter in fp32 (gamma=1e-6 and small bottleneck underflow in fp16).
|
"""Apply MONA adapter in bfloat16.
|
||||||
|
|
||||||
|
bfloat16 has the same exponent range as fp32 (min ~1.2e-38), so gamma=1e-6
|
||||||
|
is safe. fp16 would underflow (min normal ~6.1e-5). RTX 4090 supports bf16
|
||||||
|
natively with comparable throughput to fp16.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
x: Token features [B, N, D] where N includes CLS + register + patch tokens.
|
x: Token features [B, N, D] where N includes CLS + register + patch tokens.
|
||||||
@@ -78,10 +82,9 @@ class MonaAdapter(nn.Module):
|
|||||||
Adapted features [B, N, D] (same shape, residual connection).
|
Adapted features [B, N, D] (same shape, residual connection).
|
||||||
"""
|
"""
|
||||||
orig_dtype = x.dtype
|
orig_dtype = x.dtype
|
||||||
with torch.amp.autocast("cuda", enabled=False):
|
with torch.amp.autocast("cuda", dtype=torch.bfloat16):
|
||||||
x = x.float()
|
|
||||||
identity = x
|
identity = x
|
||||||
# Scaled LayerNorm.
|
# Scaled LayerNorm (gamma=1e-6 safe in bf16).
|
||||||
x = self.norm(x) * self.gamma + x * self.gammax
|
x = self.norm(x) * self.gamma + x * self.gammax
|
||||||
|
|
||||||
x = self.down(x) # [B, N, bottleneck]
|
x = self.down(x) # [B, N, bottleneck]
|
||||||
|
|||||||
Reference in New Issue
Block a user