Hello! In our last lesson, we built a crucial debugging tool: a gradient checking function. By comparing your backpropagation results with a numerical approximation, you can now have high confidence that your gradient calculations—the very engine of learning—are correct.
With all the individual components built and verified (initialization, forward pass, backpropagation), it's time to assemble the complete machine. Today, you will learn how to integrate the forward pass, backpropagation, and parameter updates into a complete training loop. This is the master process that orchestrates all the pieces, allowing your neural network to iteratively learn from data.
1. The Training Cycle
At its heart, training a neural network is a cyclical process. We make a guess, measure how wrong it is, and then use that error to make a slightly better guess. This cycle is repeated thousands of times.
This iterative process is often called the training loop. It has four main steps:

This entire process is an application of the gradient descent algorithm you've seen before. The "forward pass" and "loss calculation" define the value of our complex cost function , and the "backward pass" calculates the gradient . Finally, the "weight update" step takes a step down the gradient.
2. The Anatomy of the Training Loop
Let's translate that diagram into a concrete algorithm that you can implement. The main function, which we can call train_model or fit, will orchestrate these steps.
Here's the sequence of operations:
- Initialization: Before the loop begins, initialize the network's parameters (weights
Wand biasesb) for all layers. This happens only once. - Iteration: Start a loop that runs for a predefined number of
iterations(orepochs). - Inside the Loop: For each iteration:
a. Forward Propagation: Take the entire training datasetXand pass it through the network. This calculates the activations for each layer and produces the final predictions,Y_hat. You'll also need to store the intermediate values (ZandAfrom each layer) in acache, as they are required for the backward pass.
b. Cost Calculation: Compare the predictionsY_hatwith the true labelsYusing your cost function (e.g., cross-entropy) to get a single scalar value representing the total loss.
c. Backward Propagation: Use thecachefrom the forward pass and the labelsYto compute the gradients of the cost with respect to every parameter (dW1,db1,dW2,db2, etc.).
d. Parameter Update: Use the computed gradients and the learning rate (alpha) to update each parameter in the network. This is the classic gradient descent step:W = W - alpha * dW. - Completion: After the loop finishes, the
parametersdictionary contains the learned weights and biases.
Let's watch a video that shows a developer implementing this exact logic in Python.
Deep Neural Network Python from scratch | L layer Model | No Tensorflow
The video 'Deep Neural Network Python from scratch' by Learn With Jay provides a clear implementation of a main model function that orchestrates this entire training process for an L-layer network.
Watch the section from 34:22 to 39:00. This part focuses on building the main model function. Observe how it: Initializes the parameters once before the loop. Iterates for a specified number of times (num_iterations). Calls forward_propagation, compute_cost, backward_propagation, and update_parameters in sequence within each iteration. Includes a simple mechanism to print the cost periodically to monitor training.
3. Implementing the Training Loop from Scratch
Now, let's formalize this into a code structure that you can build. You already have most of the necessary functions (initialize_parameters, forward_propagation, compute_cost, back_propagation). We just need to add the update_parameters function and the main train_model function to tie them all together.
The Parameter Update Function
Your back_propagation function returns a dictionary of gradients. The update function will take these gradients and the current parameters and apply the update rule.
Deep Neural Network Python from scratch | L layer Model | No Tensorflow
The backpropagation function computes the gradients, but it doesn't apply them. Let's watch how to implement the simple but crucial update_parameters function.
Watch from 32:58 to 34:22. The video shows the update_parameters function, which iterates through the layers and applies the gradient descent update rule: W = W - learning_rate * dW.
Based on the video, your update_parameters function should look like this:
def update_parameters(parameters, gradients, learning_rate):
"""
Updates parameters using the gradient descent update rule.
Args:
parameters (dict): Contains current parameters (W1, b1, ...).
gradients (dict): Contains gradients from backprop (dW1, db1, ...).
learning_rate (float): The learning rate alpha.
Returns:
dict: The updated parameters.
"""
num_layers = len(parameters) // 2 # Number of layers in the network
for l in range(1, num_layers + 1):
parameters[f"W{l}"] = parameters[f"W{l}"] - learning_rate * gradients[f"dW{l}"]
parameters[f"b{l}"] = parameters[f"b{l}"] - learning_rate * gradients[f"db{l}"]
return parameters
The Main train_model Function
Now you can assemble everything into the main training function. This function will serve as the entry point for training your MLP.
def train_model(X, Y, layer_dims, num_iterations, learning_rate, print_cost=False):
"""
Implements a L-layer neural network: [LINEAR->RELU]*(L-1)->LINEAR->SIGMOID/SOFTMAX.
Args:
X (np.array): Input data, of shape (input size, number of examples).
Y (np.array): True "label" vector.
layer_dims (list): List containing the input size and each layer size.
num_iterations (int): Number of iterations of the optimization loop.
learning_rate (float): Learning rate of the gradient descent update rule.
print_cost (bool): If True, it prints the cost every 100 iterations.
Returns:
parameters (dict): Parameters learned by the model. They can then be used to predict.
costs (list): A list of costs recorded during training.
"""
costs = []
# 1. Initialize parameters
parameters = initialize_parameters(layer_dims)
# 2. Start the training loop
for i in range(num_iterations):
# 3a. Forward propagation
# For a classification network, AL is the final activation (sigmoid/softmax output)
AL, cache = forward_propagation(X, parameters)
# 3b. Compute cost
cost = compute_cost(AL, Y)
# 3c. Backward propagation
gradients = back_propagation(AL, Y, cache)
# 3d. Update parameters
parameters = update_parameters(parameters, gradients, learning_rate)
# Record and print the cost
if print_cost and i % 100 == 0:
print(f"Cost after iteration {i}: {cost}")
costs.append(cost)
return parameters, costs
Test your understanding!
In the train_model function above, why are the parameters initialized outside the for loop, while forward_propagation, back_propagation, and update_parameters are called inside it?
Show answer
The goal of training is to iteratively improve a single set of parameters. We initialize them once to get a random starting point. Then, inside the loop, each iteration refines those same parameters based on the calculated gradients. If we were to re-initialize the parameters inside the loop, the network would "forget" everything it learned in the previous iteration and start over from a random state. The learning would never accumulate.
4. Watching the Network Learn
You've built the engine. Now, how do you know if the car is moving? The most fundamental indicator of learning is a decreasing loss value.
The costs list that our train_model function returns is not just for printing; it's a vital diagnostic tool. By plotting the cost against the number of iterations, you can generate a loss curve. A healthy loss curve slopes downward, indicating that the model's predictions are getting closer to the true labels with each iteration.
Let's see a concrete example of this in action.
Backpropagation with Python Example #1: Bitwise XOR
The article 'Backpropagation from scratch with Python' from PyImageSearch provides a clear example of training a network on the classic XOR dataset. This demonstrates how the loss decreases over epochs, proving that the network is learning.
Read the section titled Backpropagation with Python Example #1: Bitwise XOR. Pay attention to how the NeuralNetwork class is instantiated and how the .fit() method (their training loop) is called. Most importantly, look at the console output showing the loss value decreasing from 0.509 down to 0.0002452 over 20,000 epochs. This is exactly the behavior you want to see from your own implementation.
If you train your model and the loss stays flat, increases, or fluctuates wildly, it's a sign that something is wrong. Common culprits include:
- A bug in your backpropagation code (which you can verify with your gradient checker!).
- A learning rate that is too high (causing overshooting) or too low (causing negligible progress).
- Improper data normalization.
Conclusion
Congratulations! By assembling the training loop, you have created a fully functional, learning neural network from scratch. You now understand the complete end-to-end process, from feeding data forward to updating weights based on the calculated error.
Key Takeaways:
- The training loop is the central process that orchestrates learning in a neural network.
- It consists of four steps repeated iteratively: forward propagation, loss calculation, backward propagation, and parameter update.
- Parameters are initialized once before the loop begins, and are continuously refined within the loop.
- Monitoring the loss curve (cost vs. iterations) is the primary method for verifying that your model is learning correctly.
Preview of the Next Lesson:
Our current training loop uses the simplest form of gradient descent. While effective, it can sometimes be slow to converge. In the next lesson, you will implement the Momentum update rule, a popular technique to accelerate gradient descent and help it navigate complex loss landscapes more efficiently.