ml-models / Specialized learning / Multimodal models advanced

Multimodal models

Models that read images, text and audio together. This page derives CLIP's contrastive loss and its gradient on a batch of four image–caption pairs, uses the result for zero-shot classification, then builds a vision-language model by attaching an image encoder to a language model and compares the two ways to connect them, token concatenation and gated cross-attention, by their cost, and ends with how audio becomes tokens.

On this page
  1. Problem
  2. Intuition
  3. Mechanics
  4. Contrastive image–text pretraining
  5. Zero-shot classification
  6. Vision-language models
  7. Concatenation versus cross-attention
  8. Audio tokens
  9. Worked example
  10. The CLIP loss on four pairs
  11. Zero-shot with one prompt and with an ensemble
  12. What fusion costs
  13. Audio token rates
  14. Implementation
  15. Tradeoffs
  16. Connections
  17. Read first
  18. Read next
  19. References

Problem

A vision transformer trained on ImageNet knows a thousand labels, and a language model knows text but has never seen a pixel. Many tasks need both: search photos by a sentence, describe an image, answer a question about a chart, transcribe and then reason about a recording. Collecting labels for each of these is expensive, and a fixed label set cannot cover "a corgi wearing sunglasses on a skateboard".

The web, however, is full of images next to text that describes them: alt text, captions, titles. Multimodal models turn that weak, noisy pairing into supervision. The two problems are how to learn a shared space in which an image and its caption land close together, and how to feed what an image encoder sees into a language model so the language model can talk about it.

Intuition

For the first, use a two-tower model: one encoder per modality, each producing a vector, trained so that matching pairs have a high dot product and mismatched pairs a low one. Every other caption in the batch is a negative for each image. The model never has to generate text; it only has to tell which caption goes with which image, a task that already forces it to recognize objects, scenes, styles and text in the image. Once trained, any label can be turned into a caption ("a photo of a dog") and classified by similarity, with no training examples at all.

For the second, treat the image encoder's output vectors as if they were words. Either project them into the language model's embedding space and put them in the sequence in front of the text, so ordinary self-attention reads them, or keep them separate and add cross-attention layers in which the text queries the image. The first is simple and is what most open models do; the second keeps the text sequence short and the language model untouched.

Mechanics

Contrastive image–text pretraining

CLIP (Radford et al., 2021) has an image encoder f, a ResNet or a ViT, and a text encoder g, a Transformer whose output at the end-of-text token summarizes the caption. Each is followed by a linear projection into a shared space of the same width, and both outputs are L2-normalized, so their dot product is a cosine similarity. For a batch of N pairs with unit vectors ui = f(imagei)/‖·‖ and vj = g(textj)/‖·‖, the logits are

Sij = s · ui · vj,   s = et

with t a learned scalar: s is an inverse temperature, initialized to 1/0.07 ≈ 14.29 and clipped at 100. Each row of S is a classification over N captions with the correct one on the diagonal, and each column a classification over N images. The loss is the average of the two cross-entropies:

L = ½ · ( (1/N) Σi −log softmaxj(Si·)i + (1/N) Σj −log softmaxi(S·j)j )

This is InfoNCE (van den Oord et al., 2018) in both directions. It is a lower bound on the mutual information between the two views that tightens as N grows, which is one reason CLIP trained with 32,768 pairs per batch. Its gradient with respect to each logit follows from the softmax–cross-entropy derivative, p − y, applied to each direction:

∂L/∂Sij = ( Prowij + Pcolij − 2·[i = j] ) / (2N)

where Prow is the softmax of S along each row and Pcol along each column. Every diagonal entry is pulled up and every off-diagonal entry pushed down in proportion to how much probability it steals, so the gradient is spent on the hardest negatives. A larger s makes the softmax sharper: it concentrates on those hard negatives, and lets the loss approach zero once the diagonal wins; too small an s caps how confident the model can be at all.

Without labels, each row needs the others as negatives, so the loss depends on the whole batch. In data-parallel training each device gathers the embeddings from every other device to build the full N × N matrix. SigLIP (Zhai et al., 2023) replaces the softmax with an independent sigmoid on every pair, a binary "match or not" per entry, which removes the normalization over the batch and so the need to see all of it at once.

CLIP: two encoders and the batch similarity matrix On the left, four images go through the image encoder and a projection to unit vectors u1 to u4; four captions go through the text encoder and a projection to unit vectors v1 to v4. On the right is the four by four matrix of cosine similarities from this page's worked example, images as rows and captions as columns. The diagonal cells, the true pairs, are highlighted: 0.985, 0.981, 0.992, 0.980. Each row's softmax picks a caption for an image and each column's softmax an image for a caption; the loss averages both cross-entropies. images image encoder + proj, normalize captions text encoder + proj, normalize s · u·v v1 u1 0.98 0.40 0.00 0.82 v2 u2 0.36 0.98 0.50 0.74 v3 u3 0.13 0.73 0.99 0.65 v4 u4 0.69 0.84 0.69 0.98 softmax per row and per column

Zero-shot classification

To classify into K labels, embed one caption per label, such as "a photo of a {label}.", and pick the label whose text vector has the highest cosine with the image. The K text vectors act as the weights of a linear classifier that was never trained on the task. Prompt wording matters, since web captions rarely consist of a bare label; CLIP's authors averaged the normalized embeddings of many prompts per class ("a blurry photo of a …", "a drawing of a …") into one vector, a prompt ensemble, which is computed once and costs nothing at inference.

Vision-language models

A vision-language model (VLM) generates text conditioned on images. The usual recipe has three parts: a pretrained image encoder, often a CLIP or SigLIP ViT; a connector that maps its outputs into the language model's embedding space; and a pretrained decoder-only language model. LLaVA (Liu et al., 2023) used a single linear layer as the connector, and LLaVA-1.5 a two-layer MLP, on the patch tokens of a CLIP ViT-L/14 at 336 pixels: (336/14)² = 576 tokens per image. Training typically runs in two stages: first only the connector, on image–caption pairs, to align the spaces; then the connector and the language model together on instruction data (see fine-tuning).

Higher resolution means more tokens. Splitting a large image into tiles at the encoder's native resolution, plus one downscaled overview, multiplies the count: four tiles and an overview give 2,880 tokens. Connectors shrink it back by pooling neighbouring patches (2 × 2 pooling gives 144) or by a small set of learned queries that cross-attend to all patches and return a fixed number of tokens, like Flamingo's Perceiver Resampler with 64.

Concatenation versus cross-attention

Token concatenation (LLaVA and most open VLMs) places the projected image tokens in the input sequence, so every self-attention layer of the language model attends over them as over any text. Nothing in the language model changes. The cost is sequence length: every layer's attention and every layer's KV cache includes the image tokens.

Gated cross-attention (Flamingo, Alayrac et al., 2022) freezes the language model and inserts new blocks between its layers. In each, the text hidden states are queries and the visual tokens are keys and values, so attention is rectangular, text length by image length, and the text sequence never grows. The block's output is added through a gate:

y = x + tanh(α) · CrossAttn(x, image) ,   then the same for a feed-forward layer

with α a learned scalar initialized to 0. At the start, tanh(0) = 0 and the model is exactly the frozen language model, so training begins from its full ability and opens the gate gradually. Flamingo also masks the cross-attention so each text token sees only the image that immediately precedes it, which lets it read interleaved sequences of many images and text. Llama 3's vision models use the same adapter idea of cross-attention layers added to a language model.

Two ways to connect an image encoder to a language model Left, token concatenation: the image encoder's 576 patch tokens pass through a projector and are placed in front of the 100 text tokens, making a sequence of 676 that every one of the 32 language-model layers attends over with ordinary self-attention. Right, gated cross-attention: the text sequence stays 100 tokens long and goes through the frozen language-model layers; every 4th layer is preceded by a new cross-attention block whose queries are text states and whose keys and values are the image tokens, added through a tanh gate that starts at zero. token concatenation 576 image tokens 100 text tokens one sequence of 676 32 language-model layers self-attention over all 676 image tokens cached in every layer gated cross-attention image tokens 100 text tokens tanh(α)·xattn 4 frozen layers tanh(α)·xattn 4 frozen layers tanh(α)·xattn 4 frozen layers keys and values only, never in the text sequence … 8 gated blocks across 32 layers the language model is unchanged

Audio tokens

Audio enters the same way, as a sequence of vectors. Whisper (Radford et al., 2022) resamples speech to 16,000 Hz, computes an 80-channel log-Mel spectrogram with a 10 ms hop over 30-second windows, and runs two convolutions, the second with stride 2, before a Transformer encoder; a decoder cross-attends to it to emit text. Audio-language models attach such an encoder to a language model with a projector, exactly like a VLM.

To generate audio, a model needs discrete output tokens. Neural codecs such as SoundStream and EnCodec (Défossez et al., 2022) encode the waveform into frames and quantize each frame with residual vector quantization: a first codebook picks the nearest code, a second quantizes what is left, and so on. More codebooks give higher fidelity and more tokens per second. A language model trained on those tokens (AudioLM, VALL-E, and speech-native assistants) can continue speech the way a text model continues a sentence, usually predicting the first codebook first and the finer ones from it.

ComponentShape/ParamsMeaningSource
F.normalizep=2.0, dim=1, eps=1e-12v / max(‖v‖, ε): unit image and text embeddings, so dot products are cosinesfunctional.py
logits[N, N] = s · U VᵀRow i scores image i against every caption in the batchCLIP, §2.3
F.cross_entropyinput [N, N], target arange(N)Applied to the logits and to their transpose; the CLIP loss is the mean of the twofunctional.py
logit_scalescalar t, s = eᵗLearned inverse temperature, initialized to log(1/0.07) and clamped so s ≤ 100CLIP
F.scaled_dot_product_attentionquery [B, H, L, E], key/value [B, H, S, E]L ≠ S is cross-attention: L text queries, S image keys and values; is_causal=Falsefunctional.py
reference implementationattn_weight [L, S]softmax(QKᵀ · scale + bias) V, with the bias built as an L × S matrixfunctional.py
projector[V, dvision] → [V, dmodel]Linear or two-layer MLP connecting encoder and language modelLLaVA
gate αscalar per block, init 0tanh(α) scales each new cross-attention and feed-forward outputFlamingo

Worked example

The CLIP loss on four pairs

Four images and four captions, embedded in three dimensions (real models use 512 to 1,024). After normalization the cosine similarities are the matrix in the first diagram; each image's own caption is its most similar, but image 4 also resembles caption 2 (0.843) and image 1 caption 4 (0.824). At the initial scale s = 14.29:

Image iP(caption 1)P(caption 2)P(caption 3)P(caption 4)−log P(own)
10.9090.0000.0000.0910.096
20.0000.9700.0010.0290.031
30.0000.0240.9680.0070.032
40.0130.1210.0140.8520.160

The text direction uses the columns: caption 2's softmax over images gives its own image 0.856 and image 4 0.119, a loss of 0.156. The four text losses are 0.014, 0.156, 0.015, 0.137. Averaging all eight, L = 0.0800.

The gradient ∂L/∂Sij, by the formula above:

Image iCaption 1Caption 2Caption 3Caption 4
1-0.0132+0.0001+0.0000+0.0230
2+0.0000-0.0218+0.0002+0.0070
3+0.0000+0.0062-0.0058+0.0019
4+0.0033+0.0299+0.0035-0.0344

The diagonal is negative, so gradient descent raises it. The largest push down is on the confusable pair, image 4 with caption 2 (+0.0299), and the easy negatives, around 0.0000, get almost nothing. The whole matrix sums to zero, since each softmax's probabilities sum to one.

The effect of the scale on the same similarities:

Scale s1.0014.29100.00s = 0 (chance)
Loss L1.090.082.98e-07ln 4 = 1.386

At s = 1 the logits differ by less than 1 and the loss stays near chance however good the embeddings are; at s = 100 the same embeddings give almost zero loss. Learning s lets the model sharpen as the embeddings improve, and the clamp at 100 stops it from shrinking the loss by inflating the scale alone.

Zero-shot with one prompt and with an ensemble

An image embedding is compared with three classes, each with two prompt embeddings. "Single" uses the first prompt; "ensemble" averages both normalized prompts and normalizes again. Probabilities use s = 100.

ClassCosine, singleP, singleCosine, ensembleP, ensemble
dog0.2890.0000.3350.000
cat0.9471.0000.9060.993
car0.8220.0000.8570.007

Both pick "cat". A cosine gap of 0.125 becomes a logit gap of 12.5 at s = 100, so the softmax is nearly one-hot: CLIP's zero-shot probabilities are confident and need calibration before they are read as frequencies. The ensemble moves "car" up from 3.6e-06 to 7.1e-03: averaging prompts removes some of the wording each one carries.

What fusion costs

A 32-layer language model with dmodel = 4,096, full multi-head attention and 16-bit activations, reading 100 text tokens. Each cached token costs 2 (K and V) × 32 × 4,096 × 2 bytes = 524,288 bytes, half a mebibyte. Cross-attention is placed before every 4th layer, 8 blocks in all. Attention scores counted per forward pass over the whole prompt, ignoring the causal half and the heads:

Image tokensConcat: scoresConcat: image KVCross-attn: scoresCross-attn: image KV
576 (one 336 px tile)14,623,232288 MiB780,80072 MiB
2,880 (4 tiles + overview)284,172,8001,440 MiB2,624,000360 MiB
64 (resampled latents)860,67232 MiB371,2008 MiB

With one tile, concatenation computes 32 × (100 + 576)² = 14,623,232 scores, 18.7 times the cross-attention layout's 32 × 100² + 8 × 100 × 576 = 780,800, and caches the image in all 32 layers instead of 8: 288 MiB against 72 MiB. With tiling the image dwarfs the text. Compressing to 64 tokens makes concatenation cheap again (860,672 scores), which is why token reduction and concatenation are a common pair. In exchange, cross-attention adds new parameters, close to a full layer's worth per block once its gated feed-forward layer is counted, and since the image is not in the sequence the frozen model cannot attend to individual image tokens with its own heads.

The gate at work, with hidden state x = [1.0, -0.5] and a cross-attention update of [0.4, 0.8]:

α0.00.10.52.0
tanh(α)0.0000.1000.4620.964
y[1.000, -0.500][1.040, -0.420][1.185, -0.130][1.386, 0.271]

At α = 0 the output is x exactly: the frozen language model's behaviour is preserved bit for bit at step zero, and the gate's gradient, (1 − tanh²α) · update, is largest there, so the image starts to flow in at once. tanh keeps the gate below 1, so the new path never overwhelms the residual stream.

Audio token rates

Whisper: 30 s × 16,000 Hz / 160-sample hop = 3,000 spectrogram frames, halved by the stride-2 convolution to 1,500 encoder tokens, 50 per second. A codec at 24,000 Hz with a 320-sample frame produces 75 frames per second; each codebook of 1,024 entries carries log₂ 1024 = 10 bits per frame, so a bitrate fixes the number of codebooks:

BitrateCodebooksTokens per secondTokens for 10 s
1.5 kbps21501,500
3.0 kbps43003,000
6.0 kbps86006,000
12.0 kbps161,20012,000
24.0 kbps322,40024,000

Ten seconds of speech is a few dozen words, about as many text tokens, but 6,000 codec tokens at 6.0 kbps. That gap is why audio language models predict the coarse first codebook autoregressively and fill in the rest with a cheaper model, or predict all codebooks of a frame at once.

Implementation

The CLIP loss in PyTorch. The encoders are any image and text models with pooled outputs; everything that is specific to CLIP is these lines.

import math
import torch
import torch.nn.functional as F
from torch import nn

class ClipHead(nn.Module):
    def __init__(self, image_dim, text_dim, embed_dim=512):
        super().__init__()
        self.image_proj = nn.Linear(image_dim, embed_dim, bias=False)
        self.text_proj = nn.Linear(text_dim, embed_dim, bias=False)
        self.logit_scale = nn.Parameter(torch.tensor(math.log(1 / 0.07)))

    def forward(self, image_features, text_features):     # [N, image_dim], [N, text_dim]
        u = F.normalize(self.image_proj(image_features), dim=-1)
        v = F.normalize(self.text_proj(text_features), dim=-1)
        logits = self.logit_scale.exp().clamp(max=100) * u @ v.T    # [N, N]
        target = torch.arange(len(logits), device=logits.device)
        return (F.cross_entropy(logits, target) + F.cross_entropy(logits.T, target)) / 2

class GatedCrossAttention(nn.Module):
    def __init__(self, d_model, heads):
        super().__init__()
        self.heads = heads
        self.norm = nn.LayerNorm(d_model)
        self.q = nn.Linear(d_model, d_model, bias=False)
        self.kv = nn.Linear(d_model, 2 * d_model, bias=False)
        self.out = nn.Linear(d_model, d_model, bias=False)
        self.alpha = nn.Parameter(torch.zeros(()))

    def forward(self, x, image):                          # [B, L, d], [B, S, d]
        B, L, d = x.shape
        q = self.q(self.norm(x)).view(B, L, self.heads, -1).transpose(1, 2)
        k, v = self.kv(image).view(B, image.shape[1], 2, self.heads, -1).permute(2, 0, 3, 1, 4)
        y = F.scaled_dot_product_attention(q, k, v)        # [B, H, L, d/H]: L queries over S keys
        return x + self.alpha.tanh() * self.out(y.transpose(1, 2).reshape(B, L, d))

The clamp sits on the scale, not on the parameter, so the parameter keeps receiving gradients but the logits cannot exceed 100 times a cosine. In data-parallel training, gather u and v from all ranks (with a gather that passes gradients back) before forming the logits, or each device sees only its local negatives. For concatenation, the connector is just nn.Sequential(nn.Linear(d_vision, d_model), nn.GELU(), nn.Linear(d_model, d_model)) and its output is spliced into inputs_embeds where the image placeholder tokens were. A full Flamingo block also has a gated feed-forward layer after the cross-attention.

Tradeoffs

  • Contrastive vs generative pretraining. A contrastive model gives embeddings for retrieval and zero-shot classification and is cheap to run per item, but cannot describe an image. Captioning objectives learn more fine-grained detail and generate text, at a higher cost per example. Many models combine both.
  • Softmax vs sigmoid loss. The softmax over the batch needs large batches and an all-gather; SigLIP's pairwise sigmoid works with smaller batches and less communication but treats each pair independently.
  • Batch size. More in-batch negatives tighten the InfoNCE bound and give harder negatives, but the logit matrix grows as N² and the gathered embeddings with N.
  • Concatenation vs cross-attention. Concatenation needs no change to the language model and lets every layer attend to image detail, but image tokens fill the context window and the KV cache. Cross-attention keeps the sequence short and the language model frozen, at the price of new parameters and a less direct interaction.
  • Resolution vs tokens. Documents, charts and small text need high resolution, which multiplies image tokens; pooling and resamplers save compute but lose detail. Tiling with a low-resolution overview is the usual compromise.
  • Frozen vs trained encoder. Freezing the image encoder protects its general features and saves memory; unfreezing it late in training improves fine detail at the risk of forgetting.
  • Web data. Alt text is noisy, biased and sometimes harmful; filtering and deduplication matter as much as model size, and zero-shot classifiers inherit the associations in their captions.
Why does CLIP normalize the embeddings before the dot product?

So similarities are cosines in [−1, 1] and the only way to sharpen the softmax is the learned scale. Without normalization the model could lower the loss by growing vector lengths, and image and text norms would be compared directly, mixing confidence with content.

A batch of 32,768 pairs is split over 256 GPUs without gathering embeddings. How many negatives does each image get?

127, the other captions in its local batch of 128, instead of 32,767. The loss is still valid but much easier, and the gradient sees far fewer hard negatives.

Why is Flamingo's gate initialized to zero rather than one?

At zero the network is exactly the pretrained language model, so training starts from its full text ability and the randomly initialized cross-attention can't corrupt it in the first steps. The gate opens as far as the image helps.

Connections

Read first

Read next

No page builds on this one yet.

References

  • Radford, Kim, Hallacy et al. Learning Transferable Visual Models From Natural Language Supervision. ICML 2021. arXiv:2103.00020
  • Alayrac, Donahue, Luc et al. Flamingo: a Visual Language Model for Few-Shot Learning. NeurIPS 2022. arXiv:2204.14198
  • van den Oord, Li and Vinyals. Representation Learning with Contrastive Predictive Coding. 2018. arXiv:1807.03748
  • Zhai, Mustafa, Kolesnikov and Beyer. Sigmoid Loss for Language Image Pre-Training. ICCV 2023. arXiv:2303.15343
  • Liu, Li, Wu and Lee. Visual Instruction Tuning. NeurIPS 2023. arXiv:2304.08485; and Improved Baselines with Visual Instruction Tuning, arXiv:2310.03744
  • Radford, Kim, Xu et al. Robust Speech Recognition via Large-Scale Weak Supervision. (Whisper) 2022. arXiv:2212.04356
  • Défossez, Copet, Synnaeve and Adi. High Fidelity Neural Audio Compression. (EnCodec) 2022. arXiv:2210.13438
  • PyTorch v2.15.0: F.normalize, F.cross_entropy, F.scaled_dot_product_attention.