Module 25 · Part VII · Beyond Text

Vision Transformers and Contrastive Learning

Images as Tensors, Convolutions, the Vision Transformer, Contrastive Learning With CLIP and SigLIP, and Zero-Shot Classification

By the end you can turn an image into tokens, and explain how image and text encoders are aligned.

Builds onModule 8·
Check yourself ▸

Before you start, can you already…

  • ☐choose the right family for a task and explain why decoder-only won for generation. (Module 8)

If any is shaky, the linked module teaches it.

Open in Colab
4 Hours·Elective·Specialized·0/4

This module shows how the same transformer reads images, and how image and text models are trained to understand each other: from convolutional networks to the Vision Transformer, which treats an image as a sequence of patches, and contrastive learning, which aligns image and text encoders using only pictures and their captions. Module 26 uses these encoders to give language models sight.

25.1 Images, Convolutions, and Inductive BiasRun this section

A colour image is an H×W×CH \times W \times C tensor (section 1.1), with C=3C = 3 channels (red, green, blue) of brightnesses, often scaled to 0 to 1. A 224 by 224 image holds 150,528150{,}528 numbers. Fed as one long vector to an MLP, the first layer alone would need hundreds of millions of weights, and would have to learn separately that a cat's ear looks the same in every corner.

Convolutions

A fixes both. It slides a small grid of weights, a filter or kernel (say 3 by 3), across the image, writing the dot product with each patch underneath into a feature map. A handful of weights, reused everywhere, detects the same pattern, such as a vertical edge, wherever it occurs. A layer has many filters, and the next layer's filters combine their maps: edges into textures, textures into parts, parts into objects.

Figure 25.1
A filter slides across the image

Each output cell is the dot product of the 3 × 3 filter with the patch under it. Hover an output cell to see its patch.

A 10 × 10 image (a white square on black) gives an 8 × 8 feature map with no padding. Red is positive, green negative.
Predict, then try

Choose the vertical-edge filter, whose left column is negative and right column positive. Before applying it, predict where the output feature map will be bright on an image of a white square on a black background.

In words. A small black-and-white image, an editable 3 by 3 filter (presets: vertical edge, horizontal edge, blur, sharpen) and the resulting feature map. Hovering an output cell highlights its pixel patch and shows the dot product.

A convolutional neural network (CNN) stacks convolutions, usually with pooling layers that shrink the maps. It builds in two assumptions: nearby pixels matter most, and a pattern means the same anywhere. Such built-in assumptions are inductive biases; they let CNNs learn from relatively little data, and CNNs dominated vision for a decade before transformers.

25.2 The Vision TransformerRun this section

The Vision Transformer (ViT) (Dosovitskiy et al., 2020) is the ordinary transformer of Module 7 with one new first step, which turns an image into a sequence.

Architecture Flowchart
Diagram: Image, H x W x C e.g. 224 x 224 x 3 (RGB); Cut into non-overlapping P x P patches P = 16 gives (224/16)^2 = 196 patches; Flatten each patch into one vector 16 x 16 x 3 = 768 numbers; Multiply by the same matrix E 768 -> d_model; Put a learned [CLS] vector in front and add a position vector to each token; Ordinary transformer encoder layers; Output sequence z_0, z_1, ..., z_N one vector per patch, plus [CLS]

In words. Cut the image into small square patches, flatten each, multiply by one shared matrix to the model's width, add a position vector, and run a normal transformer encoder. Each patch is one token.

The Patch Formulas

For an image :

  1. Cut it into non-overlapping P×PP \times P patches, image area over patch area:
N=HWP2patchesN = \frac{HW}{P^2} \quad \text{patches}

For 224×224224 \times 224 and P=16P = 16, N=196N = 196, a 14×1414 \times 14 grid.

  1. Flatten each patch into xp(i)∈RP2C\mathbf{x}_p^{(i)} \in \mathbb{R}^{P^2 C}: 16⋅16⋅3=76816 \cdot 16 \cdot 3 = 768 numbers.
  2. Multiply each by the same learned matrix EE to width dd, put a learned class token [CLS] in front, and add learned position vectors:
z0=[xclass; xp(1)E; … ; xp(N)E]+Epos\mathbf{z}_0 = \left[\mathbf{x}_{\text{class}}; \, \mathbf{x}_p^{(1)}E; \, \dots; \, \mathbf{x}_p^{(N)}E\right] + E_{pos}

, and the semicolons stack rows, so z0\mathbf{z}_0 and Epos∈R(N+1)×dE_{pos} \in \mathbb{R}^{(N+1) \times d} have one row for the class token and one per patch. The encoder's final class-token vector summarizes the image, for classification, say.

Figure 25.2
From image to tokens

The image is cut into 16 × 16 patches. Each patch is a stack of three planes, red, green and blue; unstacked, they are laid end to end as one list of 16 × 16 × 3 = 768 numbers. One shared matrix E turns that list into a token.

A 64 × 64 image, so it has 16 patches; a 224 × 224 image cut the same way has 196. The last row shows the sequence the encoder receives: the learned [CLS] token, then one token per patch in reading order.
Predict, then try

A 224 × 224 image with 16 × 16 patches gives 196 tokens. Before changing the patch size, predict the token count with 8 × 8 patches, and what that does to the cost of attention.

In words. A small image cut into a four-by-four grid of 16-pixel patches. Each patch's red, green and blue planes of 256 numbers are laid end to end into 768 numbers, and multiplying by E makes one token. The encoder receives the class token, then one token per patch in reading order.

Multiplying by EE plays the role of section 1.5's embedding lookup. Beyond the patching, ViT has far fewer inductive biases than a CNN: its positions are learned and every patch attends to every other from the first layer, so it must learn from data which patches relate. The ViT paper found this pays off with a lot of data, while CNNs do better on smaller datasets.

Measured (lab 6, experiments/vit_patchify.py). The claim that ViT is "the standard transformer with almost no changes" can be tested, and it holds. Cutting a 224×224224 \times 224 RGB image into P=16P = 16 patches gives a 14×1414 \times 14 grid of vectors, each 162×3=76816^2 \times 3 = 768 numbers long: 196196 tokens. Adding a [CLS] token makes 197197. Passing those 197197 vectors through src/transformer/transformer_block.py, with no changes to that file, gives an output of shape (1,197,64)(1, 197, 64): one image, 197197 tokens, each 6464 wide. The block cannot tell whether its input came from text or pixels. The only thing it requires of its input is the vector width, d_model.

25.3 Contrastive Learning: CLIP and SigLIPRun this section

Classification needs labelled images, which limits both the data's scale and what is learned (a fixed list of classes). CLIP (Radford et al., 2021) instead learns from the web's billions of captioned images, with no labelling.

It trains a ViT for images and a transformer for text, each producing one vector per input, projected to a shared width and normalized to length 1. For a batch of NN image-caption pairs, all cosine similarities (section 1.4), scaled by a learned temperature τ\tau, form a matrix with the true pairs on the diagonal. Softmax cross-entropy over the captions for each image, and over the images for each caption, averaged, gives the (contrastive) loss:

LCLIP=−12N∑i=1N[log⁡esii/τ∑jesij/τ+log⁡esii/τ∑jesji/τ]\mathcal{L}_{\text{CLIP}} = -\frac{1}{2N} \sum_{i=1}^{N} \left[ \log \frac{e^{s_{ii}/\tau}}{\sum_{j} e^{s_{ij}/\tau}} + \log \frac{e^{s_{ii}/\tau}}{\sum_{j} e^{s_{ji}/\tau}} \right]

where sijs_{ij} is the similarity of image ii and caption jj. Each true pair is pulled together and pushed from the other N−1N - 1, which serve as free negative examples; bigger batches give more negatives and better models, and CLIP used 32,768.

SigLIP (Zhai et al., 2023) replaces the softmax with an independent sigmoid (section 3.6) on each entry, labelled 1 on the diagonal and 0 elsewhere. With no normalizing across entries, very large batches split across devices more easily, and SigLIP encoders are now common in vision-language models.

Figure 25.3
Contrastive training pulls each image to its own caption

Five image embeddings (solid) and five caption embeddings (hollow), normalized to the unit circle in 2D, and their similarity matrix. Play to train on the contrastive loss.

Two dimensions so the vectors can be drawn; real encoders use hundreds. Temperature τ = 0.3. The diagonal of the matrix holds the true pairs.
Predict, then try

Before training starts, the similarity matrix is noise. Predict what it should look like after training, and what the loss would be if every image matched its own caption perfectly and no other.

In words. Image and caption embeddings as two sets of arrows, beside their similarity matrix. Training on the contrastive loss rotates matching arrows together, brightens the diagonal and lowers the loss.

25.4 Zero-Shot ClassificationRun this section

A trained CLIP model can classify images into categories it never trained on: zero-shot classification.

  1. Write one caption per candidate class, from a template: "a photo of a dog", "a photo of a cat", "a photo of a car".
  2. Encode the captions with the text encoder and the image with the image encoder.
  3. Predict the class whose caption is most similar to the image.

Classes are chosen at use time in plain language. On ImageNet, CLIP's zero-shot accuracy matched a ResNet-50 trained on its 1.28 million labelled images, without seeing any (Radford et al., 2021). Template wording matters, as prompt wording does (section 13.2), and averaging several templates helps.

More important here, contrastive training yields an image encoder whose vectors already line up with the meaning of words, the natural front end for a vision-language model (section 26.1).

Exercises

Apply

How many patches does a 336 × 336 image give with 14 × 14 patches, and how many numbers is each flattened patch?

Apply

A 3 × 3 filter with all weights 19\tfrac19 is applied to a patch of pixels whose values are 0 except for a single 9 in the centre. What is the output at that position?

Apply

In a batch of 3 image-caption pairs with temperature τ=1\tau = 1, image 1's similarities to the three captions are [2,0,0][2, 0, 0]. What is its image-to-text loss term, −log⁡-\log of the softmax weight on its own caption?

Check

Why do the other pairs in a CLIP batch count as negative examples, and why does a larger batch help?

Extend

Describe how to classify an image into 1,000 categories with a CLIP model and no training, and name one way the result depends on choices other than the model.

Core Academic References

  • LeCun et al. (1998): Gradient-Based Learning Applied to Document Recognition. Proceedings of the IEEE, 86(11): 2278–2324.
  • He et al. (2016): Deep Residual Learning for Image Recognition. CVPR 2016: 770–778.
  • Dosovitskiy et al. (2020): An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale (ViT). ICLR 2021. arXiv:2010.11929
  • Oord, Li & Vinyals (2018): Representation Learning with Contrastive Predictive Coding (InfoNCE). arXiv:1807.03748
  • Radford et al. (2021): Learning Transferable Visual Models From Natural Language Supervision (CLIP). ICML 2021. arXiv:2103.00020
  • Zhai et al. (2023): Sigmoid Loss for Language Image Pre-Training (SigLIP). ICCV 2023. arXiv:2303.15343

Measured labs

These labs test what this module says by running real code. Each card states the idea, what the experiment found, and what it does not prove. The numbers come from experiments/results.json, the output of running experiments/run_all.sh on the tiny-transformers reference code at version 137f7ef. The project checks fail if a measured number in the module text stops matching that file.

Lab 6 — a vision transformer uses the same block on image patches

What the module says

Section 25.2 says a Vision Transformer (ViT) is the ordinary Transformer with small changes: cut the image into square patches, turn each patch into a vector, add position information, and run the usual blocks.

What the experiment found

The repository's transformer_block.py was used unchanged. A 224×224 image cut into 16×16 patches gives 196 patch tokens; with one extra CLS token (a summary slot) that is a (1,197,64)(1, 197, 64) input. The block cannot tell whether its tokens came from text or pixels. The 2×2 patch merge from section 26.1 turns 576 tokens into 144, the 75% reduction the module states.

patch grid, 224224 pixels at patch size P=16P = 16
14 × 14
numbers per patch
768

16 × 16 pixels × 3 color channels

patch tokens
196

plus one CLS token

2×2 patch merge
576 → 144

75% fewer tokens

a 2048×20482048 \times 2048 image at P=16P = 16
16,384

tokens: why section 26.2 treats high resolution as a problem

What this does not prove

The image is a fixed synthetic picture (a gradient with two rectangles). The lab checks the patch arithmetic and that the block accepts the result. It says nothing about how well a ViT sees: nothing here is trained, and the patch projection and positions are random.

Run it yourself

bash
cd tiny-transformers && .venv/bin/python experiments/vit_patchify.py

The code being tested