Chapter 10: CNNs & Sequence Models
Master specialized neural network architectures for structured data. Learn convolutional networks that exploit spatial patterns in images, recurrent networks that process sequences with memory, and attention mechanisms that power modern transformers.
- Topics
- 4
- Demo
- 1
- Videos
- 3
- Min read
- 35
Chapter Overview
Different data types have different structures that specialized architectures can exploit. Images have spatial locality—nearby pixels are related. Sequences like text and time series have temporal dependencies—earlier elements influence later ones.
Convolutional Neural Networks (CNNs) revolutionized computer vision by using learnable filters that detect local patterns regardless of their position in the image. The same edge detector works whether the edge appears in the top-left or bottom-right. This translation equivariance, combined with parameter sharing, makes CNNs incredibly efficient for visual data.
Recurrent Neural Networks (RNNs) process sequences by maintaining a hidden state that acts as memory, carrying information from earlier time steps. However, vanilla RNNs struggle with long-range dependencies due to vanishing gradients. LSTMs and GRUs solve this with gating mechanisms that control information flow.
The Transformer architecture replaced recurrence with self-attention, allowing direct connections between any positions in a sequence. This parallel processing is faster and handles long-range dependencies better. Transformers now dominate both NLP (BERT, GPT) and increasingly vision (ViT).
This chapter covers:
- Convolutions: Learnable filters that extract local features through sliding-window operations
- Pooling: Downsampling operations that provide translation invariance and reduce computation
- RNNs/LSTMs: Recurrent architectures that maintain memory across sequence elements
- Attention: Self-attention mechanisms that enable transformers to model complex dependencies
Chapter Roadmap
Click any topic to jump in
Convolutions & CNNs
Learnable filters that exploit spatial locality — parameter sharing, translation equivariance, and feature hierarchies.
Pooling
Spatial downsampling for translation invariance — max pooling, average pooling, and global average pooling.
RNNs & LSTMs
Recurrent architectures with memory — hidden states, gating mechanisms, and long-range sequence dependencies.
Attention & Transformers
Self-attention and multi-head attention — parallel sequence processing that replaced recurrence in modern NLP and vision.
Topics
4 topics in this chapter
The previous chapter, Neural Networks, built fully connected MLPs and trained them with backpropagation and Adam. They reached about 98 percent on MNIST, but the design breaks down on real images. A 224 by 224 color photo has 150,528 inputs, so a single fully connected layer of 1,000 units already needs 150 million weights. Worse, an MLP treats a cat in the top-left corner and a cat in the bottom-right as unrelated patterns, to be learned twice. Images have structure: nearby pixels belong together, and the same edge or texture can appear anywhere. Can a layer exploit that?
This chapter, CNNs and Sequence Models, builds architectures around the structure of their data. This topic starts with the convolution, a small filter slid across the image and shared by every position. We then cover stride and padding, which set the output size, and feature maps, the stack of channels each layer produces. The topic closes with how convolutions, activations, and pooling assemble into CNN architectures from LeNet to VGG and ResNet.
Definition
A convolutional layer slides a small learnable kernel over its input and, at each position, computes the sum of elementwise products between the kernel and the input patch beneath it. Each kernel produces one feature map; a layer with kernels over input channels has parameters, shared across every spatial position.
In this topic
- 1Convolution Operation
- 2Stride & Padding
- 3Feature Maps
- 4CNN Architecture
Convolution Operation
In the formula above, the input is an image or feature map, the kernel is a small grid of weights, often 3 by 3, and is one output value: lay the kernel over the patch starting at row , column , multiply overlapping entries, and sum. Sliding over every position gives the output map. Deep learning libraries compute this cross-correlation without flipping the kernel; since the weights are learned, the distinction does not matter. Two properties make it powerful: local connectivity, since each output sees only a small patch, and weight sharing, since one kernel is reused everywhere. Shifting the input shifts the output, which is translation equivariance.
The 2D convolution slides a kernel of size over an input of size , producing an output of size . In the frequency domain, convolution becomes pointwise multiplication: , which is why FFT-based convolution is versus for direct computation — but GPU hardware makes direct convolution faster for typical kernel sizes.
3×3 kernel K=[[1,0,-1],[1,0,-1],[1,0,-1]] applied to image patch [[10,20,30],[10,20,30],[10,20,30]]. Output?
Stride & Padding
Stride is how far the kernel moves between positions; padding adds a border of zeros around the input. For input width and kernel size , the output width is . Without padding, every layer trims the border, and edge pixels are covered by fewer positions than central ones. 'Same' padding, for odd at stride 1, keeps the size unchanged. Stride 2 roughly halves each spatial dimension, quartering the computation of later layers, and is a common way to downsample. When the formula does not divide evenly, the last partial window is dropped.
The output size formula encodes the interplay of input width , kernel size , padding , and stride . 'Same' padding sets to preserve spatial dimensions when . A stride-2 convolution halves spatial dimensions, performing downsampling with learnable parameters — unlike pooling which uses fixed operations. The total FLOPs for a conv layer is .
Input 32×32, kernel 5×5, stride 2, no padding. Output size?
Feature Maps
Each kernel produces one feature map, a 2D grid showing where its pattern appears. A layer stacks many kernels, so its output has one channel per kernel. Each kernel spans all input channels: a 3 by 3 kernel on a 64-channel input has 64 times 9 weights plus a bias, mixing every channel's evidence at each location. Early layers detect edges and color blobs; deeper layers combine those maps into textures, parts, and objects. Networks typically increase the number of channels as they downsample, trading spatial resolution for richer features. Weight sharing keeps the parameter count independent of image size, unlike a fully connected layer.
Each of output filters convolves across all input channels and sums the results: the kernel tensor has shape , giving parameters. A fully connected layer mapping the same input to a output would need weights, a factor of more: about for a map with kernels. This weight sharing is the key insight: the same edge detector is reused at every position.
Conv layer: 64 input channels, 128 kernels (output channels), 3×3 kernels. How many parameters?
CNN Architecture
A classic CNN alternates convolution, activation, and downsampling stages: spatial size shrinks while channel count grows. A classifier head, flattening plus dense layers or global average pooling, then produces class scores. LeNet (1998) read digits with two conv layers; AlexNet (2012) scaled this up on GPUs and won ImageNet. VGG (2014) stacked only 3 by 3 convolutions: two of them cover a 5 by 5 region and three cover 7 by 7, with fewer parameters and more nonlinearities than one large kernel. ResNet (2015) added skip connections, , so networks with over 100 layers train. Layer counts include only layers with weights.
A typical CNN repeats conv-ReLU-downsample stages. If each stage halves spatial dimensions, after stages the spatial size is , while channels grow from to roughly . ResNets add skip connections , so each block only learns a residual and gradients flow directly through the identity path. He et al. (2015) found that without them, a 34-layer plain network had higher training error than an 18-layer one, a degradation problem that residual connections removed, allowing networks of 152 layers and more.
VGG16 has 13 conv layers and 3 dense layers. Why is it called VGG-16?
Theory Exercise
Problem:
A convolutional layer with a 3×3 kernel connecting 64 input channels to 64 output channels is applied to a 56×56 feature map. Compare its parameter count and per-position connectivity to a fully connected layer mapping the same flattened input to the same flattened output. Why is parameter sharing the key win?
Hints:
- Conv params depend on kernel size and channels, not spatial size
- An FC layer connects every input element to every output element
- Translation equivariance means the same filter is reused at every position
Coding Exercise
Problem:
Build a small CNN in PyTorch (two conv-ReLU-pool stages followed by a linear head) for 28×28 grayscale images, run one forward pass on a random batch, and print the output shape plus the total trainable parameter count.
Hints:
- nn.Conv2d(in_channels, out_channels, kernel_size, padding=1) keeps spatial size with a 3×3 kernel
- nn.MaxPool2d(2) halves height and width each time
- Sum p.numel() for p in model.parameters() if p.requires_grad to count parameters
Related Problems on PixelBank
The previous topic, Convolutions and CNNs, showed that a convolutional layer keeps the spatial size of its input when padded, while the number of channels grows. Left alone, that is expensive: a 224 by 224 map with 256 channels holds 12.8 million values per image. It is also more detail than classification needs. To say that an image contains a cat, a network needs to know that cat-like features are present somewhere, not their exact pixel positions. How can a network summarize a region of a feature map, shrinking it while keeping what matters?
Pooling answers this with a fixed, parameter-free summary over small windows. This topic starts with max pooling, which keeps the strongest response in each window and adds some tolerance to small shifts. Average pooling keeps the mean instead. Global average pooling averages each whole channel to a single number and replaced the huge dense heads of earlier networks. The topic closes by comparing pooling with strided convolutions, the learnable alternative most modern architectures prefer.
Definition
A pooling layer downsamples each feature map independently by sliding a window, typically 2 by 2 with stride 2, and replacing each window by a summary statistic: the maximum for max pooling or the mean for average pooling. It has no learnable parameters. Global average pooling averages each entire channel, mapping a tensor to a length- vector.
In this topic
- 1Max Pooling
- 2Average Pooling
- 3Global Average Pooling
- 4Stride in Conv vs Pooling
Max Pooling
Max pooling slides a window, usually 2 by 2 with stride 2, over each channel and keeps the largest value in each window, halving height and width. Because a convolutional feature map records how strongly a pattern appears at each location, the maximum answers whether the pattern appears anywhere in this window. Small shifts of the input often leave the maximum unchanged, so max pooling adds some local translation invariance, though not much. In backpropagation, the whole gradient flows to the winning position and the others get zero. It has no parameters, and it was the standard downsampling step in CNNs from LeNet through VGG.
Max pooling over a window computes . Its gradient routes entirely to the max element: . This selective routing means only the strongest activation contributes to the gradient, which can be interpreted as a form of attention — the network learns which spatial positions matter most.
2×2 max pooling on [[1,3],[5,2]]. Result?
Average Pooling
Average pooling replaces each window with the mean of its values, so every activation contributes equally. The output is smoother and keeps information about overall activation strength, but a single strong response gets diluted by weak neighbors, so it is less selective than max pooling. In backpropagation, the gradient is split equally among all positions in the window, one quarter each for 2 by 2. Average pooling was used in LeNet-5 and is mostly found today in its global form at the end of a network. Within the body of a network, max pooling or strided convolution usually works better for detecting whether a feature is present.
Average pooling computes , distributing the gradient equally to all positions: . This uniform distribution preserves information about the overall activation level but loses spatial precision. The choice between max and average pooling trades off selectivity against information preservation.
2×2 average pooling on [[1,3],[5,2]]. Compare to max pooling result.
Global Average Pooling
Global average pooling (GAP) averages each channel over its entire spatial extent, turning a tensor into a vector of numbers, one per feature detector. Introduced in Network in Network (2013), it replaces flattening followed by large dense layers. A final linear layer then maps the values to class scores. GAP has no parameters, removes most of the classifier head's weights, and acts as a structural regularizer. It also lets one network accept different input sizes, because the output length depends only on the channel count. ResNet, DenseNet, and EfficientNet all end with GAP. The cost is discarding where features appear.
Global average pooling maps by averaging each channel completely: . This replaces flatten + dense, reducing parameters from to for output classes — a reduction. It also acts as a structural regularizer since there are no spatial parameters to overfit, and makes the model invariant to input size at inference time.
Final conv layer: 512 channels, each 7×7. Compare: flatten vs global average pooling.
Stride in Conv vs Pooling
There are two ways to downsample: a pooling layer, or a convolution with stride 2. A strided convolution computes its output only at every second position, so it halves the spatial size while learning how to summarize each region, instead of applying a fixed max or mean. The All Convolutional Net (2014) showed that replacing max pooling with strided convolutions can match accuracy with a simpler architecture. Most modern CNNs downsample mainly with strided convolutions, though ResNet still applies one max pooling after its first layer and ends with global average pooling. The trade-off is parameters and computation, since pooling is free and a strided convolution is not.
A stride-2 convolution with a kernel has learnable weights for downsampling, whereas max pooling has zero. The learnable downsampling can adapt to the data: the network might learn to preserve edges while discarding texture. Modern architectures downsample mainly with strided convolutions; ResNet does so at the start of each stage, keeping just one max pool after its first layer plus a final global average pool, and the All Convolutional Net (Springenberg et al., 2014) removed max pooling entirely without losing accuracy on CIFAR-10.
Replace 2×2 max pooling with conv layer for downsampling. What settings?
Theory Exercise
Problem:
A modern CNN designer wants to downsample a feature map by 2× and is choosing between 2×2 max pooling and a stride-2 3×3 convolution. State the parameter cost, the gradient behavior, and one reason to pick each.
Hints:
- How many learnable parameters does max pooling add?
- Where does the gradient flow in max pooling vs a strided conv?
- Which one can adapt its downsampling to the data?
Coding Exercise
Problem:
Using PyTorch, take a single 1×1×4×4 feature map and apply 2×2 max pooling, 2×2 average pooling, and global average pooling. Print each result so the contrast between the operations is clear.
Hints:
- nn.MaxPool2d(2) and nn.AvgPool2d(2) both use a 2×2 window with stride 2
- Global average pooling = mean over the spatial dimensions (or nn.AdaptiveAvgPool2d(1))
- Use a fixed tensor so the printed numbers are reproducible
Related Problems on PixelBank
The previous topics, Convolutions and Pooling, exploited the grid structure of images, where a fixed-size input is processed in one pass. Much data is not like that. A sentence can have 5 words or 500, a sensor stream never ends, and the meaning of a word depends on what came before it. An MLP or CNN with a fixed input size has no natural way to read a sequence of arbitrary length, or to carry information from step 3 to step 300. How can a network keep a running memory of everything it has read so far?
Recurrent neural networks keep a hidden state that is updated at every step from the previous state and the current input, using the same weights each time. This topic starts with the RNN cell and why its gradients vanish over long sequences. The LSTM fixes that with gated, additively updated cell state. The GRU simplifies the LSTM to two gates. The topic closes with bidirectional RNNs, which read the sequence in both directions. Attention, the next topic, replaces recurrence altogether.
Definition
A recurrent neural network processes a sequence by updating a hidden state, , with the same parameters at every step, so summarizes the inputs so far. Gated variants, the LSTM and GRU, use learned sigmoid gates to control what the state keeps, forgets, and outputs, which lets gradients survive across long sequences.
In this topic
- 1RNN Cell
- 2LSTM Cell
- 3GRU (Gated Recurrent Unit)
- 4Bidirectional RNNs
RNN Cell
At each step, the RNN cell combines the previous hidden state and the current input : multiply each by its weight matrix, and , add the bias , and apply tanh. The same weights are used at every step, so the parameter count does not depend on sequence length. Training unrolls the loop into a deep network with one layer per step and backpropagates through time. Gradients reaching early steps are multiplied by and a tanh derivative once per step, so over long sequences they usually vanish, or with large weights explode. Gradient clipping fixes explosion, but not vanishing.
The RNN update applies the same weight matrix at every time step. The gradient through steps involves . If the spectral radius , gradients vanish as ; if , they explode. Gradient clipping caps the norm: , preventing explosion but not vanishing.
RNN with hidden size 128 processes sequence of length 50. How many times is the same weight matrix applied?
LSTM Cell
The LSTM, from Hochreiter and Schmidhuber (1997), adds a cell state alongside the hidden state, updated additively: . Three sigmoid gates, each computed from and , control the flow: the forget gate decides how much old memory to keep, the input gate how much of the new candidate to write, and the output gate how much of the cell to expose as . Along the cell path, the gradient is multiplied by the forget gate, not by a weight matrix and squashing derivative, so it survives many steps when the gate stays near 1.
The LSTM cell state update is additive. Along this direct path, (elementwise), which stays near 1 when forget gates are close to 1, with no weight matrix or squashing derivative in the product. With 4 gate blocks, each with input weights and recurrent weights , an LSTM has about parameters, a vanilla RNN. A common trick (Jozefowicz et al., 2015) initializes the forget-gate bias to 1 so the default behavior is remembering.
LSTM forget gate outputs 0.9 for 10 steps. How much of initial cell state remains?
GRU (Gated Recurrent Unit)
The GRU, from Cho et al. (2014), simplifies the LSTM. It merges the cell and hidden states and uses two gates instead of three. The update gate interpolates between keeping the old state and writing a new candidate, , playing the forget and input roles at once. The reset gate controls how much of the old state feeds into the candidate. With three weight blocks instead of the LSTM's four, a GRU has about three quarters of the parameters and runs faster. Comparative studies find neither consistently better, so the choice is empirical.
GRU combines the forget and input gates into a single update gate : . This constraint reduces parameters to — 75% of an LSTM. Empirically, GRUs match LSTM performance on most benchmarks while training faster, though LSTMs retain an edge on tasks requiring very long-term memory.
LSTM has 4 gate weight matrices. GRU has how many? Compare parameter count for hidden size 256.
Bidirectional RNNs
A standard RNN's state at step summarizes only the inputs up to , but the meaning of a word often depends on what follows it. A bidirectional RNN runs two independent RNNs, one left to right and one right to left, and concatenates their hidden states at each position, so every position's representation reflects the whole sequence. This doubles the parameters and state size. It suits tasks where the full input is available before any output is needed, such as named entity recognition, tagging, or encoding a sentence for translation. It cannot be used for generation or real-time streaming, where future inputs do not exist yet.
A BiLSTM concatenates forward hidden state and backward hidden state to form . This doubles the representation size and parameter count but captures both left and right context at every position. For sequence labeling tasks like NER, BiLSTM achieves higher accuracy because the label at position depends on both past and future tokens.
Sentence: 'The bank by the river was steep.' Does 'bank' mean financial or riverbank? Why does BiLSTM help?
Theory Exercise
Problem:
Explain why a vanilla RNN suffers from vanishing gradients over long sequences, and how the LSTM cell-state update fixes it. Use the gradient of the state with respect to an earlier state in your argument.
Hints:
- Write out how the gradient propagates through T time steps in a vanilla RNN
- What role does the spectral radius of W_hh play?
- Compare the multiplicative RNN recurrence to the additive LSTM cell-state recurrence
Coding Exercise
Problem:
Use PyTorch's nn.LSTM to encode a batch of variable-content sequences. Feed a random batch of shape (batch, seq_len, input_dim), then print the output sequence shape and the final hidden-state shape to confirm how an LSTM summarizes a sequence.
Hints:
- nn.LSTM(input_size, hidden_size, batch_first=True) expects input (batch, seq, feat)
- It returns (output, (h_n, c_n)); output is per-timestep, h_n is the last hidden state
- Try bidirectional=True and observe the hidden size doubling
Related Problems on PixelBank
The previous topic, RNNs and LSTMs, carried information along a sequence one step at a time. That design has two costs. Every piece of information must survive a long chain of updates to reach a distant position, and even gated cells lose detail over hundreds of steps. And because step needs step , the computation cannot run in parallel across the sequence, which makes training slow on modern hardware. What if every position could look directly at every other position and decide which ones matter?
Attention does exactly that. This topic starts with scaled dot-product attention: each position issues a query, compares it with every position's key, and takes a weighted average of their values. Multi-head attention runs several of these in parallel to capture different relationships. The Transformer architecture stacks attention with feedforward layers, residual connections, and positional encodings, and drops recurrence entirely. The topic closes with pre-trained Transformers such as BERT and GPT, which now dominate language and increasingly vision.
Definition
Scaled dot-product attention maps queries , keys , and values to outputs: each query's dot products with all keys, divided by , pass through a softmax to give weights, and the output is the weighted average of the values. Self-attention derives , , and from the same sequence, so every position can directly attend to every other.
In this topic
- 1Attention Mechanism
- 2Multi-Head Attention
- 3Transformer Architecture
- 4Pre-trained Transformers
Attention Mechanism
Attention is a soft lookup. Each query vector is compared with every key by a dot product, measuring how relevant that position is. The scores are divided by , the key dimension, and a softmax turns them into weights that sum to 1. The output is the weighted average of the value vectors. The scaling matters because dot products of random -dimensional vectors have standard deviation ; unscaled, large scores push the softmax toward one-hot outputs with tiny gradients. Every position attends to every other in one step, so path length is constant, but computing all pairs costs time and memory that grow quadratically with sequence length.
Scaled dot-product attention computes in time and memory for sequence length and dimension . The scaling prevents the dot products from growing as in variance, which would push softmax into saturation where gradients vanish. Without scaling, attention weights approach one-hot vectors as grows, losing the ability to attend to multiple positions.
Q=[1,0], K=[[1,0],[0,1]], V=[[10],[20]], d_k=2. Compute attention output.
Multi-Head Attention
A single attention operation produces one weighted average per position, so it can capture only one kind of relationship at a time. Multi-head attention runs attention heads in parallel. Each head projects the input into its own smaller query, key, and value space of dimension , attends there, and the outputs are concatenated and mixed by an output projection. Because the head dimension shrinks as heads are added, the total cost stays about the same as one full-width head. With four projections, the layer has about parameters. Heads often specialize, but many prove redundant and can be pruned.
Multi-head attention splits into heads of dimension , computes attention independently per head, then concatenates and projects. Total parameters: (for ) plus biases, independent of . Each head works in a different learned subspace, enabling simultaneous attention to different types of relationships. Pruning studies find many heads redundant: Voita et al. (2019) removed 38 of 48 encoder heads from an English-Russian translation model for a 0.15 BLEU drop, and Michel et al. (2019) reduced some layers to a single head at test time.
d_model=512, 8 heads. What's d_k (dimension per head)? Why multiple heads?
Transformer Architecture
A Transformer layer has two sublayers: multi-head self-attention, which mixes information across positions, and a position-wise feedforward network, two linear layers with a hidden size usually four times , applied to each position separately. Each sublayer is wrapped in a residual connection and layer normalization. The original model had an encoder stack and a decoder stack, with cross-attention from decoder to encoder. Self-attention alone has no notion of order: permuting the inputs permutes the outputs in the same way. So positional encodings, fixed sinusoids or learned vectors, are added to the input embeddings. Removing recurrence lets all positions train in parallel.
A transformer block applies then , where FFN projects up by then back down. The residual connections ensure gradients flow directly through the identity path, and LayerNorm stabilizes training by normalizing across the feature dimension: . Stacking blocks gives total compute — the quadratic attention cost is the bottleneck for long sequences.
Why does Transformer need positional encoding? RNNs don't need it.
Pre-trained Transformers
Transformers are usually pre-trained on huge unlabeled text corpora, then fine-tuned or prompted for specific tasks. Three families exist. Encoder-only models such as BERT use bidirectional self-attention and are pre-trained by masked language modeling, predicting about 15 percent of hidden tokens; they suit classification, tagging, and retrieval. Decoder-only models such as GPT use a causal mask, so each position sees only earlier tokens, and are trained to predict the next token; they suit generation and now handle most tasks through prompting. Encoder-decoder models such as T5 suit sequence-to-sequence tasks like translation and summarization. Vision Transformers apply the same encoder to sequences of image patches.
BERT pre-trains with masked language modeling (MLM): randomly mask 15% of tokens and predict them, learning bidirectional context in per layer. GPT pre-trains with causal language modeling: predict using a causal mask that sets future attention weights to zero. The pre-training objective defines what the model learns: BERT excels at understanding tasks (classification, NER) because it sees full context; GPT excels at generation because it learns sequential token prediction.
BERT is encoder-only, GPT is decoder-only. Which for text classification? Which for text generation?
Theory Exercise
Problem:
In scaled dot-product attention, the scores are divided by √d_k before the softmax. Explain quantitatively why this scaling matters and what goes wrong if it is omitted for large d_k.
Hints:
- Assume the entries of Q and K are independent with mean 0 and variance 1
- What is the variance of a single dot product q·k of length d_k?
- How does softmax behave when its inputs have very large magnitude?
Coding Exercise
Problem:
Implement scaled dot-product self-attention from scratch in PyTorch (no nn.MultiheadAttention). Given Q, K, V for a short sequence, compute the attention weights and the output, and verify each row of the attention-weight matrix sums to 1.
Hints:
- scores = Q @ K.transpose(-2, -1) / sqrt(d_k)
- weights = softmax(scores, dim=-1); output = weights @ V
- weights.sum(dim=-1) should be all ones (each query distributes over all keys)