
SSA ACT workshop - Vision Transformers
RSFAS, ANU
reticulate and torch basicsA 3 \times 3 convolution sees only a small local region. Each block expands the receptive field while halving the spatial resolution. By the time a unit sees the entire 32 \times 32 image, the feature map has shrunk to 4 \times 4, compressing spatial detail into a highly summarised representation. CNNs trade spatial detail for broader context.

ViT splits an image into patches, treating each patch like a word in a sentence. It then uses attention to learn how different patches relate to each other, before using the final representation to predict the image class.
In essence, ViT applies the transformer architecture from language models to images, replacing words with image patches.

Patchification
16 × 16).224 × 224 image produces 196 patches.Patch embedding
(N_tokens, d).
Self-attention allows each token to update its representation using information from other tokens, capturing relationships across the image.
For a token embedding matrix X (N_{tokens} \times d),
Q = XW_Q,\qquad K = XW_K,\qquad V = XW_V
Attention matrix:
A = \mathrm{softmax}\left(\frac{QK^\top}{\sqrt{d_k}}\right)
Output:
\mathrm{Attention}(Q,K,V) = AV

Transformer block
A transformer is built by stacking many transformer blocks, each containing
The output of one block is the input of the next. ViT-B/16 stacks 12 of them, with d = 768 and 12 heads, 86M parameters.

So far, every token represents an image patch. But the classifier needs one vector to represent the whole image.
[CLS] token immediately after patch embedding.[CLS] token gathers information from all image tokens.[CLS] token to the classification head.Later models often omit the class token and use mean-pooling to average the patch tokens instead. This is computationally simpler and naturally supports tasks that produce spatial outputs, such as segmentation masks.

You just saw that attention matrix compares tokens with one another but has no information about their original positions. Shuffling the tokens simply shuffles the outputs.
Attention is permutation-invariant, so we need to tell each token where it came from.
Without positional embeddings, the model becomes a bag-of-patches classifier: it may recognise a cat or a dog, but it loses information about where things are in the image.

Vision transformer variants differ along four design decisions:
| decision | options | examples |
|---|---|---|
| Tokenisation | fixed patches · hierarchical merging · convolutional stem | ViT · Swin · CoAtNet |
| Positional information | learned absolute · sinusoidal · relative bias · rotary | ViT · Transformer · Swin · RoPE |
| Attention scope | global · local windows · local then global | ViT · Swin · Hiera |
| Classification | [CLS] token · mean pooling · distillation token |
ViT · Hiera · DeiT |
| CNN | ViT | |
|---|---|---|
| Inductive bias | locality, translation equivariance | relatively little |
| Global context | receptive field grows with depth | every attention layer |
| Strengths | limited data, smaller datasets | large datasets, large-scale pretraining |
| Multimodal integration (e.g. text, audio) | Typically requires modality-specific architectures | Naturally supports a shared architecture across modalities |
# Wrap a torch model as a Keras layer
vit_layer <- function() {
vit <- torchvision$models$vit_b_16(weights = "DEFAULT") # Download the ViT from torchvision
vit$heads$head <- torch$nn$Identity() # Remove the original classification head
keras$layers$TorchModuleWrapper(vit, output_shape = tuple(NULL, 768L)) # The CLS token has 768 dimensions
}
model <- keras$Sequential(list(
keras$layers$Input(shape = tuple(224L, 224L, 3L)),
keras$layers$Permute(tuple(3L, 1L, 2L)), # NHWC -> NCHW for torch
vit_layer(),
keras$layers$Dense(1L, activation = "sigmoid")
))
invisible(model$summary())Model: "sequential"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Layer (type) ┃ Output Shape ┃ Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ permute (Permute) │ (None, 3, 224, 224) │ 0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ torch_module_wrapper │ (None, 768) │ 85,798,656 │
│ (TorchModuleWrapper) │ │ │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense (Dense) │ (None, 1) │ 769 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
Total params: 85,799,425 (327.30 MB)
Trainable params: 85,799,425 (327.30 MB)
Non-trainable params: 0 (0.00 B)
torchinfo <- import("torchinfo", convert = FALSE)
torchinfo$summary(model$layers[1]$module, verbose = 0L)==========================================================================================
Layer (type:depth-idx) Param #
==========================================================================================
VisionTransformer 768
├─Conv2d: 1-1 590,592
├─Encoder: 1-2 151,296
│ └─Dropout: 2-1 --
│ └─Sequential: 2-2 --
│ │ └─EncoderBlock: 3-1 7,087,872
│ │ └─EncoderBlock: 3-2 7,087,872
│ │ └─EncoderBlock: 3-3 7,087,872
│ │ └─EncoderBlock: 3-4 7,087,872
│ │ └─EncoderBlock: 3-5 7,087,872
│ │ └─EncoderBlock: 3-6 7,087,872
│ │ └─EncoderBlock: 3-7 7,087,872
│ │ └─EncoderBlock: 3-8 7,087,872
│ │ └─EncoderBlock: 3-9 7,087,872
│ │ └─EncoderBlock: 3-10 7,087,872
│ │ └─EncoderBlock: 3-11 7,087,872
│ │ └─EncoderBlock: 3-12 7,087,872
│ └─LayerNorm: 2-3 1,536
├─Sequential: 1-3 --
│ └─Identity: 2-4 --
==========================================================================================
Total params: 85,798,656
Trainable params: 85,798,656
Non-trainable params: 0
==========================================================================================
06-vision-transformers-ex.R — 15 minutes (5 to walk through, 10 to work)
???: freeze the backbone so that only the two dense layers learn. Check model$summary() before and after — the trainable count should fall from 86M to about 197k.
Slides URL: https://ssa-act-cv-workshop.patrickli.org/ | Canberra time