A Guide to Vision-Language Models
Vision-language models (VLMs) are neural networks that take images and text as input and produce text as output, letting you ask questions about a screenshot, extract data from a scanned document, or describe what's in a photo using the same interface you'd use for a text-only chat model. They matter to working engineers because they collapse what used to be a multi-stage pipeline (OCR, object detection, a separate classifier, then a language model to stitch it together) into a single API call. This guide walks through how vision-language models are built, how to call one through a hosted API and run one locally, how fine-tuning works, and where they tend to fail in production.
What Vision-Language Models Actually Are
A vision-language model is a language model that has learned to treat image patches as another kind of token. Instead of only accepting a sequence of word tokens, a VLM accepts a mixed sequence: some tokens come from a tokenizer over text, others come from an image encoder that has chopped a picture into patches and projected each patch into the same embedding space the language model uses. Once everything lives in one embedding space, the rest of the model is just a transformer doing what transformers do: attending across the whole sequence and predicting the next token.
This is different from older "multimodal" systems that bolted a separate computer-vision model onto a language model with a thin API in between. A classic pipeline might run an object detector, convert its output to a caption with a captioning model, then feed that caption to a chatbot. Each step loses information. A true vision-language model instead lets the language model attend directly to image features, so it can answer questions that require fine-grained visual reasoning, like reading a specific number off a chart or noticing that two logos in a screenshot are subtly different colors, without ever converting the image to a lossy text description first.
The category includes hosted models you call through an API, such as Claude's vision-capable models and other frontier multimodal LLMs, as well as open-weight model families you can download and run yourself. Both share the same core recipe: a vision encoder, a projection layer, and a language model backbone, trained jointly (or with the vision encoder frozen) on paired image-text data.
How Vision-Language Models Process Images and Text
Understanding the pipeline helps you debug weird outputs later, so it's worth walking through step by step.
Image encoding. The image is resized and split into a grid of patches, commonly 14x14 or 16x16 pixels each. A vision transformer (often a CLIP-style or SigLIP-style encoder) turns each patch into a vector embedding. A single image can produce anywhere from a few hundred to a few thousand patch embeddings depending on resolution and the model's patching scheme.
Projection. Those patch embeddings live in the vision encoder's own vector space, which is not the same space the language model's token embeddings live in. A small projection network (sometimes just a couple of linear layers, sometimes a lightweight cross-attention block) maps vision embeddings into the language model's embedding space so the two modalities become directly comparable.
Sequence assembly. The projected image embeddings are spliced into the token sequence alongside the text tokens, usually with special marker tokens showing the model where an image starts and ends. From this point on, the transformer treats the sequence uniformly: causal self-attention lets later text tokens attend back to earlier image tokens, which is how the model can answer "what color is the shirt in the top-left of the image" by attending specifically to the patches near that region.
Generation. The language model decodes text autoregressively, one token at a time, exactly like a text-only LLM. The image tokens are part of the context; nothing about generation changes once encoding and projection are done.
The practical implication: image resolution and token budget are directly linked. A higher-resolution image produces more patch tokens, which costs more context window and more compute, and different vision-language models handle this tradeoff differently. Some models tile large images into multiple sub-images (useful for reading dense documents), others downsample aggressively and lose fine detail. When you pick a model, check what its maximum input resolution is and how it tokenizes images, because that decides whether it can read small text in a screenshot or count objects reliably.
Vision-Language Model Architectures Worth Knowing
You don't need to reimplement any of these to use vision-language models well, but recognizing the shapes helps you reason about tradeoffs.
Cross-attention fusion. The language model stays mostly frozen, and new cross-attention layers are inserted so text tokens can attend to image features without the image features ever entering the main token stream. This keeps the language model's original text capabilities intact and is cheaper to train, but can be less flexible for tasks that need very tight text-image interleaving.
Early fusion / unified token stream. Image patches become tokens in the same sequence as text, and a single transformer processes everything together with no separate cross-attention path. This is the dominant approach in current frontier multimodal LLMs because it lets the model reason jointly over text and vision with the full expressive power of self-attention, at the cost of a longer context window per image.
Dual-encoder retrieval models. Not every "vision-language model" is generative. Models like CLIP and SigLIP learn to embed images and text into a shared space for similarity search: given a query, you can find the closest matching images without generating any text at all. These are the backbone of most vision-language search and retrieval systems, and they're often reused as the encoder inside a generative VLM.
For most application work, you'll interact with the second category, the generative unified-token-stream models, because they're the ones you can prompt conversationally and get free-form answers from.
Sending Images to a Vision-Language Model
Here's a minimal, runnable example using the Anthropic Python SDK to send an image alongside a text question to a vision-capable Claude model. This pattern (a list of content blocks mixing image and text) is the shape most hosted vision-language model APIs converge on.
pip install anthropicimport base64
from anthropic import Anthropic
client = Anthropic() # reads ANTHROPIC_API_KEY from the environment
with open("invoice.png", "rb") as f:
image_data = base64.standard_b64encode(f.read()).decode("utf-8")
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
messages=[
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": image_data,
},
},
{
"type": "text",
"text": "Extract the invoice number, total amount, "
"and due date from this image as JSON.",
},
],
}
],
)
for block in response.content:
if block.type == "text":
print(block.text)A few details that matter in practice:
The image content block goes before the text block in most conventions, though models are generally tolerant of ordering. What isn't tolerant is media type mismatches: if you send PNG bytes labeled as image/jpeg, decoding can fail silently or the model can misread the image, so always match media_type to the actual file format.
For structured extraction like the invoice example above, ask for a specific schema in the prompt, or use whatever structured-output feature the API offers (JSON schema-constrained generation, if the provider supports it) so you don't have to parse loosely-formatted text on the way out.
If you need to send an image the model doesn't already have local bytes for, most APIs also accept a url source type as an alternative to base64, which saves you a download-then-reupload round trip when the image is already hosted somewhere reachable by the API.
Running an Open-Source Vision-Language Model Locally
If you need to keep images on your own infrastructure, or you want to fine-tune, an open-weight vision-language model running through Hugging Face Transformers is the standard path.
pip install transformers accelerate pillow torchfrom transformers import AutoProcessor, AutoModelForImageTextToText
from PIL import Image
import torch
model_id = "your-chosen-vlm-checkpoint" # pick a current open VLM from the Hugging Face hub
processor = AutoProcessor.from_pretrained(model_id)
model = AutoModelForImageTextToText.from_pretrained(
model_id,
torch_dtype=torch.bfloat16,
device_map="auto",
)
image = Image.open("receipt.jpg").convert("RGB")
messages = [
{
"role": "user",
"content": [
{"type": "image"},
{"type": "text", "text": "List every line item and its price."},
],
}
]
prompt = processor.apply_chat_template(messages, add_generation_prompt=True)
inputs = processor(images=image, text=prompt, return_tensors="pt").to(model.device)
output = model.generate(**inputs, max_new_tokens=512)
print(processor.decode(output[0], skip_special_tokens=True))The AutoModelForImageTextToText class (and the matching AutoProcessor) is the general entry point Transformers uses for vision-language models: the processor handles both image preprocessing (resizing, patching, normalization) and text tokenization, so you don't have to hand-write either. Swap model_id for whatever current open-weight vision-language model checkpoint fits your latency and quality bar; the surrounding code stays the same across most checkpoints that expose a chat template.
Two things bite people running VLMs locally for the first time. First, memory: vision-language models are often larger than a text-only model of similar quality, because you're paying for both the vision encoder and the language backbone, so check the checkpoint's parameter count against your GPU memory before committing to it. Second, chat templates: vision-language models are picky about exactly how the image placeholder and text are interleaved in the prompt, and using the wrong template (or hand-rolling your own instead of calling apply_chat_template) is the single most common cause of a model that "ignores" the image.
Fine-Tuning a Vision-Language Model on Your Own Data
Full fine-tuning of a vision-language model is expensive because you're updating both the vision encoder and the language model. In practice, most teams use one of two cheaper strategies.
LoRA on the language model, frozen vision encoder. If your task is about how the model talks about images it can already see clearly (a house style, a domain vocabulary, a specific output format), you usually don't need to touch the vision encoder at all. Freeze it, attach low-rank adapters to the language model's attention and MLP layers, and train only those adapters. This is fast, needs relatively little data (often a few hundred to a few thousand labeled examples), and avoids catastrophic forgetting of general vision ability.
from peft import LoraConfig, get_peft_model
lora_config = LoraConfig(
r=16,
lora_alpha=32,
target_modules=["q_proj", "v_proj", "k_proj", "o_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM",
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()Full or partial fine-tuning of the vision encoder too. If the model consistently misreads a specific visual domain, say, low-resolution CCTV frames, or a specialized chart type it wasn't trained heavily on, adapting the vision encoder (or at least unfreezing its last few layers) usually helps more than adapter tuning on the language side alone. This needs more data and compute, and it's easier to break general capability, so hold out a broad evaluation set and check that performance on ordinary images hasn't regressed after training.
For data, structure each example the same way you'd structure an inference call: an image plus a target text response, formatted through the same chat template you'll use at inference time. Mismatched formatting between training and inference is a common source of a model that trains cleanly but produces garbage in production, because the tokens it saw during fine-tuning don't match the tokens it sees when you actually call it.
If you don't need per-task specialization, retrieval-augmented prompting (showing the model a couple of similar labeled examples in-context before asking about the new image) often gets you most of the way there without any training at all, and it's worth trying before committing to a fine-tuning run.
Where Vision-Language Models Fit in Production
The clearest production wins for vision-language models cluster around a few patterns.
Document and form processing. Invoices, receipts, ID cards, insurance claims: any document that's semi-structured but varies in layout is a good fit, because a VLM can read the document as an image and return structured fields without you building a template per document type the way classic OCR pipelines require.
Visual QA over UI screenshots. For QA automation, customer support triage, or accessibility tooling, sending a screenshot and asking "is the submit button visible and enabled" is often more robust than parsing the DOM, especially when the underlying app is a canvas-based or heavily obfuscated frontend.
Content moderation and classification at the image level. Instead of training a bespoke classifier per policy category, a vision-language model can be prompted with the policy text directly and asked to judge an image against it, which is slower and more expensive per call but much faster to stand up and iterate on than a from-scratch classifier.
Chart and figure understanding. Extracting the underlying data from a chart image, or summarizing what a scientific figure shows, is a task classic computer vision never handled well because it requires combining shape recognition with numeric and semantic reasoning, exactly the mix a vision-language model is built for.
A pattern worth calling out explicitly: don't reach for a vision-language model when a narrower, cheaper tool solves the problem. If you only need to detect whether a face is present, a small dedicated detector will be faster and cheaper than a VLM call. Vision-language models earn their cost when the task genuinely requires open-ended reasoning about what's in the image, not when it's a fixed classification with a small label set.
Evaluating Vision-Language Model Output
Evaluation is where most vision-language model projects get sloppy, because it's tempting to eyeball a handful of outputs and call it good. A few practices that catch real problems:
Build a held-out set that mirrors production inputs, not curated stock photos. Vision-language models trained mostly on clean, high-quality internet images tend to degrade on the messy inputs real systems produce: phone photos at an angle, low light, partial occlusion, compressed screenshots. If your evaluation set doesn't include that mess, your accuracy numbers won't predict production behavior.
Separate "did it see the right thing" from "did it phrase it correctly." For structured extraction tasks, compare extracted field values against ground truth directly (exact match or normalized match, not string similarity on the whole response), since a model can phrase an answer differently while getting the actual content right, or phrase it identically while getting the number wrong.
Test resolution sensitivity deliberately. Downsample the same image to a few different resolutions and see where accuracy falls off a cliff. This tells you the minimum input resolution you need to guarantee upstream (in your image capture or preprocessing pipeline) before it even reaches the model.
Use an LLM-as-judge only for open-ended tasks, and only after checking that the judge's ratings correlate with a small set of human-labeled examples. For tasks with a clear right answer, exact-match or rule-based scoring is cheaper and more trustworthy than another model's opinion.
Common Pitfalls When Building With Vision-Language Models
Sending oversized images without resizing. Most vision-language models cap the useful input resolution somewhere well below a typical modern camera photo. Sending a 12-megapixel image doesn't help accuracy past the model's effective resolution, it just burns tokens and latency. Resize to the model's documented sweet spot before sending.
Ignoring aspect ratio distortion. Naively resizing an image to a fixed square can stretch text and shapes, which hurts a model's ability to read fine detail. Pad to preserve aspect ratio, or use whatever tiling scheme the model documents for non-square inputs.
Treating every wrong answer as a model failure. A large share of "the vision-language model got this wrong" bugs are actually image quality or prompt clarity problems. Before concluding the model can't do a task, check the image at the resolution the model actually received it and confirm the prompt states exactly what output format you want.
Forgetting that image tokens count against context. In long conversations with multiple images, token usage adds up fast, since each image can consume hundreds to thousands of tokens depending on resolution. If you're hitting context limits unexpectedly in a multi-turn, multi-image conversation, this is usually why.
Assuming counting and precise localization are solved. Vision-language models have historically been weaker at exact counting ("how many people are in this photo") and precise pixel-level localization than at holistic description and reading text. If your task depends on exact counts or coordinates, validate accuracy carefully on your own data rather than assuming it works out of the box, and consider pairing the VLM with a dedicated detection model for the counting or localization step.
FAQ
What's the difference between a vision-language model and OCR? OCR extracts literal text from an image using pattern recognition tuned specifically for character shapes. A vision-language model can read text too, but it also reasons about layout, visual context, and content that isn't text at all (colors, shapes, spatial relationships), and it can follow open-ended instructions about what to do with what it sees. For pure "give me every character on this page" tasks at scale, dedicated OCR is often faster and cheaper; for tasks that need understanding, a VLM usually wins.
Do vision-language models understand video? Some do, typically by sampling frames at intervals and treating each frame as an image in the sequence, which means they get a series of snapshots rather than true continuous motion understanding. For tasks that depend on fine-grained temporal detail (exact timing of an action), a video-native model or a frame-sampling strategy tuned to your task will outperform naively feeding a VLM a handful of frames.
How much does image resolution actually matter? A lot, and it varies by task. Reading small text in a screenshot or document needs high effective resolution; describing the general content of a photo doesn't. Test your actual task at a few resolutions rather than assuming higher is always better, since higher resolution also means more tokens, more latency, and more cost per call.
Can I use a vision-language model for real-time video analysis? Hosted API-based VLMs are usually too slow and too expensive per frame for true real-time analysis at high frame rates. For real-time use cases, either sample frames sparingly (analyze every few seconds rather than every frame) or use a smaller, purpose-built model running locally for the time-sensitive parts, reserving the full vision-language model for periodic, higher-value analysis.
Is fine-tuning worth it, or should I just prompt better? Try prompting first, including few-shot examples in the prompt itself. Fine-tuning is worth the investment when you have a large volume of a specific, repeatable task, when prompt-based approaches plateau below the accuracy you need, or when you need the model to consistently produce a narrow output format without constant reminders in every prompt. If you're still iterating on what the task even is, prompting is faster to change than a fine-tuned checkpoint.
What's the safest way to handle sensitive images (medical, legal, personal)? Check the data handling and retention policy of whichever provider or model you're using before sending sensitive images, and prefer a self-hosted open-weight model when data cannot leave your infrastructure at all. For hosted APIs, confirm whether images are retained for training by default and whether that can be disabled, since policies differ by provider and by account tier.
BootcampA 30-day guided bootcamp: build, harden and ship a production autonomous agent from scratch.
AI AgentsUnderstand how AI agents really work: the loop, the tools, the memory, and why most agent projects fail.
Related reading