LLM + RoboticsModule 08 · Stage 07

Multimodal fusion & projection

How a language model learns to see: encode the image into vectors, translate those vectors into the language model's own embedding space, and decide where in the network the two streams meet. Add an action output and you have the core of a robot vision-language-action model.

PrerequisitesStage 01 (tensors, shapes), a working grasp of transformers and attention, basic PyTorch training loops
Time20–30 h: about 8 h of reading, 12–20 h for the projector project
HardwareLaptop for the reading and demo · free Colab GPU for the small project · one 8-GPU node for a full LLaVA-scale run
Key papersCLIP (2021), SigLIP (2023), Flamingo (2022), LLaVA (2023), RT-2 (2023), OpenVLA (2024), π0 (2024)

Source: Suraj Sharma's roadmap post, stage 7 · saved in sources/08-suraj-ai-research-engineer.md

Start here (no maths needed)

A language model only understands words. To let it see, we put a picture-reading model in front of it and a small "translator" in between, so the picture arrives looking like words the language model already knows.

Think of it like this

Imagine a brilliant storyteller who is blindfolded, and a friend who can see but only speaks a different language. The friend looks at a photo and describes it in their own language. A translator sits between them and turns each description into words the storyteller understands. Now the storyteller can talk about the photo. The friend is the vision encoder, the translator is the projection layer, and the storyteller is the language model.

How it works

  1. Cut the picture into a grid of small squares, called patches.
  2. A vision model turns each patch into a list of numbers that describes what's in it.
  3. A small translator network (the projector) reshapes those numbers so they look like word-numbers to the language model.
  4. The language model reads the picture-words and the real words together, and writes an answer.
  5. For a robot, the model also writes out movements, for example "move the gripper 2 cm left and close it".

Why robots care

This is how robots that follow spoken instructions are built. You say "put the red cup in the sink". The robot's cameras supply the picture, the language model understands "red cup" and "sink", and the model's output is turned into arm movements. These models are called vision-language-action models, or VLAs.

Try it

Words to know

Vision encoder
A model that turns a picture into a list of number-vectors, one per patch.
Patch
One small square of the picture, for example 14 × 14 pixels.
Projector
A small network that translates picture-vectors into the language model's word-vector space.
Fusion
The point where picture information and word information are combined.
VLA
Vision-language-action model: sees, reads instructions, and outputs robot movements.

Going deeper: everything below is the full version, with some maths. On a first read, skim the headings and the "Why it matters for robotics" box, and come back for the rest later.

1. What this stage covers

TL;DR

Most open vision-language models are three parts glued together: a pretrained image encoder (usually CLIP or SigLIP), a small projector, and a pretrained LLM. The projector maps each image-patch vector into the LLM's token-embedding space, and the LLM reads the result as if it were a run of extra tokens. That's "early" fusion at the input. The alternative is to keep the LLM's input pure text and let it look at the image through added cross-attention layers (Flamingo-style). Robot VLAs reuse exactly this stack, then add a way to emit actions: either as discrete tokens in the vocabulary (RT-2, OpenVLA) or through a separate continuous action head or "expert" (π0 and others).

2. Concepts, explained

Vision encoders: CLIP and SigLIP

A Vision Transformer (ViT) cuts an image into P × P patches, flattens each, applies a linear layer, adds a position embedding and runs a standard transformer. The output is one vector per patch. What makes CLIP and SigLIP special is how they're trained: on huge sets of image–caption pairs, with a text encoder alongside, so that an image and its caption land near each other in a shared space. Those patch features are already "close to language", which is why a small projector is enough to hand them to an LLM.

CLIP (Radford et al., 2021) scores every image against every caption in a batch and applies a softmax in both directions. Each image must pick out its own caption from all the others.

SigLIP (Zhai, Mustafa, Kolesnikov and Beyer, 2023) replaces the softmax with an independent sigmoid on each image–text pair: "do these two match, yes or no?" No batch-wide normalisation is needed, which makes very large batches cheaper and also works better at small batch sizes. The paper found gains from growing the batch fade quickly, and that around 32k is enough.

CLIP  (softmax, per image i):  −log  exp(t·xᵢ·yᵢ) / Σⱼ exp(t·xᵢ·yⱼ)     (plus the text→image direction)
SigLIP (sigmoid, every pair):  −Σᵢⱼ log σ( zᵢⱼ · (t·xᵢ·yⱼ + b) ),   zᵢⱼ = +1 if i = j else −1

x, y = normalised image / text embeddings,  t = learned temperature,  b = learned bias

Token count is set by geometry: an image of side H with patch size P gives (H/P)² tokens. A 224-pixel image with 14-pixel patches is 16 × 16 = 256 tokens. At 384 pixels it's 27 × 27 = 729. The demo below makes this concrete, because for robots with several cameras it becomes the dominant cost.

Projection: the bridge into the LLM

The encoder's vectors have its own width (say 768 or 1152) and live in its own space. The LLM expects vectors of its hidden size, in its embedding space. A projector learns that map. LLaVA (Liu, Li, Wu and Lee, 2023) used a single linear layer from a CLIP ViT-L/14 into Vicuna. Its follow-up, LLaVA-1.5, switched to a two-layer MLP and found it better. Training is two stages: first train only the projector on image–caption pairs (feature alignment), then unfreeze the LLM and fine-tune on visual instruction data, which LLaVA generated with text-only GPT-4.

v   = Encoder(image)            # [N_patches, d_vision]
h   = W₂ · GELU(W₁ · v)         # [N_patches, d_llm]   the projector
seq = [h ; Embed(prompt_tokens)]   # image tokens first, then text
LLM(seq) → next-token loss on the answer tokens only

Other bridges compress the image into a fixed, smaller set of tokens. A Q-Former (BLIP-2) or Perceiver resampler (Flamingo) uses a few dozen learned queries that cross-attend to the patches. Pixel shuffle (used in SmolVLM-style small models) folds each 2 × 2 block of neighbouring patches into one wider vector, cutting tokens by 4×. Fewer tokens means cheaper inference and a less precise view of small details.

Cross-attention as fusion

In cross-attention, queries come from one stream and keys and values from another. Flamingo (Alayrac et al., 2022) keeps a pretrained LLM frozen and inserts new cross-attention layers between its blocks. Text hidden states form the queries; resampled image tokens form the keys and values. Each new layer's output passes through a tanh gate initialised at zero, so at the start of training the model behaves exactly like the original LLM and the visual pathway fades in.

Q = H_text · W_Q      K = V_img · W_K      V = V_img · W_V
H_text ← H_text + tanh(α) · softmax(Q Kᵀ / √d) · V       # α starts at 0

The trade-off against LLaVA-style input fusion: cross-attention leaves the text context short (image tokens never enter the sequence) and protects the LLM's text skills, but it adds parameters and a more complex architecture. Input-token fusion is simpler and lets every layer of the LLM attend to the image, at the cost of a longer sequence.

Audio tokenisation

Audio is handled in two broad ways. For understanding, an encoder such as Whisper's (Radford et al., 2022) turns a log-mel spectrogram into a sequence of vectors, which are then projected into the LLM exactly as with images. For generation, a neural codec such as EnCodec (Défossez et al., 2022) compresses the waveform into discrete codes using residual vector quantisation: several codebooks, each encoding what the previous ones missed. Those integer codes can then be predicted like text tokens. The same split, continuous features to read and discrete codes to write, reappears with robot actions below.

Early versus late fusion

The terms are used loosely, so be precise about which you mean:

The direction of travel at the frontier is towards models trained natively on several modalities, rather than bolting a frozen encoder onto a finished LLM. Module 05 records that Kimi K3 trained its vision encoder from scratch inside the language model and reported that a pretrained SigLIP-style encoder was unnecessary and less stable. That's a real counterpoint to this stage's recipe. It comes from a lab with trillion-token budgets, though. With a small dataset, reusing a pretrained encoder is still the sensible default.

Interactive: how many tokens does the robot's view cost?

The square is one image cut into patches. Shaded blocks show patches merged by token merging. The stats count tokens across all cameras and history frames, and compare attention cost (which grows with the square of sequence length) to a single 224-pixel image at patch 14.

Patches per side
–
Tokens per image
–
Total vision tokens
–
Attention cost vs 256 tokens
–

Try this: 224 px at patch 14 is 256 tokens. Raise the image to 384 px: 729 tokens. Now set 3 cameras and 2 history frames: 4,374 tokens, and attention costs about 292× the single-image case. Set token merge to 2 and the total drops about 4× (an odd grid loses its last row and column). Then see Stage 08 for what those tokens cost in KV-cache memory.

3. The practice project

(a) Laptop/Colab version: a tiny captioning VLM

Freeze a small SigLIP and a small LLM. Train only the projector. This runs on a free Colab GPU in an hour or two.

  1. Models: google/siglip-base-patch16-224 (196 patch tokens) and HuggingFaceTB/SmolLM2-135M. Read both hidden sizes from their configs; don't hard-code them.
  2. Data: a few thousand image–caption pairs (a slice of COCO Captions or Flickr30k). Keep 500 pairs aside for validation.
  3. Cache the vision features once, since the encoder is frozen. Training then only touches the projector and is fast.
  4. Train the two-layer MLP with AdamW (learning rate around 1e-3), loss on caption tokens only.
  5. Measure validation caption loss under three conditions: the true image, a shuffled image from another example, and no image. The gap between "true" and "shuffled" is the only number that proves the projector passes visual information. Also read 20 generated captions by eye.
import torch, torch.nn as nn
from transformers import AutoModel, AutoModelForCausalLM

vis = AutoModel.from_pretrained("google/siglip-base-patch16-224").vision_model.eval()
lm = AutoModelForCausalLM.from_pretrained("HuggingFaceTB/SmolLM2-135M").eval()
for p in [*vis.parameters(), *lm.parameters()]:
    p.requires_grad = False

dv, dt = vis.config.hidden_size, lm.config.hidden_size
proj = nn.Sequential(nn.Linear(dv, dt), nn.GELU(), nn.Linear(dt, dt))
opt = torch.optim.AdamW(proj.parameters(), lr=1e-3)

def loss_fn(pix, ids, pad_id):
    with torch.no_grad():
        v = vis(pixel_values=pix).last_hidden_state      # [B, 196, dv]
    img = proj(v)                                         # [B, 196, dt]
    txt = lm.get_input_embeddings()(ids)                  # [B, T, dt]
    lab = ids.masked_fill(ids == pad_id, -100)            # no loss on padding
    lab = torch.cat([torch.full(img.shape[:2], -100), lab], 1)  # or on image slots
    return lm(inputs_embeds=torch.cat([img, txt], 1), labels=lab).loss

Don't expect fluent captions from a 135M model. Expect short, roughly topical ones ("a man riding a horse") and a clear loss gap over shuffled images. If the gap is near zero, the LLM is just writing generic captions from its language prior.

(b) The full version

The post's project at full size is LLaVA-1.5: a CLIP or SigLIP encoder at 336–384 px, an MLP projector and a 7B LLM. Stage 1 trains the projector on several hundred thousand caption pairs. Stage 2 unfreezes the LLM for visual instruction tuning. The LLaVA-1.5 authors report about a day of training on a single 8×A100 node. Rented, that's a few hundred dollars per run, before the failed runs. The nanoVLM repo is the middle ground: a complete, readable VLM trainable on one GPU.

For a robotics follow-on, fine-tune OpenVLA on a small task with LoRA, or train a SmolVLA-sized policy with LeRobot on a simulated task. Both are in the course list below.

(c) Pitfalls

4. Reality check

The post says text is only half the internet and the current meta is models that natively reason over vision, audio and text together.

5. In this course

6. Why it matters for robotics

7. Self-check

Why does a small projector work at all, rather than needing a large network?

CLIP and SigLIP features were trained to align with text, so they already encode concepts language talks about. The projector mostly has to rotate and rescale them into the LLM's embedding space, which a linear layer or two-layer MLP can do.

What does SigLIP change compared with CLIP, and why does it help?

It scores each image–text pair with an independent sigmoid instead of a batch-wide softmax. No global normalisation is needed, so large batches are cheaper and small batches work better.

A 384-pixel image with patch size 14, three cameras and two history frames: how many vision tokens?

384/14 rounds down to 27, so 729 tokens per image. 729 × 3 × 2 = 4,374 tokens per control step.

Why does Flamingo initialise its cross-attention gates at zero?

So the model starts out identical to the frozen pretrained LLM. The visual pathway is added gradually and doesn't wreck the language ability at the start of training.

Name two ways a VLA can output actions and one weakness of each.

Discrete action tokens (RT-2, OpenVLA): simple and reuse the LLM head, but slow per-dimension decoding and limited precision from binning. A separate continuous action expert (π0, flow matching): smooth, fast action chunks, but more architecture and a second training objective.

Your captioning VLM writes fluent captions. How do you check it's actually using the image?

Compare validation loss and outputs with the true image against a shuffled image from another example. If they're about the same, the LLM is relying on its language prior.

8. Courses & content

ResourceTypeUse it for
Hugging Face · nanoVLMCode (free)A small VLM: vision encoder + projection + language model, trainable on one GPU
Hugging Face · Vision Language Models ExplainedBlog guideArchitectures and how to fine-tune them
Hugging Face smol-course · Vision Language Models unitCourse (free)Adapting and using multimodal models
LLaVA projectPaper + codeThe classic 'vision encoder + projector + LLM' recipe
Hugging Face · Robotics Course + LeRobotCourse (free)Robotics track: from classical robotics to learned policies on real hardware
OpenVLAPaper + codeRobotics track: a VLM turned into a robot policy that outputs actions

Start with nanoVLM: it's the practice project already built, small enough to read in an evening, and you can swap its projector for your own. Then move to OpenVLA for the robotics side.

Full course map for all 12 stages →

9. Papers & docs