File size: 14,025 Bytes
251713e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
# MAVT — Memory-Augmented Vision Tokenizer

A unified visual tokenizer for images, videos, and 3D assets operating in a shared 4D latent space. MAVT combines two architectural decisions:

- **4D Relational Graph Attention (RGAT)** — injects typed geometric relationships (spatial, temporal, cross-plane) directly into Transformer attention, replacing the implicit all-pairs weighting of standard self-attention with explicit structural priors.
- **Content-Detail Split** — decompresses the token stream into a semantic content channel and a high-frequency detail channel via learned slot attention, achieving ~2.9× token reduction without discarding information.

~276M parameters. Designed for 8× A100/H100 DDP training across three modalities in a progressive curriculum.

---

## Architecture

```
Input (image / video / 3D triplane)
        |
        v
Stage 1: Patchify
  Unified Conv3d  (kernel 2x16x16, stride 2x16x16)
  Image: causal zero-pad → equivalent to Conv2d
  Video: direct application, bidirectional
  3D:    per-plane patchification, plane_id tags
  Output: tokens (B, N, 1152)  +  4D positions (N, 4)
        |
        v
Stage 2: Hybrid Transformer-RGAT Backbone (12 blocks)
  Blocks 0-3:  StandardTransformer  [SigLIP2 init]
  Block  4:    RGAT4D               [zero-init output]
  Blocks 5-7:  StandardTransformer  [SigLIP2 init]
  Block  8:    RGAT4D               [zero-init output]
  Blocks 9-11: StandardTransformer  [SigLIP2 init]

  RGAT4D edge types (dense masked attention, no torch_geometric):
    Type 0  SPATIAL      same (t,z), |dx|<=2, |dy|<=2
    Type 1  TEMPORAL     same (x,y,z), |dt|<=1         [video only]
    Type 2  DEPTH        reserved
    Type 3  CROSS-PLANE  different plane_id, shares>=1 coord [3D only]
        |
        v
Stage 3: Content-Detail Split
  ContentExtractor (slot cross-attn, 2 layers) -> C tokens  (N_c = 0.25*N)
  Residual R = X - broadcast(C)
  DynamicsPooler  (slot cross-attn, 2 layers) -> D tokens  (N_d = 0.10*N)
  Output: [C ; D]  (B, N_c+N_d, 1152)      ~2.9x compression
        |
        v
Stage 4: Dual Latent Projection
  VAE head:      [C;D] -> mu, logvar -> z in R^32  (per token)
  Semantic head: [C;D] -> attention pooling -> s in R^768
        |
        v
Stage 5: Modality-Specific Decoder
  UnifiedDetailExpander: cross-attn from target positions into z (2 layers)
  4x self-attention blocks (d=768)
  4-stage PixelShuffle CNN: 16x spatial upsample
        |
        v
Outputs: reconstructed pixel tensor  +  semantic embedding
```

### Token counts (full resolution)

| Modality | Input N | After C-D Split | Compression |
|----------|---------|-----------------|-------------|
| Image 256px | 256 | 89 | 2.9x |
| Video 8f x 128px | 512 | 179 | 2.9x |
| 3D triplane S=64 | 48 | 24 | 2.0x |

---

## Installation

**Using uv (recommended):**
```bash
bash setup_env.sh
source .venv/bin/activate
```

**Using pip directly:**
```bash
pip install -e ".[dev]"
```

**Requirements:** Python >= 3.8, PyTorch >= 2.2, CUDA 12.x recommended.

---

## Quick Start

### Smoke test (CPU, synthetic data, ~30 seconds)
```bash
python3 smoke_test.py
```

Runs 20 unit tests covering RGAT zero-init, adjacency mask edge counts, C-D Split residual ratio, and full forward passes for all three modalities.

### Training — Stage 1 (image only, synthetic data)
```bash
python3 train.py fit \
  --config configs/train/stage1_image.yaml \
  --trainer.max_steps 500 \
  --trainer.accelerator gpu
```

### Training — Stage 1 (real data, single GPU)
```bash
python3 train.py fit \
  --config configs/train/stage1_image.yaml \
  --data.image_root /path/to/open-images
```

### Training — Stage 1 (DDP, 8 GPUs)
```bash
python3 train.py fit \
  --config configs/train/stage1_image.yaml \
  --trainer.devices 8 \
  --trainer.strategy ddp \
  --data.image_root /path/to/open-images
```

### Training — Stage 2 (resume from stage 1 checkpoint)
```bash
python3 train.py fit \
  --config configs/train/stage2_video.yaml \
  --ckpt_path checkpoints/stage1/mavt-stage1-best.ckpt \
  --data.image_root /path/to/open-images \
  --data.video_root /path/to/webvid
```

### Training — Stage 3 (all modalities)
```bash
python3 train.py fit \
  --config configs/train/stage3_3d.yaml \
  --ckpt_path checkpoints/stage2/mavt-stage2-best.ckpt \
  --data.image_root /path/to/open-images \
  --data.video_root /path/to/webvid \
  --data.threed_root /path/to/cap3d
```

### Evaluation
```bash
python3 evaluate.py \
  --ckpt checkpoints/stage1/mavt-stage1-best.ckpt \
  --modality image \
  --data_root /path/to/images \
  --resolution 256
```

### WandB logging
Append to any training command:
```bash
  --trainer.logger.class_path lightning.pytorch.loggers.WandbLogger \
  --trainer.logger.init_args.project mavt \
  --trainer.logger.init_args.name stage1-image
```

---

## Training Curriculum

Three progressive stages following the spec:

| Stage | Modalities | Steps | LR | SigLIP2 | Notes |
|-------|-----------|-------|----|---------|-------|
| 1 | Image | 200K | 1e-4 | Frozen | Establish spatial features |
| 2 | + Video | 200K | 5e-5 | Last 4 blocks unfrozen | Add temporal structure |
| 3 | + 3D | 50K | 2e-5 | Fully unfrozen | Cross-plane edges |

Resume across stages by passing `--ckpt_path` to the next stage's config.

---

## Configuration

All model and training hyperparameters are YAML-configurable via Lightning CLI. Override any field on the command line:

```bash
# Change patch size
python3 train.py fit --config configs/train/stage1_image.yaml \
  --model.patch_size 8

# Change RGAT spatial radius
python3 train.py fit --config configs/train/stage1_image.yaml \
  --model.r_s 1

# Disable LPIPS (faster, less GPU memory)
python3 train.py fit --config configs/train/stage1_image.yaml \
  --model.use_lpips false
```

### Key model parameters (`configs/model/mavt_base.yaml`)

| Parameter | Default | Description |
|-----------|---------|-------------|
| `embed_dim` | 1152 | Token embedding dimension |
| `num_heads` | 16 | Attention heads |
| `num_blocks` | 12 | Backbone depth (RGAT at positions 4, 8) |
| `patch_size` | 16 | Spatial patch size in pixels |
| `latent_dim` | 32 | VAE latent dimension per token |
| `r_s` | 2 | RGAT spatial radius (5x5 window) |
| `r_t` | 1 | RGAT temporal radius (+-1 frame) |
| `use_gradient_checkpointing` | true | Saves ~30% GPU memory, +15% compute |

---

## Loss Function

```
L_total = w_mod * (w_l1 * L1 + w_lpips * LPIPS)
        + w_kl  * KL
        + w_clip * InfoNCE(visual, text)     [optional]
        + w_aux  * SlotDiversity
```

Default weights: `w_l1=1.0, w_lpips=0.1, w_kl=1e-4, w_clip=0.1, w_aux=0.01`.

`w_mod` is a per-modality inverse-EMA scale so harder modalities receive proportionally more gradient.

---

## C-D Split Monitoring

Three signals logged during training to detect failure modes:

| Metric | Target | Failure mode |
|--------|--------|-------------|
| `cd_slot_diversity` | <= 0.5 | > 0.9 → slot collapse |
| `cd_residual_ratio` | 0.3 – 0.5 | < 0.1 → content over-fits; > 0.7 → content fails |
| `detail_contribution` | >= 15% | Detail branch inactive |

---

## SigLIP2 Weight Initialization

To load pretrained SigLIP2 weights into the 10 Transformer backbone blocks:

```bash
python3 train.py fit \
  --config configs/train/stage1_image.yaml \
  --model.init_siglip2 true \
  --model.siglip2_model_name google/siglip2-base-patch16-224
```

This requires `pip install transformers` and HuggingFace Hub access. The two RGAT4D blocks are always randomly initialized with zero-init output projections (identity at step 0).

---

## Hardware Requirements

**Minimum (smoke test / development):**
- Any CPU, 4 GB RAM

**Recommended (full-scale training):**
- 8x A100 80GB or H100 80GB
- bf16 mixed precision
- Batch size: 32 image / 16 video / 32 3D per GPU
- Estimated wall-clock: ~10 days for 450K total steps

**Memory notes:**
- RGAT4D blocks operate on the full (B, N, N) attention matrix
- For video with N=2048, enable `use_gradient_checkpointing=true` and reduce batch size if OOM
- Adjacency masks are precomputed once per (modality, resolution) and cached on device

---

## Project Structure

```
Antoken/
├── pyproject.toml
├── setup_env.sh
├── train.py              # LightningCLI entry point
├── evaluate.py           # Evaluation script
├── smoke_test.py         # 20 unit tests (no GPU required)
├── quick_train_test.py   # End-to-end training loop check
├── configs/
│   ├── model/mavt_base.yaml
│   └── train/
│       ├── stage1_image.yaml
│       ├── stage2_video.yaml
│       └── stage3_3d.yaml
└── src/mavt/
    ├── model/
    │   ├── patchify.py           # Conv3d patchification, 4D position grids
    │   ├── rgat.py               # RGAT4DBlock, build_adjacency
    │   ├── transformer.py        # StandardTransformerBlock (SigLIP2-compatible)
    │   ├── backbone.py           # 12-block hybrid backbone, mask caching
    │   ├── content_detail_split.py  # SlotPooler, ContentDetailSplit
    │   ├── latent_heads.py       # VAEHead, SemanticHead
    │   ├── decoder.py            # UnifiedDetailExpander, PixelShuffleCNNDecoder
    │   └── mavt.py               # Full MAVT model
    ├── losses/losses.py          # MAVTLoss, ModalityEMAWeighter, infonce_loss
    ├── data/
    │   ├── datasets.py           # Synthetic, ImageFolder, Video, ThreeD datasets
    │   └── datamodule.py         # MAVTDataModule (3-stage curriculum)
    ├── training/
    │   └── lightning_module.py   # MAVTLightningModule, LR schedule, visualization
    └── evaluation/
        └── metrics.py            # PSNR, SSIM, temporal-PSNR, FIDTracker
```

---

## Data Formats

| Modality | Dataset tensor shape | Source |
|----------|---------------------|--------|
| Image | `(3, H, W)` float32, normalized [-1, 1] | Open Images V7, any image folder |
| Video | `(3, T, H, W)` float32, normalized | WebVid, Panda-70M, HMDB51, MSVD |
| 3D triplane | `(3, 3, S, S)` float32 | Cap3D, TRELLIS-SLAT preprocessing |

Video reconstruction target is temporally downsampled to `(3, T//t_patch, H, W)` to match the patch-grid temporal resolution of the encoder output.

---

## Ablation Variants

As specified in section 10.2 of the design doc:

| Variant | RGAT | C-D Split | Run |
|---------|------|-----------|-----|
| V0 (baseline) | No | No | `--model.num_blocks 12` with standard Transformer only |
| V1 | No | Yes | Remove RGAT blocks from backbone |
| V2 | Yes | No | Set `content_ratio=1.0` to disable split |
| V3 (target) | Yes | Yes | Default config |

---

## 📌 MAVT Path — Step-by-Step Trace (rgat-demo branch)

> Detailed 3D-path documentation with code references, comparison vs AToken/LRM, and slide outline.
> See [`3D_pipeline_and_review.md`](3D_pipeline_and_review.md) for the full document.

### 3D path code trace

```
GLB mesh → offline render → 3 plane PNG (oxoy/oxoz/oyoz, 256×256 RGB)

UniversalThreeDDataset     # src/mavt/data/datasets.py:174-228
                         # → (B, 3, 3, 256, 256) in [-1, 1]

PatchifyEncoder.forward_threed    # src/mavt/model/patchify.py:134-175
                                # → (B, 768, 768) tokens + positions (768, 4) + plane_ids (768,)
                                # Plane XY: pos = (0, x, y, 0)   plane_id = 0
                                # Plane XZ: pos = (0, x, 0, z)   plane_id = 1
                                # Plane YZ: pos = (0, 0, y, z)   plane_id = 2

Hybrid Transformer + RGAT4D backbone (12 blocks)
   # src/mavt/model/backbone.py + rgat.py
   # 4 edge types: spatial (5×5) / temporal (|dt|≤1) / depth (reserved) / cross-plane (share ≥1 coord)

ContentDetailSplit                # src/mavt/model/content_detail_split.py:160-220
                                 # → 268 content slots + 192 detail tokens = 460 tokens

VAEHead                          # src/mavt/model/latent_heads.py
                                 # → 460 × 32 latent

AsymmetricDecoder (3 plane loop, share weights)   # src/mavt/model/decoder.py:170-405
   # Distance bias: implicit plane-aware reconstruction
   # → (B, 3, 3, 256, 256) reconstruction
```

### 3D vs other modalities

| Modality | Raw tokens | Compressed | Compression |
|---|---:|---:|---:|
| Image 256² | 256 | 128 | 48× |
| Video 16×256² | 2 048 | 1 024 | 96× |
| **3D 256² ×3 planes** | **768** | **460** | **13.4×** |

### Comparison with related work

| Model | 3D repr | Params | Stage 3 3D |
|---|---|---:|---|
| AToken (Lu et al., arXiv:2509.14476) | triplane | 224 M | not reported |
| LRM (Hong et al., ICLR 2024) | triplane | 7 000 M | not reported |
| EG3D (Chan et al., CVPR 2022) | triplane | 80 M | n/a |
| **MAVT (rgat-demo)** | triplane | 224 M | **val/3d = 0.11** |

### Known limitations

- 3D tested only on triplane renders (not raw GLB end-to-end).
- Cross-plane RGAT edge density high (~32 edges/token) — cap radius L1 ≤ 1 recommended.
- Video data is ~51% corrupt (loader handles).
- 30k 3D objects (vs AToken 1M+).
- 3D compression ratio (13.4×) lower than image (48×) and video (96×).
- No 3D mesh metrics (Chamfer, Volume IoU) yet — only per-plane PSNR/SSIM.

---

## 🎯 Stage 3 Training (rgat-demo branch, live)

| | |
|---|---|
| Job | Slurm 11841 on dgx01 (2× A100 DDP) |
| Status | RUNNING, 22k/50k steps |
| W&B | [banalaxis93/mavt/zbq1iqma](https://wandb.ai/banalaxis93/mavt/runs/zbq1iqma) |
| Init | Stage 1.5 step 120k (val/loss=0.124) |
| 3D data | 30,519 Objaverse triplanes + LVIS captions |

Latest metrics @ step 22k:

| Metric | image | video | threed |
|---|---:|---:|---:|
| val/loss | 0.081 | 0.197 | 0.110 |
| train/L1 | 0.044 | 0.110 | 0.032 |
| Semantic (1-cos) | 0.119 | — | — |

→ Image already beats Stage 1.5 baseline (0.124) at 22k steps.
→ 3D pooler converges 64% in 14k steps (0.30 → 0.11).