TAI BUI
Back to curriculum
BuildPython

Vision-Language Models — The ViT-MLP-LLM Pattern

A vision encoder turns an image into tokens. A small MLP projector maps those tokens into the LLM's embedding space. The language model does the rest. ViT, MLP, LLM: that's the pattern behind every production VLM in 2026.

TIME: ~75 minutesPREREQUISITES: Phase 4 Lesson 14 (ViT), Phase 4 Lesson 18 (CLIP), Phase 7 Lesson 02 (Self-Attention)

Learning Objectives

  • State the ViT-MLP-LLM architecture and explain what each of the three components contributes
  • Compare Qwen3-VL, InternVL3.5, LLaVA-Next, and GLM-4.6V on parameter count, context length, and benchmark performance
  • Explain DeepStack: why multi-level ViT features tighten vision-language alignment better than a single last-layer feature
  • Measure VLM hallucination in production with Cross-Modal Error Rate (CMER) and act on the signal

The Problem

CLIP (Phase 4 Lesson 18) gives you a shared embedding space for images and text, which is enough for zero-shot classification and retrieval. It still can't answer "how many red cars are in this image?" — CLIP doesn't generate text, it only scores similarities.

A vision-language model (VLM) fixes that by bolting a CLIP-family image encoder onto a full language model. Qwen3-VL, InternVL3.5, LLaVA-Next, and GLM-4.6V all follow this pattern: the model sees an image plus a question and generates an answer. By 2026, open-source VLMs rival or beat GPT-5 and Gemini-2.5-Pro on multimodal benchmarks (MMMU, MMBench, DocVQA, ChartQA, MathVista, OSWorld).

Every one of them is built from the same trio: ViT, projector, LLM. Models differ in which ViT, which projector, which LLM, what training data, and what alignment recipe they use. Learn the pattern once and swapping any component becomes mechanical.

Warm-up0/3
1. A Vision Transformer (ViT) splits an image into fixed-size patches and treats each patch like a word-sized unit. What does it output after processing an image?Warm-up
A grid of equal patches labelled a photo split into equal patches, an arrow into a box labelled ViT processes every patch, above a dashed question mark box asking what comes out the other side for the whole photo.
2. CLIP embeds images and text into the same vector space and scores how well a caption matches an image by comparing the embeddings. What is CLIP built to do?Warm-up
An image box and a caption box, each with an arrow into a shared region holding an image embedding point and a caption embedding point, over a dashed question mark box, with a line asking what CLIP does with the two points once both land in the shared space.
3. Two pretrained networks output vectors of different sizes: 768 numbers from one, while the next network expects 4096. What standard component maps one size to the other?Warm-up
A box labelled 768 numbers, one network's output, an arrow into a dashed question mark box, an arrow into a box labelled 4096 numbers, the next network's input, with a line asking what standard component changes a vector's length like this.

The Concept

The ViT-MLP-LLM architecture

Mermaid diagram

  1. Vision encoder: a pretrained ViT (CLIP-L/14, SigLIP, DINOv3, or a fine-tuned variant). Produces patch tokens.
  2. Projector: a small module, usually a 2-4 layer MLP or a Q-former (a fixed-token-count alternative), that maps vision tokens into the LLM's embedding dimension. This is where most of the fine-tuning happens.
  3. LLM: a decoder-only language model (Qwen3, Llama, Mistral, GLM, InternLM). Reads the vision + text tokens in sequence, generates text.

All three pieces are trainable in principle. In practice, the vision encoder and LLM stay mostly frozen while the projector trains. A few billion parameters end up carrying the whole cross-modal signal, which is cheap.

DeepStack

Vanilla projection keeps only the last ViT layer and throws the rest away. DeepStack (Qwen3-VL) doesn't: it samples features from multiple ViT depths and stacks them. Deeper layers carry high-level semantics; shallower layers carry fine-grained spatial and textural information. Feeding both into the LLM closes the gap between "what does the image contain" (semantics) and "where exactly" (spatial grounding).

Three training stages

Modern VLMs train in stages:

  1. Alignment: freeze ViT and LLM. Train only the projector on image-caption pairs. Teaches the projector to map vision space into language space.
  2. Pre-training: unfreeze everything. Train on large-scale interleaved image-text data (500M+ pairs). Builds the model's visual knowledge.
  3. Instruction tuning: fine-tune on curated (image, question, answer) triples. Teaches conversational behaviour and task formats. This is what turns a "vision-aware LM" into a usable assistant.

Most LoRA fine-tunes target stage 3 with a small labelled dataset.

Model family comparison (early 2026)

A few entries below are Mixture-of-Experts (MoE) models: only a fraction of their total parameters activate for any given token, so the "active" figure, not the total, is what actually drives inference cost.

ModelParamsVision encoderLLMContextStrengths
Qwen3-VL-235B-A22B (MoE)235B (22B active)custom ViT + DeepStackQwen3256KGeneral SOTA, GUI agent
Qwen3-VL-30B-A3B (MoE)30B (3B active)custom ViT + DeepStackQwen3256KSmaller MoE alternative
Qwen3-VL-8B (dense)8Bcustom ViTQwen3128KProduction dense default
InternVL3.5-38B38BInternViT-6BQwen3 + GPT-OSS128KStrong MMBench / MMVet
InternVL3.5-241B-A28B241B (28B active)InternViT-6BQwen3128KCompetitive with GPT-4o
LLaVA-Next 72B72BSigLIPLlama-332KOpen, easy to fine-tune
GLM-4.6V~70BcustomGLM64KOpen-source, strong OCR
MiniCPM-V-2.68BSigLIPMiniCPM32KEdge-friendly

Visual agents

A visual agent is a VLM that operates GUIs directly: desktop, mobile, web. It reads a screenshot, understands the UI, and emits actions (click, type, scroll). Qwen3-VL-235B reaches top global performance on OSWorld, the benchmark built for exactly this. Combined with tools, it closes the loop on common desktop tasks, and it's what most 2026 "AI PC" demos run under the hood.

Agentic capabilities + RoPE variants

VLMs need to know when a frame is in a video. Qwen3-VL evolved from T-RoPE (temporal rotary position embeddings) to text-based time alignment: explicit timestamp text tokens interleaved with video frames. The model sees "<timestamp 00:32> frame, prompt" and can reason about temporal relationships.

The alignment problem

12% of image-text pairs in a crawled dataset contain descriptions not fully grounded in the image. A VLM trained on this silently learns to hallucinate: it fabricates objects, misreads numbers, and invents relationships. In production, this is the dominant failure mode.

Skywork.ai introduced the Cross-Modal Error Rate (CMER) to track it:

CMER = fraction of outputs where the text confidence is high but the image-text similarity (via a CLIP-family checker) is low

High CMER means the model is confidently saying things that aren't grounded in the image. Skywork.ai treated it as a production KPI and cut their hallucination rate by roughly 35%. The fix wasn't fixing the model: it was routing high-CMER outputs to human review.

Fine-tuning with LoRA / QLoRA

Full fine-tuning of a 70B VLM is out of reach for most teams. LoRA (rank 16-64) on attention and projector layers, or QLoRA with 4-bit base weights, fits on a single A100 or H100. Cost: 5,000-50,000 examples, $100-$5,000 in compute, 2-10 hours of training.

Spatial reasoning is still weak

Current VLMs score 50-60% on spatial reasoning benchmarks (above-below, left-right, counting, distance), below human performance. If your use case depends on "which object is on top of which," validate heavily. Better alternatives for pure spatial tasks: a specialised keypoint / pose estimator, a depth model, or a detection model with box geometry post-processed.

Check: The Concept0/22
1. What is DeepStack in Qwen3-VL?Define
A ViT box labelled many layers deep, an arrow into a dashed box labelled DeepStack holding a question mark, an arrow into a projector box labelled then the LLM, with a line saying Qwen3-VL inserts a step it calls DeepStack at this point and asking what it is.
2. What is the ViT-MLP-LLM pattern?Define
A box labelled a photo, an arrow into one dashed question mark box, an arrow into a box labelled a sentence, a dog runs, illustrative, with a line saying the lesson calls this the ViT-MLP-LLM pattern and asking what is in the box and how data moves through it.
3. What is the projector in a vision-language model?Define
A ViT box, an arrow into a highlighted middle box labelled projector holding a question mark, an arrow into an LLM box, with a line saying the lesson calls the middle slot the projector and asking what kind of module goes there and what it does.
4. What does a high Cross-Modal Error Rate (CMER) indicate?Define
A code box showing the lesson's CMER line reading fraction of outputs where, followed by a masked question mark in place of the condition, with lines saying a high CMER means many outputs meet that condition and asking what is true of those outputs.
5. What is a visual agent?Define
A mock desktop window with a title bar, two input rows and a Save button, an arrow into a dashed question mark box, with a line saying the lesson calls a VLM put to work on a screen like this a visual agent and asking what it does.
6. What is a Q-former in a VLM?Define
A box labelled ViT output, N patches varies by image, an arrow into a highlighted Q-former box, an arrow into a dashed question mark box, with a line saying the lesson calls this module a Q-former and asking what it produces for the LLM to read.
7. After the alignment stage trains only the projector, what do the next two VLM training stages do?Define
Three boxes in a row: stage 1 labelled alignment, stage 2 masked with a question mark, and stage 3 masked with a question mark, connected by arrows, asking what the next two stages actually do.
8. In the usual VLM training recipe, which component does the alignment stage train on image-caption pairs?Define
Three boxes in a row, ViT, projector and LLM, each with a question mark instead of a status, with lines saying each part is either frozen or training in this stage and asking which weights alignment updates.
9. Why does a VLM need a projector between the vision encoder and the LLM?Explain
A ViT box, an empty dashed slot labelled nothing here, and an LLM box joined by arrows, with lines saying the ViT's tokens go straight on with no module in between and asking why the lesson says that does not work.
10. Why does adding shallower ViT layers, as DeepStack does, tighten vision-language alignment?Explain
A box labelled a shallow ViT depth and a box labelled a deep ViT depth, each with its contents masked by a question mark, both arrowed down into an LLM box, with a line saying adding the shallow depth tightens alignment and asking why.
11. What does OSWorld measure, and why does a top score matter for visual agents?Explain
A card naming OSWorld and stating that Qwen3-VL-235B reaches top global performance on it, above a dashed question mark box asking what OSWorld is actually testing.
12. Why is the active parameter count, not the total, what drives inference cost for a Mixture-of-Experts VLM?Explain
A bar chart with two bars, total params at 235B and active params at 22B, for the same model, with a line asking which of the two bars inference cost follows and why.
13. How does text-based time alignment tell Qwen3-VL when a video frame occurs?Explain
Four frame boxes in a row, frame 0 through frame 3, above a dashed question mark box, with a line saying Qwen3-VL uses text-based time alignment and asking how that tells it when each frame occurs.
14. Why does training on crawled image-text pairs push a VLM toward hallucination?Explain
A flow of three boxes, crawled pairs of image plus text from the web, VLM training on those pairs, and production where it hallucinates, above a dashed question mark box, with a line asking what about the training data pushes the model that way.
15. Once CMER flags confident but poorly grounded outputs in production, what did Skywork.ai do with them?Explain
A box labelled flagged output, confident, poorly grounded, an arrow into a dashed question mark box, with a line asking what Skywork.ai did with outputs like this, per the lesson.
16. How do current VLMs compare with humans on spatial reasoning (above-below, left-right, counting, distance)?Explain
A full-width human performance reference bar above a VLM score bar left empty as a dashed outline of the same width with a question mark inside, with a line asking where the VLM bar lands on spatial questions like above-below and counting.
The human bar is drawn full width as a reference; the lesson gives no exact human figure.
17. Using the model family table, which model pairs the longest context window with a Mixture-of-Experts design?Apply
The lesson's own 8-row model family table reduced to model name, context length and design, no row highlighted, with a line asking which row pairs the longest context with a Mixture-of-Experts design.
18. CMER on a VLM endpoint climbs for one customer's images while the overall figure stays flat. What does the lesson's guidance imply?Analyze
A line chart over time with an overall CMER line staying roughly flat and a customer X CMER line climbing steadily, above a dashed question mark box, with a line asking what the lesson's guidance implies about customer X given the flat overall figure.
The two curves are illustrative shapes; the lesson gives no CMER values.
19. A VLM transcribes receipts and reads charts fluently, but a warehouse team finds it often gets 'which box is behind which' wrong. What is the most likely explanation?Analyze
A card stating the same VLM transcribes receipts well, reads charts well, but is often wrong about which box is behind which, above a dashed question mark box asking for the most likely explanation.
20. A team trains only the projector on image-caption pairs. The model describes images but ignores question formats and does not hold a conversation. Which stage is missing?Analyze
A card stating only the alignment stage ran, the projector trained on image-caption pairs, and the result describes images but ignores question format and holds no conversation, above a dashed question mark box asking which stage is missing.
21. A team has one A100, 20,000 labelled domain examples, and wants to adapt a 70B VLM. Which plan fits?Design
A card stating the hardware is one A100, the data is 20,000 labelled domain examples, and the model is a 70B VLM, above a dashed question mark box asking which fine-tuning plan fits.
22. Your product must report which object sits on top of which in factory photos, and errors are costly. Which design fits?Design
A card stating the task is to report which object sits on top of which in factory photos and errors are costly, above a dashed question mark box asking which family of model fits.

Build It

Step 1: The projector

The part you will train most often. 2-4 layer MLP with GELU.

python
import torch
import torch.nn as nn
 
 
class Projector(nn.Module):
    def __init__(self, vit_dim=768, llm_dim=4096, hidden=4096):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(vit_dim, hidden),
            nn.GELU(),
            nn.Linear(hidden, llm_dim),
        )
 
    def forward(self, x):
        return self.net(x)

Input is a (N_patches, d_vit) token tensor. Output is (N_patches, d_llm). The LLM treats every output row as just another token.

Step 2: Assemble ViT-MLP-LLM end-to-end

Skeleton of the forward pass for a minimal VLM. Real code uses transformers; this is the conceptual layout.

python
class MinimalVLM(nn.Module):
    def __init__(self, vit, projector, llm, image_token_id):
        super().__init__()
        self.vit = vit
        self.projector = projector
        self.llm = llm
        self.image_token_id = image_token_id  # placeholder token in text prompt
 
    def forward(self, image, input_ids, attention_mask):
        # 1. vision features
        vision_tokens = self.vit(image)                     # (B, N_patches, d_vit)
        vision_embeds = self.projector(vision_tokens)       # (B, N_patches, d_llm)
 
        # 2. text embeddings
        text_embeds = self.llm.get_input_embeddings()(input_ids)  # (B, M, d_llm)
 
        # 3. replace image placeholder tokens with vision embeds
        merged = self._merge(text_embeds, vision_embeds, input_ids)
 
        # 4. run LLM
        return self.llm(inputs_embeds=merged, attention_mask=attention_mask)
 
    def _merge(self, text_embeds, vision_embeds, input_ids):
        out = text_embeds.clone()
        expected = vision_embeds.size(1)
        for b in range(input_ids.size(0)):
            positions = (input_ids[b] == self.image_token_id).nonzero(as_tuple=True)[0]
            if len(positions) != expected:
                raise ValueError(
                    f"batch item {b} has {len(positions)} image tokens but vision_embeds has {expected} patches."
                    " Every sample in the batch must be pre-padded to the same number of image placeholder tokens.")
            out[b, positions] = vision_embeds[b]
        return out

The <image> placeholder token in the text gets replaced with real image embeddings. LLaVA, Qwen-VL, and InternVL all use this same pattern.

Step 3: CMER computation

A lightweight runtime check.

python
import torch.nn.functional as F
 
 
def cross_modal_error_rate(image_emb, text_emb, text_confidence, sim_threshold=0.25, conf_threshold=0.8):
    """
    image_emb, text_emb: embeddings of image and generated text (normalised internally)
    text_confidence:     mean per-token probability in [0, 1]
    Returns:             fraction of high-confidence outputs with low image-text alignment
    """
    image_emb = F.normalize(image_emb, dim=-1)
    text_emb = F.normalize(text_emb, dim=-1)
    sim = (image_emb * text_emb).sum(dim=-1)        # cosine similarity
    high_conf_low_sim = (text_confidence > conf_threshold) & (sim < sim_threshold)
    return high_conf_low_sim.float().mean().item()

Treat CMER as a production KPI. Monitor it per endpoint, per prompt type, per customer. Rising CMER indicates the model is starting to hallucinate on some input distribution.

Step 4: Toy VLM classifier (runnable)

This demonstrates that the projector actually trains: fake "ViT features" go in, and a tiny LLM-style token predicts a class.

python
class ToyVLM(nn.Module):
    def __init__(self, vit_dim=32, llm_dim=64, num_classes=5):
        super().__init__()
        self.projector = Projector(vit_dim, llm_dim, hidden=64)
        self.head = nn.Linear(llm_dim, num_classes)
 
    def forward(self, vision_tokens):
        projected = self.projector(vision_tokens)
        pooled = projected.mean(dim=1)
        return self.head(pooled)

Fit it on synthetic (feature, class) pairs and it converges in under 200 steps, enough to show the projector pattern works.

Check: Build It0/6
1. What is an image token in a VLM prompt?Define
A code box showing input_ids built from tokenizing the words describe, an image token, and the scene, above a dashed question mark box, with a line asking what the image token is and what the model does with it.
2. Projector(vit_dim=768, llm_dim=4096, hidden=4096) receives a patch tensor of shape (196, 768). What shape does it return?Apply
A code box showing patch_tokens has shape 196 by 768, out is assigned from calling the projector on patch_tokens, and a highlighted line asking what out.shape equals.
3. In MinimalVLM's _merge method, what happens if a batch item's number of <image> placeholder tokens does not match the number of vision patches?Apply
A code box finding the image placeholder positions for one batch item, a condition checking whether their count differs from expected, and a masked line, asking what the masked line actually does.
4. cross_modal_error_rate runs with default thresholds on four outputs, given as (text_confidence, cosine similarity): (0.90, 0.31), (0.95, 0.18), (0.60, 0.10), (0.85, 0.20). What does it return?Apply
A table of four rows with confidence and similarity values 0.90/0.31, 0.95/0.18, 0.60/0.10, 0.85/0.20, each row's counts column masked with a question mark, with a line giving the thresholds confidence greater than 0.8 and similarity less than 0.25 and asking what fraction counts.
5. ToyVLM(vit_dim=32, llm_dim=64, num_classes=5) gets vision_tokens of shape (8, 16, 32). What shape does forward return?Apply
A code box showing vision_tokens shaped 8 by 16 by 32, projected shaped 8 by 16 by 64 after the projector, pooled shaped 8 by 64 after mean over dim 1, and a highlighted final return line asking what head of pooled equals.
6. A batch fails with 'batch item 2 has 196 image tokens but vision_embeds has 256 patches'. What is the likely cause?Analyze
A terminal box showing the error batch item 2 has 196 image tokens but vision_embeds has 256 patches, above a dashed question mark box, with a line asking what went wrong on item 2.

Use It

Three ways production teams use VLMs in 2026:

  • Hosted API: OpenAI Vision, Anthropic Claude Vision, Google Gemini Vision. Zero infra, vendor risk.
  • Open-source self-host: Qwen3-VL or InternVL3.5 via transformers and vllm. Full control, higher up-front effort.
  • Fine-tune on domain: load Qwen2.5-VL-7B or LLaVA-1.6-7B, LoRA on 5k-50k custom examples, serve with vllm or TGI.
python
from transformers import AutoProcessor, AutoModelForVision2Seq
import torch
from PIL import Image
 
model_id = "Qwen/Qwen3-VL-8B-Instruct"
processor = AutoProcessor.from_pretrained(model_id)
model = AutoModelForVision2Seq.from_pretrained(model_id, torch_dtype=torch.bfloat16, device_map="auto")
 
messages = [{
    "role": "user",
    "content": [
        {"type": "image", "image": Image.open("plot.png")},
        {"type": "text", "text": "What does this chart show?"},
    ],
}]
inputs = processor.apply_chat_template(messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt").to("cuda")
generated = model.generate(**inputs, max_new_tokens=256)
answer = processor.decode(generated[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True)

apply_chat_template hides the <image> placeholder tokenisation; the model handles the merge internally.

Ship It

This lesson produces:

  • outputs/prompt-vlm-selector.md: picks Qwen3-VL / InternVL3.5 / LLaVA-Next / API given accuracy, latency, context length, and budget.
  • outputs/skill-cmer-monitor.md: emits the code to instrument a production VLM endpoint with cross-modal error rate, per-endpoint dashboards, and alerting thresholds.
Author's Note

The failure mode that actually bites people in production isn't picking the wrong VLM. It's assuming the VLM you picked can do things it fundamentally can't. Spatial reasoning is the one that gets me every time — a model that reads a chart, transcribes a receipt, and describes a scene in fluent prose feels like it should also be able to tell you which box is behind which. It usually can't, not reliably, and its confidence in the wrong answer looks identical to its confidence in a right one.

That's the whole reason CMER exists. Not to make the model smarter, but to catch the gap between what it says and what's actually in the frame before a customer does. If you're shipping a VLM feature, budget the CMER-style check before you budget the fine-tune.

Exercises

  1. (Easy) Run three prompts ("what is this?", "count the objects", "describe the scene") through any open VLM on five images. Score each answer as correct / partially correct / hallucinated by hand. Compute a first-pass CMER-like rate.
  2. (Medium) Fine-tune Qwen2.5-VL-3B or LLaVA-1.6-7B with LoRA (rank 16) on 500 images of a target domain with captions. Compare zero-shot vs fine-tuned MMBench-style accuracy.
  3. (Hard) Replace the VLM's image encoder with DINOv3 instead of its default SigLIP/CLIP. Re-train only the projector (frozen LLM + frozen DINOv3). Measure whether dense-prediction tasks (counting, spatial reasoning) improve.

Key Terms

TermWhat people sayWhat it actually means
ViT-MLP-LLM"The VLM pattern"Vision encoder + projector + language model; every 2026 VLM
Projector"The bridge"2-4 layer MLP (or Q-former) that maps vision tokens into LLM embedding space
DeepStack"Qwen3-VL feature trick"Multi-level ViT features stacked rather than last-layer only
Image token" placeholder"Special token in the text stream replaced by projected vision embeddings
CMER"Hallucination KPI"Cross-Modal Error Rate; high when text confidence is high but image-text similarity is low
Visual agent"VLM that clicks"VLM operating GUIs (OSWorld, mobile, web) with tool calls
Q-former"Fixed-count token bridge"BLIP-2 style projector producing a fixed number of visual query tokens
Alignment / pre-training / instruction tuning"Three stages"Standard VLM training pipeline

Further Reading