Before Transformers dominated, CNNs conquered vision and RNNs ruled sequences. Understanding
these architectures reveals why structure-aware design matters—and when to use each approach today.
Learning Objectives
Understand Convolutional Neural Networks (CNNs) and why they excel at image tasks
Explain Recurrent Neural Networks (RNNs) and LSTMs for sequential data
Compare inductive biases: locality (CNNs) vs. sequence (RNNs) vs. learned (Transformers)
Apply unsupervised learning techniques: clustering and dimensionality reduction
Know when to use each architecture in modern practice
Why This Module Matters
🌉
The Bridge to Modern Architectures
You've learned that MLPs are universal function approximators (Module 2.2). But MLPs
treat every input dimension independently—they don't "know" that pixels next to each
other are related, or that words form sequences.
CNNs and RNNs solved this by building structure into the architecture.
Understanding their design choices prepares you for Transformers, which take a different
approach: learning structure from data rather than hard-coding it.
Inductive Bias
The inductive bias of an architecture is the set of assumptions it makes
about data structure. These assumptions guide learning, reducing the amount of data needed—but
can hurt performance when assumptions don't match reality.
Part 1: Convolutional Neural Networks (CNNs)
CNN
A Convolutional Neural Network is a neural network that uses convolution
operations to exploit spatial structure in data—particularly images. It assumes that
patterns are local (nearby pixels are related) and translation-invariant (a cat is a
cat regardless of where it appears).
Intuition
Imagine looking at a photo through a small sliding window. As you move the window
across the image, you look for specific patterns: edges, textures, shapes. You don't
need to see the whole image at once—local patterns combine to form global understanding.
This is exactly what CNNs do. Small "filters" slide across the image, detecting
local patterns. Early layers find edges; deeper layers combine edges into shapes;
final layers recognize objects.
Technical: The Convolution Operation
A convolution applies a small filter (kernel) to every location in the input:
Output[i,j] = Σₘ Σₙ Input[i+m, j+n] × Filter[m,n]
The same filter is applied everywhere (weight sharing), meaning:
Far fewer parameters than a fully-connected layer
Translation invariance: pattern detection is position-independent
Local connectivity: each output depends only on a small input region
In Practice
A typical CNN stacks: Conv → ReLU → Pool → Conv → ReLU → Pool → ... → Flatten → Dense
Pooling (max or average) reduces spatial dimensions, creating a
hierarchical representation: pixel → edge → texture → part → object.
A Recurrent Neural Network processes sequences by maintaining a hidden
state that accumulates information from previous time steps. It assumes that data has
sequential structure where earlier elements influence later ones.
Intuition
Reading a sentence word-by-word, you build up context. When you see "The dog
chased the ___", your understanding of the sentence so far influences your
expectation for the next word.
RNNs formalize this: a hidden state vector is updated at each time step, carrying
forward information from the past. The same weights process each step—the network
learns a reusable "update rule" for any sequence length.
Technical: The Recurrence Equation
At each time step t, the RNN updates its hidden state:
h_t = tanh(W_h · h_{t-1} + W_x · x_t + b)
The same weights (W_h, W_x, b) are used at every step—this is weight sharing
across time, analogous to CNN weight sharing across space.
The Vanishing Gradient Problem
Problem: During backpropagation through many time steps, gradients
shrink exponentially (vanishing) or explode. Long-range dependencies are hard to learn.
Solution: LSTM (Long Short-Term Memory) and GRU (Gated Recurrent Unit)
add gates that control information flow, allowing gradients to flow unchanged over
many steps.
RNN Unrolled Through Time
x₀
RNN Cellh₀ = f(x₀, h₋₁)
h₀
→
x₁
RNN Cellh₁ = f(x₁, h₀)
h₁
→
x₂
RNN Cellh₂ = f(x₂, h₁)
h₂
→
x_T
RNN Cellh_T = f(x_T, h_{T-1})
h_T
→ Output
Same weights at every step. Hidden state h carries information forward.
LSTM: Solving the Memory Problem
LSTMs add a separate "cell state" C that runs through time like a conveyor belt.
Three gates control what to forget, what to add, and what to output:
Forget Gate
f_t = σ(W_f · [h_{t-1}, x_t])
What to remove from cell state
Input Gate
i_t = σ(W_i · [h_{t-1}, x_t])
What new info to store
Output Gate
o_t = σ(W_o · [h_{t-1}, x_t])
What to output from cell state
PyTorch LSTM Implementation
import torch.nn as nn
class LSTMClassifier(nn.Module):
def __init__(self, vocab_size, embed_dim, hidden_dim, num_classes):
super().__init__()
self.embedding = nn.Embedding(vocab_size, embed_dim)
self.lstm = nn.LSTM(
input_size=embed_dim,
hidden_size=hidden_dim,
num_layers=2,
batch_first=True,
dropout=0.3,
bidirectional=True # Process both directions
)
# Bidirectional doubles the output dimension
self.classifier = nn.Linear(hidden_dim * 2, num_classes)
def forward(self, x):
# x: [batch, seq_len] token indices
embedded = self.embedding(x) # [batch, seq_len, embed_dim]
# LSTM returns: output (all hidden states), (h_n, c_n)
lstm_out, (h_n, c_n) = self.lstm(embedded)
# Use final hidden state for classification
# h_n: [num_layers*2, batch, hidden_dim] for bidirectional
# Concatenate forward and backward final states
final_hidden = torch.cat([h_n[-2], h_n[-1]], dim=1)
return self.classifier(final_hidden)
# Key limitation: Sequential processing = no parallelization
# This is why Transformers replaced RNNs for most NLP tasks
Part 3: Unsupervised Learning
Unsupervised Learning
Unsupervised learning finds structure in data without explicit labels.
The algorithm discovers patterns, groupings, or representations that weren't provided
by humans—essential for understanding data before building models.
Why It Matters
Most real-world data is unlabeled—labels are expensive. Unsupervised learning lets you:
Explore: Discover natural groupings in customer data
Compress: Reduce dimensionality while preserving structure
Pre-train: Learn representations that transfer to supervised tasks
Detect anomalies: Find points that don't fit any cluster
Clustering: Finding Natural Groups
K-Means Clustering
Partitions data into K clusters by minimizing within-cluster variance.
Each point belongs to the cluster with the nearest centroid.
1Initialize K centroids randomly
2Assign each point to nearest centroid
3Recompute centroids as cluster means
4Repeat until convergence
from sklearn.cluster import KMeans
import numpy as np
# Cluster customer data
X = load_customer_features() # [n_customers, n_features]
kmeans = KMeans(n_clusters=5, random_state=42)
cluster_labels = kmeans.fit_predict(X)
# Find cluster centers
centers = kmeans.cluster_centers_
# Evaluate clustering quality
from sklearn.metrics import silhouette_score
score = silhouette_score(X, cluster_labels)
print(f"Silhouette Score: {score:.3f}") # Higher is better
Other Clustering Methods
Hierarchical
Builds tree of clusters. No need to specify K upfront.
DBSCAN
Density-based. Finds clusters of arbitrary shape. Handles outliers.
Gaussian Mixture
Soft clustering. Points have probabilities of belonging to each cluster.
Dimensionality Reduction: Compressing Information
Principal Component Analysis (PCA)
Finds directions of maximum variance in data and projects onto them.
The first few principal components often capture most of the information.
Key Insight: If your 100-dimensional data mostly lies on a
3-dimensional surface (manifold), PCA finds that surface.
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
# Reduce dimensions for visualization
pca = PCA(n_components=2)
X_2d = pca.fit_transform(X)
# How much variance is explained?
print(f"Explained variance: {pca.explained_variance_ratio_.sum():.1%}")
# Visualize
plt.scatter(X_2d[:, 0], X_2d[:, 1], c=labels, cmap='viridis')
plt.xlabel('PC1')
plt.ylabel('PC2')
plt.title('Data in Principal Component Space')
PCA vs. t-SNE vs. UMAP
Method
Type
Preserves
Best For
PCA
Linear
Global structure, variance
Fast exploration, preprocessing
t-SNE
Non-linear
Local neighborhoods
Visualization, finding clusters
UMAP
Non-linear
Local + some global
Visualization + downstream ML
Autoencoders: Neural Network Compression
Learn to Compress and Reconstruct
An autoencoder learns to compress input into a small latent code, then
reconstruct the original. The bottleneck forces it to learn the most
important features.