Method to train neural nets by adjusting its weights. Backpropagation is reverse-mode automatic differentiation applied to the computation graph of a network: it computes the gradient of the loss with respect to every parameter in a single backward sweep.
Steps
- Forward Pass: Compute activations for all layers
- Loss Calculation: Compute the Loss
- Backward Pass: Use the Chain rule to compute , the error term for each layer starting from the output and propagating backwards
- Update weights and biases using the computed gradients by Gradient Descent.
Reverse-Mode Autodiff
A network is a directed acyclic graph of primitive operations. Forward mode propagates derivatives input to output; reverse mode propagates them output to input. Because a loss has one scalar output and many parameters, reverse mode computes all parameter gradients at the cost of roughly one extra forward pass, which is why it is the method of choice. This is exactly what Autograd engines automate.
Chain Rule on the Graph
Define the error at layer as . The recurrence that flows backward is:
From the error terms the parameter gradients are:
Worked 2-Layer MLP
For , , , , with cross-entropy loss:
From-Scratch numpy (vectorized, batch N)
import numpy as np
def forward(X, W1, b1, W2, b2):
Z1 = X @ W1 + b1
A1 = 1 / (1 + np.exp(-Z1)) # sigmoid
Z2 = A1 @ W2 + b2
A2 = np.exp(Z2 - Z2.max(1, keepdims=True))
A2 /= A2.sum(1, keepdims=True) # softmax
return Z1, A1, Z2, A2
def backward(X, Y, W2, Z1, A1, A2):
N = X.shape[0]
dZ2 = (A2 - Y) / N # softmax + cross-entropy
dW2 = A1.T @ dZ2
db2 = dZ2.sum(0)
dA1 = dZ2 @ W2.T
dZ1 = dA1 * A1 * (1 - A1) # sigmoid'
dW1 = X.T @ dZ1
db1 = dZ1.sum(0)
return dW1, db1, dW2, db2Torch Autograd Check
import torch
X = torch.randn(8, 4)
Y = torch.eye(3)[torch.randint(0, 3, (8,))]
W1 = torch.randn(4, 5, requires_grad=True)
W2 = torch.randn(5, 3, requires_grad=True)
A1 = torch.sigmoid(torch.einsum('nd,dh->nh', X, W1))
logits = torch.einsum('nh,hc->nc', A1, W2)
loss = -(Y * logits.log_softmax(1)).sum(1).mean()
loss.backward() # dW1, dW2 match the manual grads
print(W1.grad.shape, W2.grad.shape)Pitfalls
- Vanishing/exploding gradients: repeated multiplication by and weights can shrink or blow up in deep nets; use ReLU, residual connections, or normalization.
- Cache the forward activations; recomputing them in the backward pass wastes work (or use checkpointing to trade compute for memory).
- A subtle sign or transpose error is best caught with numerical gradient checking or a torch autograd comparison as above.
Related
- Autograd generalizes this to arbitrary graphs.
- Gradient Descent consumes the gradients to update parameters.