Personalize this lesson
Adapt explanations and teaching visuals to your background and preferred voice.
LLM-powered search turned retrieved evidence into cited answers. Vision-language models add visual evidence to that same loop: images, diagrams, screenshots, document pages, and asset photos become searchable and reasoned about alongside text.
Vision-language models connect images and text so systems can search, classify, and reason across both modalities. Start with Contrastive Language-Image Pre-training (CLIP), then expand to production uses and failure modes.
A data-center inspection system shouldn't learn rack hazards only from a fixed list of labels like rack_hazard or safe_rack. It needs real photos paired with descriptions: "blocked vent on rack door," "asset tag torn near barcode," or "warning label visible on power unit." The system learns the concept from images and language together.
Product brief: data-center visual inspection
Design a service that lets technicians upload rack photos, search past inspections in natural language, and route uncertain hazards to a reviewer. CLIP or Sigmoid Loss for Language-Image Pre-training (SigLIP) powers first-pass retrieval; optical character recognition (OCR), grounding, or a generative VLM handles questions that need label text, regions, or explanations.
Requirements and API
POST /v1/inspection-imagesaccepts an immutable image reference, site, rack, capture time, and idempotency key. It returnsimage_id, ingestion state, and encoder version.POST /v1/inspection-searchacceptsquery, authorized site filters,limit, and an optional minimum reviewed score. It returns rankedimage_idvalues, similarity scores, thumbnails, and review state. Scores are ranking signals, not probabilities.POST /v1/inspection-analysisacceptsimage_id,question, and requested evidence type. It returnsanswer, OCR spans or grounded regions, model versions, andanswered,abstained, orreview.- Search targets p95 below 300ms. Analysis targets p95 below 3 seconds. Access filters apply before retrieval, and a hazard never auto-closes from an unreviewed model result.
Data flow and sizing
Ingestion follows upload -> malware and format checks -> metadata authorization -> image encoder -> normalized embedding -> versioned vector index. Search follows authorized query -> text encoder -> site-filtered nearest-neighbor search -> optional reranker -> response. Analysis adds crop or tile policy -> OCR or VLM -> grounding check -> review policy after the selected image is authorized.
For 10 million images and 768-dimensional FP16 embeddings, raw vectors need 10,000,000 x 768 x 2 = 15.36 GB before index structures, metadata, replicas, and old encoder versions. If HNSW or IVF structures, IDs, and filters take the measured working set to 2 to 4 times raw vectors, one index generation needs roughly 31 to 61 GB RAM. Replicas and dual generations multiply that figure again, so measure the selected ANN library before setting a 40 to 80 GB host budget. At 100 QPS search, also size the text-encoder plus ANN query path as its own pool so graph probes and encoder GPUs don't compete with the separately sized generative-analysis pool.
Recovery, rollout, and evaluation
Ingestion is idempotent by content hash and source version. Failed images enter a retry or dead-letter state without becoming searchable. An encoder upgrade writes a new index generation; reads stay on the old complete generation until backfill and validation finish, then an atomic alias switch enables rollback. If OCR or grounding fails, return abstained or review rather than a caption presented as verified evidence.
Roll out with a frozen, technician-reviewed set, shadow traffic, a site-limited canary, and staged index switching. Block expansion when asset retrieval recall@10, hazard false-negative rate, asset-tag OCR accuracy, grounding IoU, p95 latency, review rate, or cross-site access tests miss their route-specific bars. Monitor drift by site, camera, lighting, rack type, and prompt template instead of relying on one aggregate benchmark.
Many supervised computer vision systems expose a fixed label inventory: recognizing a new category generally requires labeled examples and another training step. CLIP showed that image-text matching at web scale can produce representations that transfer to new text-defined classes without training a new classifier head.[1] That made practical open-vocabulary retrieval possible and provided useful vision features for later generative systems.
Conceptual foundation
A quick refresher: embeddings and similarity
Before CLIP, recall what embeddings are: high-dimensional vectors that compress meaning into a list of numbers. A sentence like "blocked vent on rack door" becomes a vector, perhaps 768 numbers long. An image of that same blocked rack vent becomes another 768-number vector.
If the model has done its job, those two vectors point in roughly the same direction in space. We measure that alignment with cosine similarity, a score between -1 and 1. Within one trained model, a higher score ranks an image-text pair as more aligned than a lower-scoring candidate. It isn't a calibrated probability and doesn't prove that the pair means exactly the same thing. That's the geometric heart of CLIP: it learns to rank matching image-text pairs above mismatches.
What does cosine similarity measure in CLIP-style systems?
Answer
It measures whether an image embedding and a text embedding point in a similar learned direction. Use it to rank candidate matches; don't treat it as a calibrated probability or proof that a caption is correct.
From fixed labels to open vocabulary
A fixed label list can teach that one class is rack_hazard, but it can't show how blocked airflow appears across lighting, angles, cables, and rack labels.
Learning from many real inspection images, UI screenshots, diagrams, and captions gives Vision-Language Models (VLMs) a broader signal. Instead of memorizing fixed categories, they learn to align the visual concept of an object with its natural language description.
This shared image-text space allows a system to score text prompts that were not fixed training labels. Transfer still depends on whether pre-training learned the relevant visual evidence and whether the new domain resembles the data it saw. A data-center team must validate prompts on its own racks, lighting, cabling, and review policy before using scores operationally.
Why is open-vocabulary classification different from fixed-label classification?
Answer
Fixed-label classifiers choose from labels baked into training. CLIP-style models can score new text-defined classes without training a new head; launching a new class still requires validation and a threshold or review policy.
Shared image-text space
CLIP creates a shared routing map for vision and language. One side starts with pixels, the other with words. They don't share raw input format, but the original model was trained on 400 million image-text pairs.[1] Over training, matched images and captions receive higher similarity than mismatches. If its training produced useful rack-safety features, it may rank a new prompt such as "blocked airflow behind rack panel" well even when that exact label was not a training class. That's a transfer hypothesis to evaluate, not an automatic guarantee.
What is the shared image-text space buying you?
Answer
It gives image and text encoders a common coordinate system for ranking pairs. That enables retrieval or text-defined classification without retraining a new label head for every candidate task, subject to domain validation.
CLIP: contrastive language-image pre-training
Architecture[1]
CLIP jointly trains an image encoder and text encoder to align visual and textual representations in a shared embedding space. In the original paper, the vision side was either a ResNet (Residual Network) or a ViT (Vision Transformer), while the text side was a Transformer that produced one representation for each caption.[1] The contrastive objective pulls matching pairs together and pushes mismatches apart, which is what makes zero-shot transfer and cross-modal retrieval work.

The illustration above shows core CLIP idea: keep encoders separate, normalize both outputs, then judge whether image and text land near each other in one shared space.
Why is CLIP called a dual-encoder model?
Answer
It has one encoder for images and one encoder for text. Both produce embeddings in the same dimension, and the model scores image-text pairs by similarity rather than by decoding text.
Training objective
The training process resembles matching data-center photos to their correct captions. For every image, the model must pick the correct caption out of thousands of incorrect ones.
A tiny batch makes the pattern easier to see with real numbers. Suppose you have 3 images and 3 captions:
| Image | Caption |
|---|---|
| A | "cable label torn near port" |
| B | "asset tag partly covered by tape" |
| C | "warning label visible on power unit" |
There are possible pairings. Only the 3 diagonal pairs (A+A, B+B, C+C) are correct. The other 6 are mismatches. CLIP's job is to make the similarity scores for the diagonal pairs as high as possible, while keeping the off-diagonal scores low.

In a real training run, the batch size is much larger (32,768 in the original paper[1]), so each image sees thousands of negative examples. But the logic is the same: maximize the similarity of matching pairs and minimize it for non-matching pairs using a symmetric contrastive loss that's often described as InfoNCE-style (Information Noise Contrastive Estimation).
Let and be the L2-normalized image and text embeddings. CLIP computes logits
where is a learned logit-scale parameter. You'll also see this written as division by a temperature , where . The symmetric objective is:
The first equation asks: "Given image , what's the probability that caption is the right one?" It compares the matching score against the sum of all scores for that image across every caption in the batch. From the text side, the second equation repeats the same comparison in the other direction. The final loss averages both directions so neither encoder dominates.
In a batch of 3 images and 3 captions, which pairs should the contrastive loss reward?
Answer
Only the 3 diagonal image-caption pairs are positives. The other 6 off-diagonal pairs are negatives. The loss raises diagonal similarity and lowers mismatched similarities in both image-to-text and text-to-image directions.
This implementation uses a simplified fixed-temperature version of that loss. It keeps the math readable while matching the same symmetric training pattern.
1import math
2
3def normalize(vector: list[float]) -> list[float]:
4 length = math.sqrt(sum(value * value for value in vector))
5 return [value / length for value in vector]
6
7def dot(left: list[float], right: list[float]) -> float:
8 return sum(a * b for a, b in zip(left, right, strict=True))
9
10def cross_entropy(logits: list[float], target: int) -> float:
11 max_logit = max(logits)
12 log_sum_exp = max_logit + math.log(sum(math.exp(x - max_logit) for x in logits))
13 return -logits[target] + log_sum_exp
14
15def clip_loss(
16 image_embeds: list[list[float]],
17 text_embeds: list[list[float]],
18 temperature: float = 0.2,
19) -> tuple[float, list[list[float]]]:
20 images = [normalize(vector) for vector in image_embeds]
21 texts = [normalize(vector) for vector in text_embeds]
22 logits = [
23 [dot(image, text) / temperature for text in texts]
24 for image in images
25 ]
26
27 loss_i2t = sum(cross_entropy(row, i) for i, row in enumerate(logits)) / len(logits)
28 columns = [list(column) for column in zip(*logits, strict=True)]
29 loss_t2i = sum(cross_entropy(column, i) for i, column in enumerate(columns)) / len(columns)
30 return (loss_i2t + loss_t2i) / 2, logits
31
32image_embeds = [
33 [0.95, 0.05, 0.00],
34 [0.05, 0.92, 0.03],
35 [0.00, 0.07, 0.94],
36]
37text_embeds = [
38 [0.91, 0.08, 0.01],
39 [0.06, 0.89, 0.04],
40 [0.02, 0.09, 0.90],
41]
42
43loss, logits = clip_loss(image_embeds, text_embeds)
44print("logit matrix:")
45for row in logits:
46 print([round(value, 2) for value in row])
47print(f"symmetric_loss: {loss:.3f}")1logit matrix:
2[5.0, 0.6, 0.14]
3[0.71, 5.0, 0.66]
4[0.09, 0.59, 5.0]
5symmetric_loss: 0.022Low loss means the diagonal image-caption pairs dominate the off-diagonal mismatches. A random or poorly aligned batch would have a much flatter matrix and a higher loss.
The softmax-based cross-entropy requires the full similarity matrix, which means CLIP benefits from very large batch sizes (32,768 in the original paper[1]) so that each example sees many in-batch negatives. In practice, that pushes training toward large distributed runs with expensive cross-device synchronization.
Why does original CLIP benefit from very large batch sizes?
Answer
The contrastive loss uses in-batch negatives. Larger batches give each image many more wrong captions to push away, but they also require a large similarity matrix and cross-device synchronization.
An off-diagonal pair is treated as negative by this objective, but it isn't automatically a clean semantic negative. Two photographs of the same rack hazard may be valid matches even if they arrived as separate labeled pairs. A batch-construction audit catches obvious collisions before the loss pushes them apart.
1from dataclasses import dataclass
2
3@dataclass(frozen=True)
4class Pair:
5 image_id: str
6 caption: str
7 concept: str
8
9pairs = [
10 Pair("rack-front", "blocked vent on rack door", "blocked-airflow"),
11 Pair("rack-side", "airflow blocked by loose cabling", "blocked-airflow"),
12 Pair("asset-tag", "barcode obscured by cable tie", "blocked-barcode"),
13]
14
15potential_false_negatives = [
16 (left.image_id, right.image_id, left.concept)
17 for index, left in enumerate(pairs)
18 for right in pairs[index + 1:]
19 if left.concept == right.concept
20]
21
22print("pairs_treated_as_negative_but_related:", potential_false_negatives)1pairs_treated_as_negative_but_related: [('rack-front', 'rack-side', 'blocked-airflow')]Training data
The success of CLIP is heavily dependent on the sheer scale and diversity of its pre-training data. The original paper doesn't release or assign a canonical public name to the full corpus. It describes training on 400 million (image, text) pairs collected from the internet.[1]
Unlike prior vision datasets (e.g., ImageNet) that relied on manual class labels, CLIP learns from natural language supervision attached to images on the web. That means the model trains against captions and descriptive text rather than a closed vocabulary of hand-assigned categories. That supervision gives CLIP a much broader representation of visual concepts and a much stronger zero-shot transfer story.
Zero-shot classification
CLIP enables classification without any task-specific training. Instead of a final classification layer with fixed weights, the model computes the similarity between the image and the text descriptions of possible classes.
Here's a concrete example. Suppose you have a data-center rack photo and you want to classify it into one of three categories:
| Class | Prompt | Similarity |
|---|---|---|
| rack hazard | "a photo of a rack hazard" | 0.87 |
| safe rack | "a photo of a safe rack" | 0.12 |
| blocked asset tag | "a photo of a blocked asset tag" | 0.41 |
The highest score is the model's top candidate. In a real decision path, it should win only if validation has established an acceptable score or margin policy; ambiguous images should abstain or route to review. You did not retrain the model, but you still need to calibrate the decision rule.
The practical breakthrough isn't classification alone; it's a text-defined candidate vocabulary. A data-center inspection system can test a prompt such as "cable label partially obscured by a tie" without fitting a new label head. Whether the prompt is reliable on real claims still has to be measured.
What changes between training a new classifier head and doing CLIP zero-shot classification?
Answer
A classifier head learns fixed class weights from labeled examples. CLIP zero-shot classification embeds natural-language class prompts and compares them to the image embedding, so the class set can change at inference time.

This function shows the standard zero-shot pattern: prompt the candidate classes in natural language, encode image and text, normalize both, then score them with a scaled dot product. This standalone version uses small hand-written vectors so the inference mechanics are visible.
1import json
2import math
3
4def normalize(vector: list[float]) -> list[float]:
5 length = math.sqrt(sum(value * value for value in vector))
6 return [value / length for value in vector]
7
8def dot(left: list[float], right: list[float]) -> float:
9 return sum(a * b for a, b in zip(left, right, strict=True))
10
11def softmax(values: list[float]) -> list[float]:
12 max_value = max(values)
13 exp_values = [math.exp(value - max_value) for value in values]
14 total = sum(exp_values)
15 return [value / total for value in exp_values]
16
17def zero_shot_classify(
18 image_embed: list[float],
19 class_names: list[str],
20 text_embeds: dict[str, list[float]],
21 logit_scale: float = 12.0,
22) -> dict[str, object]:
23 image = normalize(image_embed)
24 prompts = [
25 f"a photo of {'an' if name[0] in 'aeiou' else 'a'} {name}"
26 for name in class_names
27 ]
28 logits = [
29 logit_scale * dot(image, normalize(text_embeds[prompt]))
30 for prompt in prompts
31 ]
32 probs = softmax(logits)
33 best_index = max(range(len(probs)), key=probs.__getitem__)
34 return {
35 "predicted_class": class_names[best_index],
36 "scores": {
37 class_name: round(probability, 3)
38 for class_name, probability in zip(class_names, probs, strict=True)
39 },
40 }
41
42text_embeds = {
43 "a photo of a rack hazard": [0.92, 0.20, 0.05],
44 "a photo of a safe rack": [0.18, 0.88, 0.08],
45 "a photo of a blocked asset tag": [0.48, 0.31, 0.81],
46}
47
48result = zero_shot_classify(
49 image_embed=[0.89, 0.24, 0.08],
50 class_names=["rack hazard", "safe rack", "blocked asset tag"],
51 text_embeds=text_embeds,
52)
53print(json.dumps(result, indent=2))1{
2 "predicted_class": "rack hazard",
3 "scores": {
4 "rack hazard": 0.988,
5 "safe rack": 0.001,
6 "blocked asset tag": 0.01
7 }
8}Prompt engineering matters
The format of the text prompt affects performance. "A photo of a {class}" typically works better than just "{class}".[1]
Ensembling multiple prompts can systematically improve the stability of zero-shot classifications. The snippet below takes a hardcoded list of templates as input to format the target class description in various contexts. By tokenizing and passing each variation through the text encoder and averaging the resulting embeddings, the procedure outputs a single, more stable vector representation that mitigates the variance of any individual prompt template.
Why does prompt ensembling improve zero-shot classification?
Answer
Different templates emphasize slightly different contexts. Averaging normalized embeddings across templates reduces sensitivity to one brittle phrase and creates a more stable class representation.
1import json
2import math
3
4def normalize(vector: list[float]) -> list[float]:
5 length = math.sqrt(sum(value * value for value in vector))
6 return [value / length for value in vector]
7
8def mean_vector(vectors: list[list[float]]) -> list[float]:
9 return [
10 sum(vector[i] for vector in vectors) / len(vectors)
11 for i in range(len(vectors[0]))
12 ]
13
14prompt_vectors = {
15 "a photo of a rack hazard": [0.92, 0.20, 0.05],
16 "a picture of a rack hazard": [0.88, 0.24, 0.07],
17 "an image showing rack hazard": [0.86, 0.28, 0.08],
18 "a rack hazard in the wild": [0.82, 0.31, 0.12],
19}
20
21ensemble = normalize(mean_vector([
22 normalize(vector)
23 for vector in prompt_vectors.values()
24]))
25
26print(json.dumps({
27 "class": "rack hazard",
28 "templates": len(prompt_vectors),
29 "ensemble_embedding": [round(value, 3) for value in ensemble],
30}, indent=2))1{
2 "class": "rack hazard",
3 "templates": 4,
4 "ensemble_embedding": [
5 0.955,
6 0.284,
7 0.088
8 ]
9}Prompt ensembling reduces wording sensitivity; it doesn't create a deployment threshold. Add an abstention rule using held-out data, and make uncertain classifications visible to downstream policy.
1def decide(scores: dict[str, float], min_score: float, min_margin: float) -> str:
2 ranked = sorted(scores.items(), key=lambda item: item[1], reverse=True)
3 (best_label, best_score), (_, second_score) = ranked[:2]
4 if best_score < min_score or best_score - second_score < min_margin:
5 return "review"
6 return best_label
7
8cases = {
9 "clear_hazard": {"rack_hazard": 0.87, "safe_rack": 0.12, "tag_blocked": 0.31},
10 "ambiguous_panel": {"rack_hazard": 0.56, "safe_rack": 0.53, "tag_blocked": 0.18},
11}
12
13for name, scores in cases.items():
14 print(name, decide(scores, min_score=0.60, min_margin=0.10))1clear_hazard rack_hazard
2ambiguous_panel reviewLimitations of CLIP
A common deployment mistake is using CLIP's global similarity score to count assets from data-center rack photos or localize blocked vents. Standard CLIP inference exposes a pooled image representation optimized for image-text matching, not calibrated counts or boxes. For counting and grounded localization, validate a detector such as Grounding DINO[2] or a grounding-aware VLM instead of interpreting global similarity as a region prediction.
CLIP was an important advance in vision-language alignment, but its contrastive objective and standard scoring interface impose specific constraints. Because zero-shot scoring compares one pooled image representation with one text representation, the output doesn't expose region coordinates or counting evidence. Understanding that boundary matters before using it in production systems.
Why is CLIP bad at counting and exact localization?
Answer
Its standard zero-shot interface scores one pooled image representation against one text representation. That output isn't supervised to return region coordinates, counts, or relations that detection or grounding models preserve.
No generation. CLIP can only classify or retrieve; it can't generate text captions or new images on its own.
- Symptom: You ask CLIP to "describe this image" and it gives you a similarity score instead of a sentence.
- Cause: CLIP is a dual-encoder alignment model, not a decoder. It has no language-generation head.
- Fix: Use a generative VLM like LLaVA (Large Language-and-Vision Assistant) for captioning, or use CLIP for retrieval and a separate LLM for generation.
Compositional understanding can be weak. For example, CLIP benchmarks show failures when concepts are rearranged into different relations, such as "a red toolbox beside a blue safety cone" versus "a blue toolbox beside a red safety cone."
- Symptom: Zero-shot accuracy drops sharply on tasks that require understanding spatial relationships between multiple objects.
- Cause: The training objective only asks "does this caption match this image overall?" It doesn't supervise exact object positions or relations.
- Fix: For spatial reasoning, use grounding-aware models or add a specialized detection head like Grounding DINO.
Fine-grained tasks need separate validation. Standard pooled scoring is a poor contract for counting ("how many racks") or exact spatial reasoning because it doesn't output region-level evidence.
- Symptom: A team maps a top similarity score to "three rack hazards," but a labeled audit shows five.
- Cause: The deployed scoring path returns global alignment rather than count-supervised region predictions.
- Fix: Use region-based models or add an explicit counting module.
Text in images is brittle. CLIP shows non-trivial OCR transfer, especially on rendered text, but the paper reports highly variable performance across text domains and formats.[1] Don't treat a pooled similarity score as a document-OCR interface.
- Symptom: CLIP misses label numbers on server racks or misreads stylized logos.
- Cause: Standard CLIP scoring was not trained as a reliable text-transcription contract, and OCR transfer changes with the text domain and format.
- Fix: For document OCR, use specialized document models or preprocessing pipelines with explicit OCR stages.
Bias. It inherits biases from web-crawled data, sometimes associating certain visual concepts with stereotypes.[3]
- Symptom: Certain demographic groups or contexts get systematically lower similarity scores for neutral prompts.
- Cause: The training data reflects internet distributions, which include skewed representations.
- Fix: Audit embeddings on representative data before deployment; use debiasing techniques or domain-specific fine-tuning.
When is CLIP the wrong tool even if it recognizes the concept?
Answer
Use a different tool when you need generated captions, object counts, bounding boxes, exact OCR, spatial relations, or safety-critical decisions that require calibrated, audited behavior.
Vision encoder architectures
The vision encoder is the foundation of any VLM. Its job is to compress high-dimensional pixels into useful features. This transformation should preserve task-critical details like shape, color, and object relationships while discarding irrelevant noise. Architecture choice affects scalability and fine-grained spatial fidelity, but training data, objective, resolution, and connector design matter too.
Vision Transformer (ViT)[4]
While earlier models used Convolutional Neural Networks (CNNs), like ResNet, many modern published VLMs use Vision Transformers (ViT). ViT treats images as sequences of patches, processing them with a standard Transformer encoder, much like Large Language Models (LLMs) process text tokens. This visual illustrates this process: an input image is sliced into fixed-size patches, linearly projected into embeddings, and fed through a Transformer stack to produce a sequence of contextualized visual tokens.

Patch tokenization
A 224×224 image divided into 16×16 patches results in visual tokens, plus a special [CLS] token used to aggregate the global image representation.
How many patch tokens does a 224x224 image produce with 16x16 ViT patches?
Answer
patches per side, so patch tokens, plus an optional [CLS] token depending on the implementation.
Patch size is a token-budget choice. The example above uses 16x16 patches; the high-resolution calculation below uses 14x14 patches. Compute the budget from the actual tower rather than copying a memorized number.
1def patch_tokens(height: int, width: int, patch_size: int, crops: int = 1) -> int:
2 assert height % patch_size == 0 and width % patch_size == 0
3 return (height // patch_size) * (width // patch_size) * crops
4
5for label, size, crops in [("thumbnail", 224, 1), ("page_crop", 336, 1), ("tiled_page", 336, 4)]:
6 print(label, patch_tokens(size, size, patch_size=14, crops=crops))1thumbnail 256
2page_crop 576
3tiled_page 2304ViT vs. ResNet
| Feature | Vision Transformer (ViT) | ResNet (CNN) |
|---|---|---|
| Scaling | Scales well with sufficient data; attention cost grows with patch-token count. | Strong convolutional baseline with different compute and inductive-bias trade-offs. |
| Context Window | Global context from the first layer via self-attention across all patches. | Local context initially, building to global context only in deep layers. |
| Inductive Bias | Low (treats image as sequence of patches), requiring more data to learn structure. | High (locality and translation-equivariance biases), often requiring less data to train from scratch. |
| Architecture | Transformer-shaped token sequence is convenient for many connectors. | Also usable as a vision tower; connector still maps its features to the downstream task. |
SigLIP[5]
A scaling cost in standard CLIP training is the softmax-based contrastive loss over image-text candidates in an effective batch. Original CLIP used a batch size of 32,768, which required a distributed setup; smaller implementations can still train, with a different accuracy and negative-diversity trade-off.[1]
Instead of asking "Which of these 32,000 captions is the best match?" (a global multiple-choice question), SigLIP (Sigmoid Loss for Language-Image Pre-training) asks "Is this specific image and caption a good match, yes or no?" (a local true/false question). It replaces the softmax-based contrastive loss with a pairwise sigmoid loss:
Here is the batch size, for matching pairs and otherwise, while and are the L2-normalized image and text embeddings. and are learned temperature and bias parameters, and is the logistic sigmoid. The original paper initializes the bias to a large negative value so that the loss starts near the all-negative prior, which stabilizes early training when most pairs in a batch are mismatches.[5] Rather than forcing a single matching caption to compete against all others in the batch, this equation independently evaluates each pairing, treating it as a separate binary classification problem.
Advantages over CLIP
- Decoupled computation: No global normalization is needed. Each pair is scored independently, allowing the similarity matrix to be computed in chunks across devices.[5]
- Efficiency: The sigmoid loss removes global softmax normalization. The SigLIP paper reports stronger results than its softmax baseline at studied smaller batch sizes and diminishing returns from pushing batch size much further, rather than a blanket hardware guarantee.[5]
- Memory and partitioning: The loss is easier to chunk across devices because it doesn't require a single global softmax over the full batch.
SigLIP's pairwise sigmoid loss changes the computation contract: positive and negative pair terms can be evaluated without a batch-wide probability distribution. It doesn't by itself say what accelerator, data volume, or accuracy a data-center matcher will need.
What does SigLIP change relative to CLIP's softmax loss?
Answer
It scores image-text pairs with independent sigmoid terms instead of one global softmax over the batch. That reduces global synchronization pressure and works better at smaller batches, though large batches can still help.
1import math
2
3def softplus(value: float) -> float:
4 return max(value, 0.0) + math.log1p(math.exp(-abs(value)))
5
6def pair_loss(score: float, is_match: bool) -> float:
7 label = 1.0 if is_match else -1.0
8 return softplus(-label * score)
9
10scores = [
11 ("same_asset", 3.2, True),
12 ("wrong_caption", -2.1, False),
13 ("hard_negative", 0.4, False),
14]
15
16for name, score, match in scores:
17 print(name, round(pair_loss(score, match), 3))1same_asset 0.04
2wrong_caption 0.116
3hard_negative 0.913Why SigLIP matters in newer stacks
A common published VLM pattern is to swap plain CLIP-style towers for newer SigLIP-family encoders because the sigmoid objective is easier to scale and stronger at moderate batch sizes. SigLIP 2 extends that recipe with caption-based pretraining, self-distillation, masked prediction, and native-aspect-ratio support, and the paper reports better transfer than original SigLIP across model scales when used as a VLM vision backbone.[6] The contrastive idea from CLIP is still the foundation; SigLIP changed the loss, and SigLIP 2 hardened the training recipe.
Modern VLMs: from alignment to generation
CLIP and SigLIP tell a model whether an image and a caption belong together. Modern VLMs go further: they turn visual features into something a language model can reason over token by token.
Study published architectures first. Open papers already show the main patterns we need:
- simple projectors that map vision features into the LLM space
- query-based compressors that reduce many visual patches into a fixed token budget
- resamplers and adapters that preserve more spatial detail for high-resolution inputs
That gives us a solid mental model without pretending we know the internals of proprietary systems.

LLaVA: the straightforward projector path[7]
LLaVA is the clearest example of the "project visual features into the language model" recipe. It takes a pretrained CLIP vision encoder and a pretrained LLM, then learns a projection layer between them.
The original LLaVA recipe uses two stages:[7]
- Feature alignment: freeze the vision encoder and LLM, train only the projector.
- Visual instruction tuning: fine-tune the projector and LLM on multimodal instruction data.
The architectural lesson is important: LLaVA reported useful multimodal instruction-following behavior from a pretrained vision encoder, an LLM, and a learned projector, without introducing a new vision backbone.[7]
What is the core LLaVA architecture recipe?
Answer
Use a pretrained vision encoder, project its visual features into the LLM hidden dimension, concatenate projected visual tokens with text tokens, then train first for feature alignment and then for multimodal instruction following.
Common multimodal training ladder
A common published VLM recipe doesn't train in one jump from raw images to polished chat behavior. It uses stages, with each stage solving a different problem.
| Stage | Typical objective | What it teaches |
|---|---|---|
| Contrastive pretraining | image-text matching as in CLIP or SigLIP | shared semantic space for retrieval and zero-shot recognition |
| Connector or projector alignment | freeze big backbones, train connector | map vision features into language-model space cheaply |
| Multimodal instruction tuning | image + prompt -> assistant response | teach task behavior, dialogue format, OCR usage, and grounded answers |
| Preference or safety post-training | chosen vs rejected responses, critiques, or policy filters | reduce harmful or low-quality multimodal behavior |
CLIP and SigLIP mostly cover the first row.[1][5] LLaVA makes the second and third rows explicit with feature alignment followed by visual instruction tuning.[7] A chat-oriented system needs generative and instruction-following training beyond contrastive alignment, because matching alone doesn't teach long-form answer behavior.
This ladder is useful because it prevents a common confusion:
- contrastive pretraining teaches matching
- instruction tuning teaches how to answer
- post-training teaches which answers are preferred or allowed
If a multimodal assistant sees the right evidence but answers in the wrong format, you need better instruction tuning, not more CLIP data. If it gives polished but unsafe grounded answers, you need better post-training, not a new projector.
BLIP-2 and the Q-Former: compress before the LLM[8]
BLIP-2 (Bootstrapping Language-Image Pre-training, version 2) is useful because it attacks a different problem. Instead of forwarding every visual feature directly into the LLM, it introduces a Q-Former that learns a fixed set of query vectors. Those queries pull the most relevant information out of the frozen vision encoder.
This creates a bounded number of visual tokens regardless of how large the raw vision feature map is.[8]
That's relevant in production because tokens admitted to the language path affect prefill and cache cost. If a smaller fixed budget preserves task-critical evidence, it can improve the measured latency-cost trade-off over naive concatenation.
Why does BLIP-2 put a Q-Former before the LLM?
Answer
The Q-Former uses learned queries to extract a bounded set of useful visual tokens from a frozen vision encoder. That keeps visual evidence useful without sending every patch token into the expensive language model.
Flamingo-style resampling: preserve more context without exploding token count[9]
Flamingo pushed another important idea: first compress variable-length visual features with a Perceiver Resampler, then let language tokens attend to that compact bank through gated cross-attention blocks.[9]
This kind of design is especially useful when:
- multiple images appear in one prompt
- video or long visual sequences are involved
- the language model needs to revisit visual evidence later in the answer
The core trade-off is complexity. Resamplers and cross-attention blocks preserve more structure, but they also add more moving parts than the very simple LLaVA recipe.
When is a Flamingo-style resampler more attractive than simple projection?
Answer
Use it when prompts include multiple images, video frames, or long visual sequences where the language model needs to revisit visual evidence without paying for every raw patch token in the main prefix.
Qwen-VL: grounding and text reading beyond caption matching[10]
Qwen-VL (Qwen Vision-Language) is a good published example of a chat-oriented VLM explicitly built for grounding and text reading, rather than caption matching alone. The paper describes a visual receptor, an input-output interface, and a multi-stage training pipeline that let the model handle OCR-heavy and box-grounded tasks in addition to generic image understanding.[10]
That matters because text reading and grounding are resolution-hungry tasks.
High-resolution token math
Suppose your vision tower uses 14×14 patches. A single 336×336 crop becomes patch tokens before any projector-side compression. Tile one document page into four such crops and you're already above 2,300 visual tokens before the model even reads the user's question.
In a projected-prefix VLM, those admitted visual tokens hurt twice: they make prefill slower, and they enlarge the KV cache that must remain resident during decode. PagedAttention helps the serving stack pack and recycle that memory more efficiently, but it doesn't reduce the number of prefix tokens you created in the first place.[11] Cross-attention designs keep a separate visual bank, so measure their memory and latency path separately.

Why can high-resolution document images hurt projected-prefix VLM serving twice?
Answer
They can create many prefix tokens, which makes prefill slower, and those same tokens enlarge the KV cache that must stay resident during decoding. Memory managers help pack the cache, but they don't erase token count. Cross-attention designs need separate measurement because they expose visual evidence through a different path.
In a projector path, the connector decides how many encoder features become autoregressive prefix tokens. Other architectures can expose a separate cross-attention memory bank instead. This comparison uses raw tiles and a fixed-size prefix compression path without claiming that compression preserves enough OCR evidence.
1def prefix_tokens(text_tokens: int, crops: int, patch_tokens_per_crop: int, compressed_tokens: int | None) -> int:
2 visual_tokens = crops * patch_tokens_per_crop
3 if compressed_tokens is not None:
4 visual_tokens = compressed_tokens
5 return text_tokens + visual_tokens
6
7raw_prefix = prefix_tokens(text_tokens=180, crops=4, patch_tokens_per_crop=576, compressed_tokens=None)
8compressed_prefix = prefix_tokens(text_tokens=180, crops=4, patch_tokens_per_crop=576, compressed_tokens=64)
9
10print("raw_prefix_tokens:", raw_prefix)
11print("compressed_prefix_tokens:", compressed_prefix)
12print("tokens_avoided_if_quality_holds:", raw_prefix - compressed_prefix)1raw_prefix_tokens: 2484
2compressed_prefix_tokens: 244
3tokens_avoided_if_quality_holds: 2240Production systems usually respond in one of three ways:
- resize aggressively and accept some information loss
- tile or crop the image, then summarize each region
- compress the visual stream with a Q-Former, resampler, or other projector-side bottleneck before handing it to the LLM
When a model performs well on documents, inspect the full evidence path: resolution, tiling, OCR, connector compression, and the model's measured failure slices. Published designs such as BLIP-2's Q-Former and Flamingo's resampler show ways to bound or mediate visual features before language reasoning.[8][9]
Adaptive pruning and merging under a latency budget
A fixed visual-token budget makes capacity predictable, but it spends the same allowance on a blank panel and a dense wiring diagram. An adaptive reducer estimates which tokens carry useful information for each image, retains important tokens, and either drops or merges the rest.
Pruning and merging are different operations:
- Pruning removes low-importance tokens. It's cheap, but discarded evidence can't be recovered.
- Merging combines redundant tokens into retained representatives. It can preserve more aggregate context, but similarity mistakes can blend distinct objects or text regions.
PruMerge is one researched example for projected-prefix VLMs.[12] It uses sparse attention between the vision encoder's class token and spatial tokens to select important tokens per image. It then clusters tokens by key similarity and folds related pruned tokens into retained representatives with weighted averaging. The paper evaluates this mechanism on LLaVA-1.5 and Video-LLaVA. That evidence supports the design as a candidate, not a guarantee for every encoder, OCR route, or domain.
An adaptive policy still needs a hard serving contract:
The route should also reserve minimum coverage for task-critical evidence. A dashboard policy might preserve tokens that overlap OCR boxes, small-object proposals, or selected crops before spending the remaining budget on saliency. A global spatial sample can protect against an importance scorer that focuses only on one object.
| Control | Why it matters |
|---|---|
| Hard maximum tokens | Protects p95 prefill, memory, and concurrency from dense outliers |
| Task-specific token floor | Prevents an easy latency win from erasing text, defects, or small objects |
| Spatial coverage | Keeps neglected regions represented when saliency is concentrated |
| Token-count buckets | Reduces padding waste when adaptive requests are batched together |
| Full-token shadow sample | Measures whether reduction changes answers, OCR, or grounding |
Placement determines which cost disappears. Reducing tokens after the vision encoder saves connector and language-model work, but it doesn't save the completed vision-encoder pass. A reducer inside the vision tower can save later encoder work, but it changes a larger portion of the model and needs separate evaluation.
Track the admitted-token distribution, reduction overhead, vision latency, prefill time, time to first token, peak memory, and quality by slice. For this inspection product, OCR exact match, small-hazard recall, and grounding IoU matter more than average VQA alone. If the hard cap would remove protected evidence, use a slower route or return review instead of silently forcing the image through the fast path.
Why does an adaptive token reducer still need a hard maximum and protected evidence floors?
Answer
Adaptive counts can spike on dense inputs, so a hard maximum protects the latency and memory contract. Protected floors keep the reducer from meeting that contract by deleting OCR regions, small objects, or other evidence the task requires.
Connector design controls a serving trade-off
The component between the vision encoder and the LLM controls what evidence reaches generation and how many visual tokens the language path must handle.
Common options include:
- Linear projection: cheap and simple, but can bottleneck representational power.
- MLP projector: still simple, usually stronger than a single linear layer.
- Q-Former / query transformer: compresses many visual features into a fixed learned token set.[8]
- Convolutional or locality-aware abstractors: preserve more spatial structure before handing features to the LLM.[13]
An overcompressed connector can remove evidence needed for OCR or grounding. A large or expensive connector can increase latency or language-prefix cost. Both outcomes must be measured by task slice.
That's why there's no single best adapter. The right design depends on whether you care more about:
- raw simplicity
- token efficiency
- spatial fidelity
- video or multi-image support
How do you choose between a linear projector, MLP, Q-Former, and locality-aware adapter?
Answer
Use linear or MLP projectors for simple, cheap alignment. Use Q-Former-style compression when token budget matters. Use locality-aware adapters when spatial detail, OCR, or grounding matters enough to spend more compute.
Designing around unpublished internals
Closed vendors may publish capability demos and benchmark numbers without enough architecture detail to justify internals claims.
Treat the model as an evaluated dependency: measure supported input resolution, visual-token billing if exposed, latency by image count, OCR and grounding slices, refusal behavior, and output contract. Don't infer a projector type, token budget, or training curriculum unless the provider publishes it.
What is the safe way to discuss closed VLM architectures?
Answer
Describe observed API behavior and measured task performance. Don't claim projector types, token budgets, or training recipes unless the vendor published them.
Evaluation
Evaluating a VLM requires more than one benchmark because different tests stress very different abilities.
| Benchmark | Focus | Metric | What it tells you |
|---|---|---|---|
| ImageNet | Zero-shot classification | Top-1 Accuracy | Whether the model recognizes common visual categories |
| COCO (Common Objects in Context) Captions | Captioning | CIDEr, BLEU | Whether the generated text overlaps with human references |
| VQAv2 | Visual question answering | Accuracy | Whether the model can answer grounded questions about an image |
| TextVQA | OCR + reasoning | Accuracy | Whether the model can read and reason over embedded text |
| MMMU | Expert multimodal reasoning | Accuracy | Whether it can combine specialist knowledge and visual evidence |
| MMBench | Fine-grained multimodal QA | CircularEval score | Whether performance holds across ability slices and reordered multiple-choice options |
No single metric is enough.
- CLIP-like models can do well on retrieval and still fail badly on counting.
- Caption metrics can look fine even when spatial reasoning is weak.
- OCR-heavy tasks expose very different failure modes than general image description.
That's why serious evaluation needs a benchmark mix, not a single scoreboard.
Why is one VLM benchmark insufficient for deployment?
Answer
Retrieval, captioning, VQA, OCR, expert reasoning, and prompt variation stress different capabilities. A model can look strong on one benchmark while failing counting, layout, text reading, or prompt-variation cases.
Aggregate scores can also hide the exact slice that blocks launch. Keep capability slices separate and require task-specific pass bars.
1results = {
2 "asset_retrieval_recall_at_10": (0.94, 0.90),
3 "asset_tag_ocr_accuracy": (0.71, 0.92),
4 "rack_hazard_grounding_iou": (0.76, 0.70),
5}
6
7failed = [
8 metric
9 for metric, (measured, required) in results.items()
10 if measured < required
11]
12
13print("launch_ready:", not failed)
14print("failed_slices:", failed)1launch_ready: False
2failed_slices: ['asset_tag_ocr_accuracy']Production applications
Document understanding
Document AI is a token-budget problem. Dense documents contain tiny text, tables, figures, and layout cues that don't survive aggressive resizing.
In practice, production systems usually combine some mix of:
- page tiling or crop-based preprocessing
- OCR features
- a VLM for ambiguous or layout-heavy cases
The engineering goal isn't "feed the page in at maximum resolution." It's "preserve the evidence that matters while keeping the token budget survivable."
What is the core design goal for document understanding with VLMs?
Answer
Preserve the evidence that matters, such as small text, tables, layout, and figures, while keeping visual tokens and prefill cost survivable. Maximum raw resolution isn't the goal by itself.
Image search and retrieval
For image search, a generative VLM is usually the wrong first tool.
A dual-encoder model like CLIP or SigLIP is better because you can:
- precompute image embeddings offline
- index them once
- embed only the query at request time
That supports an indexed first stage whose latency and cost can be measured independently. Generative VLMs are candidates for reranking or explanation after retrieval when those added capabilities justify their measured cost.
Why is CLIP or SigLIP usually better than a generative VLM for first-pass image search?
Answer
You can precompute image embeddings offline, index them once, and embed only the query at request time. A generative VLM is more expensive and better used for reranking or explanations after retrieval.
1def select_path(task: str) -> list[str]:
2 if task == "retrieve_similar_assets":
3 return ["dual_encoder", "vector_index"]
4 if task == "answer_from_label_text":
5 return ["ocr", "generative_vlm", "citation_check"]
6 if task == "click_disable_port":
7 return ["grounding_model", "action_policy", "human_confirm"]
8 raise ValueError(task)
9
10for task in ["retrieve_similar_assets", "answer_from_label_text", "click_disable_port"]:
11 print(task, "->", " + ".join(select_path(task)))1retrieve_similar_assets -> dual_encoder + vector_index
2answer_from_label_text -> ocr + generative_vlm + citation_check
3click_disable_port -> grounding_model + action_policy + human_confirmVisual agents
Visual agents need more than a captioning model. They need grounding.
It isn't enough to answer "the blue button is near the top-right." The system often has to emit an action like:
- click
(x, y) - select the second menu item
- type into the input field beneath a label
That usually requires a grounding-aware model or a separate grounding component on top of the core VLM, plus strong safety checks around actions.
Why do visual agents need grounding beyond captioning?
Answer
Actions need coordinates, UI targets, object regions, or bounding boxes. A caption can say "the button is near the top-right," but an agent often needs a grounded action like click (x, y) with safety checks.
Common pitfalls
"One pooled CLIP embedding understands an image like a person does"
- Symptom: Teams ask for counts, boxes, or grounded actions from one global embedding.
- Cause: CLIP was trained for global alignment, not dense spatial supervision.
- Fix: Use detectors, grounding-aware VLMs, or region features when localization or counting matters.
"CLIP or SigLIP can also serve my chat and captioning path"
- Symptom: Similarity scores are wired into a task that needs open-ended explanations or grounded dialogue.
- Cause: Image-text matching was confused with multimodal generation.
- Fix: Keep CLIP or SigLIP for retrieval and zero-shot scoring, and use a generative VLM when you need open-ended answers.
"Higher resolution always means better OCR"
- Symptom: Document quality rises a little, but latency and memory spike hard.
- Cause: Resolution was increased without checking token budget, tiling policy, or compression.
- Fix: Measure visual token count, tile selectively, preserve only the evidence that matters, and compress before the LLM when possible.
"Visual serving cost ends when the encoder finishes"
- Symptom: Offline profiling looks cheap, but production decode concurrency collapses.
- Cause: The team counted encoder FLOPs but ignored prefill latency and KV-cache growth from large visual prefixes.
- Fix: Track prefill cost and KV pressure together with encoder cost before picking image resolution or crop count.
"Adaptive pruning makes the latency budget self-enforcing"
- Symptom: Typical images are fast, but dense screenshots create token-count spikes or tiny defects disappear.
- Cause: The reducer had no hard maximum, protected evidence floor, or failure route.
- Fix: Cap the admitted sequence, preserve task-critical regions, bucket requests by retained count, and route unsafe reductions to a slower path or review.
"Every in-batch non-match is a clean negative"
- Symptom: Contrastive training underperforms on visually similar assets or captions that describe the same scene in different words.
- Cause: Some in-batch negatives are false negatives, so the loss pushes semantically related pairs apart.
- Fix: Curate data carefully, use richer captioning, and treat contrastive batch design as a modeling choice rather than a harmless training detail.
"Zero-shot prompts will hold in fine-grained domains"
- Symptom: Prompt wording swings predictions or specialist categories collapse together.
- Cause: Generic templates don't provide enough domain signal.
- Fix: Prompt-ensemble, calibrate on validation data, and fine-tune or probe when domain precision matters.
What you should carry forward
At this point, check these skills:
- Explain how CLIP's dual-encoder architecture aligns images and text in a shared embedding space.
- Walk through the contrastive loss logic with a concrete batch example.
- Implement zero-shot classification using prompt engineering and embedding similarity.
- Diagnose why CLIP fails on spatial reasoning, counting, or fine-grained OCR.
- Compare LLaVA, BLIP-2, and Flamingo-style designs for connecting vision encoders to language models.
- Size the visual token budget for a production VLM and choose fixed or adaptive compression with explicit latency and evidence protections.