← CLIP Series Reading Guide
Deep Learning · Architectures · CLIP (Contrastive Language–Image Pre-training) Chapter 01 / 4

1. Why Contrastive Beats Captioning: Data Efficiency ($3\times \to 12\times$), WIT-400M & The $N \times N$ Alignment Matrix

How do you build a vision model that can classify samples from classes it has never observed during training across dozens of distinct test benchmarks in a single forward pass? Our first set of study notes (adapted from Yannic Kilcher's walkthrough of Radford et al., 2021) traces the core engineering discovery that made CLIP possible: why predicting exact image captions word-by-word with a Transformer language model is prohibitively slow, why relaxing the target to an unordered Bag of Words yields a $3\times$ speedup, and why switching from generation to contrastive pre-training over an $N \times N$ similarity matrix yields an additional $4\times$ leap ($12\times$ total data efficiency).

Left: Zero-Shot ImageNet accuracy vs images processed showing Transformer LM (1x), Bag-of-Words prediction (3x), and Contrastive CLIP (12x). Right: N by N matrix contrasting diagonal positive pairs I_i dot T_i against off-diagonal negatives.
Figure 1: Left: Replacing exact autoregressive caption prediction ($1\times$) with Bag-of-Words prediction ($3\times$) and finally Contrastive Pre-training ($12\times$) unlocks steep zero-shot scaling over 400M web pairs. Right: CLIP encodes $N$ images and $N$ texts and maximizes the $N$ diagonal dot products $I_i \cdot T_i$ against $N^2 - N$ off-diagonal negatives.

1. What Does "Zero-Shot" Really Mean in Vision?

In classical supervised vision (such as ResNet-50 trained on ImageNet-1K), the final layer is a fixed linear projection $\mathbf{W} \in \mathbb{R}^{d \times 1000}$ where row $k$ represents an opaque integer index $k \in \{0, \dots, 999\}$. If you evaluate that model on Stanford Cars ($196$ fine-grained make/model classes), Food-101, or EuroSAT satellite imagery, the $1{,}000$-way head is useless—you must discard it, freeze or fine-tune the backbone on labeled training images from the new dataset, and learn a new matrix $\mathbf{W}_{\text{new}}$.

As summarized in our study notes below, a zero-shot classifier handles all of those different test datasets in one go—predicting classes that were not explicitly carved out as fixed output neurons during training—by connecting images directly to natural language descriptions:

Handwritten OneNote study notes titled Contrastive Language-Image Pretraining showing the definition of zero-shot classifiers, the intuition of connecting images to text, and the 3x and 4x data efficiency graph comparing Transformer Language Model, Bag of Words prediction, and Bag of Words Contrastive.
Figure 2 (Study Notes — Part 1): Handwritten notes on zero-shot classification, why intermediate representations of an image-to-text network must encode visual semantics, and the $3\times \to 12\times$ data-efficiency progression from exact captioning to contrastive alignment.

2. The $12\times$ Data-Efficiency Discovery: Why Captioning Loses to Contrastive

Suppose we collect $400\text{ million}$ $(I, T)$ pairs from the internet (OpenAI's WIT — WebImageText corpus, built from 500,000 search queries so every Wikipedia concept appears at least 20,000 times). How should we train a neural network to connect image $I$ to text $T$?

Intuitively, if an image goes into a neural network and out comes a text caption (as in VirTex, Sariyildiz et al., 2020), then somewhere in the intermediate representation of the neural network there must be a strong representation of what is inside the image. However, when OpenAI scaled this idea up, they observed dramatic differences in sample efficiency across three training objectives:

  1. Objective 1 — Autoregressive Transformer Language Model ($1\times$ baseline): Given image features $f_\theta(I)$, train a causal Transformer decoder to predict the exact caption word-by-word:
    $$\mathcal{L}_{\text{LM}}(I, T) = -\sum_{t=1}^{|T|} \ln P_\psi(w_t \mid w_{<t}, f_\theta(I)).$$
    Why is it so slow? Because real web alt-text is noisy and idiosyncratic (e.g., "IMG_4821.JPG — Pepper the aussie pup at sunset, Canon EOS 5D f/2.8!"). Forcing the vision backbone to help predict exact punctuation, word order, camera metadata, and stop-words wastes gradient capacity on details that have nothing to do with visual recognition.
  2. Objective 2 — Bag-of-Words (BoW) Multilabel Prediction ($3\times$ more efficient): Discard word order and syntax altogether. Treat the caption as an unordered set of unique vocabulary tokens $\mathcal{B}(T) \subset \{1, \dots, V\}$. As noted in the handwritten example above: if the caption is "my cute cat" and the model predicts {cat, my, cute} in any order, it is already $100\%$ correct! Stripping out syntactic ordering immediately accelerates zero-shot ImageNet learning by $3\times$.
  3. Objective 3 — Contrastive Pre-Training / CLIP ($4\times$ faster than BoW, $12\times$ faster overall): Even Bag-of-Words prediction still asks the image encoder to predict every possible word in a $49{,}152$-token vocabulary for a single image, when a photo of a dog in a park could be validly described by thousands of different sentences. Switching from generative/predictive modeling ($P(T \mid I)$ over all strings) to discriminative/contrastive matching ("which of the $N$ texts in this batch actually accompanied image $I_i$?") produces another $4\times$ speedup—making CLIP $12\times$ more compute-efficient than autoregressive captioning!

3. How Contrastive Pre-Training Works: The $N \times N$ Alignment Grid

In our second handwritten note panel below, we trace how a single training step processes a minibatch of $N$ image–text pairs $\{(I_1, T_1), (I_2, T_2), \dots, (I_N, T_N)\}$ (for example, $I_1 = \text{photo of a dog}$ paired with $T_1 = \text{“Pepper the aussie pup”}$):

Handwritten OneNote diagram showing an image of a dog passed through an Image Encoder to produce vectors I_1 through I_n, text Pepper the aussie pup passed through a Text Encoder to produce T_1 through T_N, and the N by N grid of dot products I_i T_j.
Figure 3 (Study Notes — Part 2): Passing $N$ images through the Image Encoder and $N$ captions through the Text Encoder produces $N$ image vectors and $N$ text vectors whose pairwise dot products form an $N \times N$ matrix.

Let us walk through the exact mechanics illustrated in the diagram:

4. Why Contrastive Learning Demands Massive Mini-Batches ($N = 32{,}768$)

Notice the crucial note on Page 2 of our handwritten sheets: "This requires larger mini-batches." Why does CLIP train with a global batch size of $N = 32{,}768$ across hundreds of GPUs instead of $N = 64$ or $N = 256$?

In in-batch contrastive learning, the other $N - 1$ items in the minibatch are the negative distractors!

Combinatorial scaling of positive diagonal pairs versus off-diagonal in-batch negatives as minibatch size $N$ increases.
Minibatch Size $N$ Encoder Forward Passes ($2N$) Positive Diagonal Pairs ($N$) Off-Diagonal Negative Pairs ($N^2 - N$) Negatives per Positive ($N - 1$)
$N = 32$ (Toy batch) $64$ $32$ $992$ $31 : 1$ (Coarse topic matching only)
$N = 256$ (Single GPU) $512$ $256$ $65{,}280$ $255 : 1$
$N = 4{,}096$ (Multi-node) $8{,}192$ $4{,}096$ $16{,}773{,}120$ $4{,}095 : 1$
$N = 32{,}768$ (OpenAI CLIP) $65{,}536$ $32{,}768$ $1{,}073{,}709{,}056$ ($\approx 1.07\text{B}$) $32{,}767 : 1$ (Fine-grained semantics)

5. Python Simulation: $N \times N$ Contrastive Matrix & Hard-Negative Pressure

Below is a runnable Python script verifying the $N \times N$ cosine similarity matrix and showing how in-batch negative count $N - 1$ sharpens the gradient signal on the ground-truth diagonal:

import math

def matmul_nt(I_e: list[list[float]], T_e: list[list[float]]) -> list[list[float]]:
    """Compute N x N cosine similarity matrix I_e @ T_e.T."""
    return [[sum(a * b for a, b in zip(row_i, row_t)) for row_t in T_e] for row_i in I_e]

def l2_norm(v: list[float]) -> list[float]:
    s = math.sqrt(sum(x * x for x in v))
    return [x / s for x in v]

# 3 aligned (Image, Text) pairs in a 3D toy embedding space:
# Pair 0: Aussie pup ("Pepper the aussie pup")
# Pair 1: Tabby cat ("my cute cat")
# Pair 2: Sports car ("red convertible on highway")
I_e = [
    l2_norm([0.92, 0.25, -0.10]),
    l2_norm([0.20, 0.95, -0.05]),
    l2_norm([-0.15, -0.10, 0.98]),
]
T_e = [
    l2_norm([0.90, 0.28, -0.08]),
    l2_norm([0.24, 0.93, -0.04]),
    l2_norm([-0.12, -0.08, 0.99]),
]

S = matmul_nt(I_e, T_e)
for i, row in enumerate(S):
    formatted = ", ".join(f"{val:+.3f}" for val in row)
    print(f"Row I_{i}: [{formatted}]  (diagonal I_{i}·T_{i} = {row[i]:+.3f})")

# Verify every diagonal positive strictly exceeds all off-diagonal negatives
for i in range(3):
    for j in range(3):
        if i != j:
            assert S[i][i] > S[i][j] and S[i][i] > S[j][i]
print("Verified: All N diagonal pairs beat all N^2 - N off-diagonal pairs!")