The mechanics of a neural network — neurons, gradients, backprop, and the loss table you'll use for the rest of the certificate
10 hours · 5 modules · Keras (TensorFlow backend) Final project: Classification and Captioning.
Shortest course in the certificate, and the most conceptually important. This is where the mechanics of a neural network get explained. Everything after this assumes you have it.
Why deep learning happened when it did — the algorithms are old (backprop is 1986). Three things changed: data volume, GPU compute, and better initialisation/activation tricks that made deep networks actually trainable.
Biological neuron → artificial neuron. The videos draw the analogy: dendrites (inputs), soma (summation), axon (output), synapse strength (weights). Useful as a mnemonic; don't take it literally — an artificial neuron is a weighted sum plus a non-linearity, nothing more.
The artificial neuron:
z = Σ (wᵢ · xᵢ) + b ← linear part: weighted sum plus bias
a = f(z) ← non-linear activation
The bias b shifts the activation threshold — without it, every neuron's decision boundary would be forced through the origin.
Network anatomy:
Why non-linearity is non-negotiable: stack two linear layers and you get W₂(W₁x + b₁) + b₂ = (W₂W₁)x + (W₂b₁ + b₂) — which is just another linear layer. Without an activation function, a 100-layer network has exactly the expressive power of a 1-layer one. This is a favourite exam question.
Data flows input → output, one layer at a time. For layer l:
z⁽ˡ⁾ = W⁽ˡ⁾ · a⁽ˡ⁻¹⁾ + b⁽ˡ⁾
a⁽ˡ⁾ = f(z⁽ˡ⁾)
That's it — a matrix multiply, an add, an element-wise function, repeated per layer. The Module 1 lab has you code this by hand in NumPy. Do it properly; understanding shapes here saves you hours of debugging later.
Shape discipline. If x is (features, 1) and layer 1 has 5 nodes, W⁽¹⁾ is (5, features) and b⁽¹⁾ is (5, 1). Most beginner errors in the lab are transposition errors.
The learning algorithm. You have a loss J(w); you want the w that minimises it. The gradient ∂J/∂w points uphill, so step downhill:
w := w − α · ∂J/∂w
α (learning rate) is the step size, and it's the hyperparameter that matters most:
Gradient descent finds a local minimum. In high-dimensional networks this turns out to be fine in practice — most local minima are about as good as each other, and the real obstacles are saddle points and plateaus, not bad minima.
How you get ∂J/∂w for every weight in the network. It's the chain rule, applied backwards through the layers so intermediate results get reused instead of recomputed.
The four-step training loop, which is the whole of deep learning:
Repeat for every batch, for every epoch.
Chain rule, spelled out for one weight:
∂J/∂w⁽ˡ⁾ = (∂J/∂a⁽ˡ⁾) · (∂a⁽ˡ⁾/∂z⁽ˡ⁾) · (∂z⁽ˡ⁾/∂w⁽ˡ⁾)
↑ from the ↑ derivative ↑ = a⁽ˡ⁻¹⁾
layer above of activation
Note the middle term: the derivative of the activation function multiplies into every gradient. That single fact explains the vanishing gradient problem.
Sigmoid squashes everything into (0, 1), and its derivative peaks at 0.25.
Multiply 0.25 by itself through many layers: 0.25⁵ ≈ 0.001, 0.25¹⁰ ≈ 0.0000009. The gradient reaching the early layers is effectively zero, so those layers stop learning. The network trains its last few layers and leaves the rest at their random initialisation.
tanh is a bit better (derivative peaks at 1.0) but still saturates at the tails.
The fix: ReLU. Its derivative is exactly 1 for all positive inputs — no shrinkage, no matter how deep. This is the single change that made deep networks trainable, and it's the answer to "why did deep learning suddenly work".
| Function | Formula | Range | Derivative | Use |
|---|---|---|---|---|
| Sigmoid | 1/(1+e⁻ᶻ) | (0,1) | max 0.25 | binary output layer only |
| Tanh | (eᶻ−e⁻ᶻ)/(eᶻ+e⁻ᶻ) | (−1,1) | max 1.0 | zero-centred; RNNs |
| ReLU | max(0, z) | [0,∞) | 0 or 1 | default for hidden layers |
| Leaky ReLU | max(0.01z, z) | (−∞,∞) | 0.01 or 1 | fixes dying ReLU |
| Softmax | eᶻⁱ/Σeᶻʲ | (0,1), sums to 1 | — | multi-class output layer |
Dying ReLU: a neuron whose weights push it permanently negative outputs 0 forever, gets gradient 0 forever, and is dead. Leaky ReLU keeps a small negative slope so there's always some gradient.
Softmax vs sigmoid at the output — the distinction they quiz:
can be both "beach" and "sunset").
Multi-class (a digit is exactly one of 0–9).
tf.keras. Fast to write.The certificate teaches both Keras and PyTorch on purpose: Keras for speed of expression, PyTorch because that's what the GenAI ecosystem (Hugging Face) is built on.
import keras
from keras.models import Sequential
from keras.layers import Dense
model = Sequential()
model.add(Dense(50, activation='relu', input_shape=(n_cols,)))
model.add(Dense(50, activation='relu'))
model.add(Dense(1)) # no activation → raw number
model.compile(optimizer='adam', loss='mean_squared_error')
model.fit(X_train, y_train, validation_split=0.2, epochs=100, verbose=2)
preds = model.predict(X_test)
Three things define the model shape:
from keras.utils import to_categorical
y_train = to_categorical(y_train) # one-hot encode labels
model = Sequential()
model.add(Dense(50, activation='relu', input_shape=(n_cols,)))
model.add(Dense(50, activation='relu'))
model.add(Dense(num_classes, activation='softmax'))
model.compile(optimizer='adam',
loss='categorical_crossentropy',
metrics=['accuracy'])
model.fit(X_train, y_train, epochs=10, validation_split=0.2)
Learn this. It answers a large fraction of every Keras quiz in the certificate:
| Task | Output nodes | Output activation | Loss |
|---|---|---|---|
| Regression | 1 | none (linear) | mean_squared_error |
| Binary classification | 1 | sigmoid | binary_crossentropy |
| Multi-class, one-hot labels | n_classes | softmax | categorical_crossentropy |
| Multi-class, integer labels | n_classes | softmax | sparse_categorical_crossentropy |
| Multi-label | n_labels | sigmoid | binary_crossentropy |
categorical_ vs sparse_categorical_ is purely about label format: one-hot [0,0,1] vs integer 2. Mixing them up is the single most common Keras error in these labs.
oscillation
fit argumentsmodel.fit(X, y,
epochs=50, # full passes over the data
batch_size=32, # samples per gradient update
validation_split=0.2, # hold out 20% for validation
verbose=2,
callbacks=[keras.callbacks.EarlyStopping(patience=5,
restore_best_weights=True)])
Epoch vs batch vs iteration — quizzed:
EarlyStopping watches validation loss and stops when it stops improving. Cheapest possible defence against overfitting; restore_best_weights=True gives you the best epoch, not the last.
input_shape is per-sample and excludes the batch dimension. 10 features → (10,)..values if it complains.conditioned and training will be slow or unstable.
model.summary() prints layer shapes and parameter counts. Use it to sanity-checkarchitecture before wasting an hour training the wrong thing.
Survey module. Each architecture gets a few minutes; you get depth later in courses 3 and 5. What matters here is knowing which architecture for which data shape.
Shallow = one hidden layer. Can theoretically approximate any function (universal approximation theorem) but may need an impractically wide layer. Deep = many hidden layers, and each layer builds features on the previous layer's features. This hierarchy is the point — edges → shapes → parts → objects.
For grid-like data: images, video, spectrograms.
feature map. The same weights are used at every position (parameter sharing), which is why a CNN needs vastly fewer parameters than a dense layer on the same image.
cutting compute.
Two properties that make CNNs work on images: local receptive fields (nearby pixels are related) and translation invariance (a cat is a cat wherever it is in the frame).
Output size of a conv layer:
out = floor((in − kernel + 2·padding) / stride) + 1
from keras.layers import Conv2D, MaxPooling2D, Flatten
model.add(Conv2D(16, (5,5), activation='relu', input_shape=(28,28,1)))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Conv2D(32, (3,3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Flatten())
model.add(Dense(100, activation='relu'))
model.add(Dense(10, activation='softmax'))
Keras image input shape is (height, width, channels) — channels last. PyTorch puts channels first. This bites everyone who switches between courses 3 and 5.
For sequential data where order matters: text, time series, audio.
A hidden state is carried from timestep to timestep — the network has a memory. But long chains of multiplication cause vanishing gradients, so plain RNNs can't learn long-range dependencies.
LSTM (Long Short-Term Memory) fixes this with a cell state and three gates — forget, input, output — that control what's kept, added and emitted. GRU is a lighter variant with two gates.
Process the whole sequence in parallel rather than step by step, using self-attention so every token can look directly at every other token. No recurrence, no vanishing gradient over sequence length, and it parallelises on GPUs — which is why transformers displaced RNNs and made today's LLMs possible.
Courses 9–11 cover this properly. Here just fix the comparison:
| RNN/LSTM | Transformer | |
|---|---|---|
| Processing | sequential | parallel |
| Long-range dependencies | weak | strong |
| Training speed | slow | fast (GPU-friendly) |
| Cost | linear in sequence length | quadratic in sequence length |
Unsupervised. Encoder compresses input to a low-dimensional bottleneck (latent representation); decoder reconstructs the input from it. Trained to make output ≈ input, so the labels are the inputs themselves.
Uses: dimensionality reduction (non-linear PCA), denoising, anomaly detection (high reconstruction error = anomaly).
Restricted Boltzmann Machines get a mention — historical, largely superseded.
The idea the entire second half of the certificate rests on: someone else spent millions of GPU-hours learning general features; you reuse them.
from keras.applications import VGG16
base = VGG16(weights='imagenet', include_top=False, input_shape=(224,224,3))
base.trainable = False # freeze the learned features
# add your own small head on top and train only that
Why it works: early layers learn generic features (edges, textures) that transfer across domains. Only the last layers are task-specific.
document translation / credit-card anomaly detection?
Final Project: Classification and Captioning — combines a CNN image classifier with a caption-generation component. Peer/AI graded.
What the rubric wants:
compile with the loss appropriate to the taskStandard training-history plot the graders expect:
history = model.fit(X_train, y_train, validation_split=0.2, epochs=20)
import matplotlib.pyplot as plt
plt.plot(history.history['loss'], label='train')
plt.plot(history.history['val_loss'], label='validation')
plt.xlabel('epoch'); plt.ylabel('loss'); plt.legend()
Reading that plot is a skill they assess: