How Backpropagation Is Implemented From Scratch in the AI-Engineering-From-Scratch Curriculum
The AI-Engineering-From-Scratch curriculum implements backpropagation through a minimal autograd engine that wraps scalars in Value objects, builds a computational graph via operator overloading, and executes reverse-mode automatic differentiation using topological sorting to compute gradients for every parameter in a single backward pass.
The backpropagation from scratch lesson (Phase 03, Lesson 03) in the rohitg00/ai-engineering-from-scratch repository provides a complete pure-Python implementation of reverse-mode automatic differentiation. Located in phases/03-deep-learning-core/03-backpropagation/code/main.py, this educational module reconstructs the core mechanics behind modern deep learning frameworks like PyTorch, demonstrating exactly how gradients flow from a loss value back to every weight in a neural network without external dependencies.
The Value Node: Foundation of the Autograd Engine
Storing Data, Gradients, and Graph Edges
At the heart of the implementation is the Value class defined in phases/03-deep-learning-core/03-backpropagation/code/main.py (lines 5-15). Every scalar quantity in the network—whether an input, weight, bias, or intermediate activation—is encapsulated in a Value object that tracks four critical pieces of state:
data: The raw scalar value (e.g., the result of a multiplication).grad: The accumulated gradient, initialized to 0.0, representing the partial derivative of the final loss with respect to this node._prev: A set ofValueobjects that are the parents (children in the computational graph) of this operation._op: A string label indicating which operation produced this node (e.g.,'+','*', or'sigmoid').
This structure allows the engine to treat every arithmetic operation as a node in a directed acyclic graph (DAG), where edges represent data dependencies required for gradient computation.
Building the Computational Graph
Operator Overloading and Local Gradient Rules
To construct the graph dynamically, the curriculum overloads Python's arithmetic operators. In phases/03-deep-learning-core/03-backpropagation/code/main.py (lines 16-38), the __add__ and __mul__ methods do not merely return numbers; they create new Value nodes and attach a custom _backward closure to each.
For addition, the _backward closure implements the local gradient rule: the gradient flows equally to both inputs. For multiplication, it implements the product rule, scaling each input's gradient by the other input's data.
Similarly, the sigmoid activation (lines 50-59) creates a new Value and defines its _backward to propagate gradients using the derivative formula sigmoid(x) * (1 - sigmoid(x)). These closures are stored but not executed during the forward pass; they act as deferred instructions for the reverse pass.
Executing Reverse-Mode Differentiation
Topological Sort and the Backward Pass
The actual backpropagation from scratch algorithm is implemented in the backward() method (lines 61-75 of main.py). Because gradients must flow from outputs to inputs, the engine must process nodes in reverse topological order—ensuring that a node's gradient is fully accumulated before it propagates gradients to its parents.
The method implements this via a recursive depth-first search:
- Build a topologically sorted list (
topo) starting from the loss node. - Reverse this list so the loss comes first.
- Iterate through the reversed list, calling each node's stored
_backward()closure.
This single pass efficiently computes dL/dw for every parameter w in the network, exactly mirroring the reverse-mode autodiff algorithm used in production frameworks.
Neural Network Architecture on Top of Value
Composing Neurons, Layers, and Networks
The curriculum demonstrates how to build higher-level abstractions using the Value primitive. In phases/03-deep-learning-core/03-backpropagation/code/main.py (lines 83-130), three classes provide a familiar API:
Neuron: Holds a list ofValue-wrapped weights and a bias. Its__call__method computes the dot product of inputs and weights, adds the bias, and applies a nonlinearity.Layer: Groups multipleNeuroninstances and processes them in parallel.Network: StacksLayerobjects to form a multi-layer perceptron.
Because every weight is a Value and every operation uses the overloaded operators, calling Network(x) automatically constructs the full computational graph required for backpropagation, with the final output being a single Value object representing the prediction.
Training with Backpropagation: The XOR Example
End-to-End Gradient Computation
The repository includes a complete training demonstration in phases/03-deep-learning-core/03-backpropagation/code/main.py (lines 36-70) that solves the XOR problem. The training loop follows the standard four-step pattern:
- Forward pass: Compute predictions using
net(x). - Loss calculation: Compute mean squared error (MSE) using
mse_loss(pred, target), which returns aValueobject representing the loss. - Backward pass: Call
loss.backward()to trigger the topological sort and propagate gradients back through the entire graph. - Parameter update: Perform a simple SGD step:
p.data -= lr * p.gradfor every parameterp, followed byzero_grad()to reset gradients for the next iteration.
# Example: Training loop structure from the curriculum
net = Network([2, 4, 1]) # 2 inputs, 4 hidden, 1 output
# Single training step
x = [Value(0.0), Value(1.0)] # XOR input (0, 1)
pred = net(x)
loss = mse_loss(pred, 1.0) # Target is 1
loss.backward() # Compute all gradients
# Update weights manually
for p in net.parameters():
p.data -= 0.1 * p.grad
This implementation proves that the from-scratch autograd engine can successfully train a network to learn non-linear decision boundaries, including the circular classification dataset also provided in the lesson.
Summary
- The
Valueclass inphases/03-deep-learning-core/03-backpropagation/code/main.py(lines 5-15) encapsulates scalars, gradients, and parent pointers to form the nodes of a computational graph. - Operator overloading on
__add__,__mul__, andsigmoid(lines 16-59) attaches_backwardclosures that encode local gradient rules without executing them immediately. - Topological sorting in the
backward()method (lines 61-75) ensures gradients propagate correctly from the loss node to every input via reverse-mode automatic differentiation. - The
Neuron,Layer, andNetworkclasses (lines 83-130) demonstrate how to compose the autograd primitives into a trainable neural network architecture. - The XOR training loop (lines 36-70) validates the implementation by successfully optimizing parameters using the computed gradients.
Frequently Asked Questions
What file contains the backpropagation implementation?
The core implementation resides in phases/03-deep-learning-core/03-backpropagation/code/main.py within the repository. This file contains the Value class, operator definitions, topological backward pass, and neural network classes.
How does the engine handle the chain rule?
The engine implements the chain rule through the combination of _backward closures and topological ordering. Each operation stores a closure that computes local gradients (the derivative of the operation's output with respect to its inputs). During the backward pass, these closures multiply the incoming gradient (from upstream) by the local gradient, effectively applying the chain rule as gradients flow backward through the graph.
Where is the mathematical theory documented?
The conceptual explanation of the chain rule, computational graphs, and vanishing gradients is documented in phases/03-deep-learning-core/03-backpropagation/docs/en.md. This markdown file accompanies the code and provides the mathematical derivation for a two-layer network.
Does this implementation support vectorized operations?
No, this educational implementation focuses on scalar Value objects for clarity. Each neuron computes weighted sums and activations using individual scalar operations rather than matrix multiplications. While less efficient than vectorized frameworks like NumPy or PyTorch, this scalar approach makes the gradient flow explicit and easier to debug for learning purposes.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →