Learning by Example: The Manual Training Loop
Introduction
Welcome back to the final lesson of JAX in Action: Neural Networks from Scratch! What an incredible journey we've taken together through the fundamentals of building neural networks from the ground up. We started by tackling the classic XOR problem and establishing our MLP architecture with proper parameter initialization. We then implemented forward propagation to see our network generate predictions, and in our previous lesson, we developed the crucial ability to measure prediction quality using Binary Cross Entropy loss and compute gradients with JAX's automatic differentiation.
Today marks the culmination of our neural network construction project. We'll implement a complete training loop that iteratively improves our MLP's performance using gradient descent. You'll learn to orchestrate the training process by repeatedly computing predictions, calculating loss and gradients, and updating parameters to minimize error. By the end of this lesson, you'll witness your network actually learning to solve the XOR problem through experience — a truly magical moment in machine learning!
The Core of Machine Learning: Iterative Improvement
At its heart, machine learning is about iterative improvement. Think of learning to play a musical instrument: you don't master it overnight, but through countless repetitions, gradually adjusting your technique based on feedback. Neural network training follows this same principle.
Our training process operates in cycles called epochs. During each epoch, we present our network with the training data and perform these key steps: compute predictions using forward propagation, calculate how wrong these predictions are using our loss function, determine the direction to adjust parameters using gradients, and finally update parameters to (hopefully) improve performance. This cycle repeats hundreds or thousands of times until our network performs well.
The beauty of this approach lies in its simplicity — we don't need to manually figure out what each parameter should be. Instead, we let the mathematical framework of gradient descent guide us toward better solutions automatically. Each iteration brings us closer to a network that can accurately solve our XOR problem.
Gradient Descent: The Update Rule
The mathematical foundation of our training process is gradient descent, an optimization algorithm that systematically adjusts parameters to minimize our loss function. The core update rule is elegantly simple:
Where and are the old and new parameter values respectively, is the learning rate, and is the gradient of the loss with respect to . The learning rate is a crucial hyperparameter that controls how big of a step we take during optimization: if it's too small, training progresses slowly, but if it's too large, we might overshoot the optimal solution and never converge. The gradient tells us the direction of steepest increase in loss, so by subtracting it (multiplied by the learning rate), we move in the direction that should decrease the loss.
This update rule applies to every single parameter in our network — every weight and bias gets adjusted according to its corresponding gradient. Since our parameters are organized in a PyTree structure, we need a way to apply this rule efficiently to the entire parameter tree, which is where JAX's tree utilities come in handy.
