Lab 05 — Multimodal Bridge (CLIP → LLM Projection)
Phase: 02 — Model Architecture Mastery | Difficulty: ⭐⭐⭐⭐⭐ | Time: 4–5 hours
LLaVA's core insight: a 2-layer MLP is all it takes to connect a frozen vision encoder to a frozen LLM. This lab builds that bridge at toy scale, where the architecture, the freezing discipline, and the loss masking are identical to the 7B-parameter version.
What you build
Projector— the trainable 2-layer MLP mapping vision-feature space → LLM embedding space (the only component that trains; <0.25% of total parameters)MultimodalModel— frozen vision tower + frozen causal LM with visual tokens prepended to the text sequenceloss— next-token cross-entropy on text positions only, with the visual-token causal shift and prompt masking (ignore_index=-100)generate— greedy image-conditioned decoding
Frozen TinyVisionEncoder / TinyCausalLM stand-ins are provided with the same interfaces
as CLIP ViT-L/14 and TinyLlama, so the tests run in seconds with no downloads; swapping in
the real models is the extension.
Key concepts
| Concept | What to understand |
|---|---|
| Patch tokens, not CLS | LLaVA feeds all patch tokens to the LM — VQA needs spatial detail the pooled embedding discards |
| Freezing discipline | requires_grad_(False) + vision under no_grad — optimizer never sees frozen params, no grad buffers allocated |
| The causal shift | Logits at the LAST visual position predict the FIRST text token; off-by-one here is the classic multimodal bug (a test targets it) |
| Loss masking | Prompt/question positions set to -100 — grade the answer, not the echo |
| Stage-1 vs stage-2 | LLaVA trains projector-only on captions first, then unfreezes the LM for instruction tuning |
Files
| File | Purpose |
|---|---|
lab.py | Skeleton with # TODO markers |
solution.py | Complete reference; python solution.py shows projector-only training working |
test_lab.py | 12 tests: freezing, grad flow, logit alignment, masking, image→text information flow |
requirements.txt | torch, pytest |
Run
pip install -r requirements.txt
pytest test_lab.py -v
LAB_MODULE=solution pytest test_lab.py -v
Suggested TODO order
Projector— two lines of architecture, but know why MLP > single linearMultimodalModel.__init__— the freezing; checktest_only_projector_is_trainablevisual_tokens→forward— concatenation order mattersloss— draw the position layout on paper before coding the slicegenerate
Success criteria
- All 12 tests pass — especially
test_loss_alignment_uses_image_conditioned_prediction(the off-by-one catcher) andtest_trained_model_uses_the_image(proves information actually flows image → projector → LM) - You can state the trainable-parameter fraction and why that makes stage-1 training cheap
- You can explain what breaks if you
detach()the projector output instead of the vision output (no gradient reaches the projector — silent non-training)
Full-scale extension (the original spec)
Swap stand-ins for openai/clip-vit-large-patch14 (frozen) and TinyLlama-1.1B (frozen);
train the projector on the LLaVA-Instruct-150K caption subset; evaluate VQAv2 accuracy
(target >50%); compare single-linear vs MLP projector. Then: Q-Former connector (BLIP-2),
and measure the NPU-relevant cost — visual tokens inflate the KV cache by N_patches at
every layer.
Interview Q&A
Q: Why freeze both towers in stage 1? The projector starts random — its gradient noise would destroy pretrained representations (the LM would "catastrophically adapt" to garbage visual tokens). Train the bridge until visual tokens live in the LM's embedding distribution, then optionally unfreeze for instruction tuning.
Q: Why does LLaVA prepend visual tokens rather than cross-attend (Flamingo-style)? Prepending reuses the LM unchanged (no new cross-attention layers to train), is trivially compatible with KV caching and existing serving stacks, and scales with patch count. Cross-attention isolates modalities better and keeps text-only throughput intact, at the cost of new trained layers — a classic deployment-vs-architecture tradeoff.
Q: What is the inference cost of visual tokens on an edge NPU? Each of the N visual tokens occupies KV cache in every layer (N × layers × 2 × d_kv) and participates in every attention computation — for 576 CLIP tokens this often dominates a short-question VQA prompt. Mitigations: token pruning/merging, pooled visual tokens, Q-Former-style compression to a fixed small budget.