Hello! Welcome to the third lesson in our module on Neural Network Foundations.
In the last lesson, you did something incredible: you built and trained a neural network from scratch using Python and NumPy. In that process, we made a couple of design choices without much justification: we used the ReLU activation function in our hidden layer and initialized our weights with small random numbers.
Today, we're going to dive deep into the "why" behind those choices. Our goal is to explain the role of various activation functions and weight initialization strategies. These are not minor details; they are critical hyperparameters that can mean the difference between a model that trains efficiently and one that gets stuck, learns nothing, or produces unstable results.
Understanding these components is a core competency for any AI engineer. It's what allows you to diagnose training problems and intelligently design network architectures, moving you closer to your goal of becoming an indispensable expert in the field.
1. The Role of Activation Functions
In the network you built, the activation function was the non-linear step () applied after the linear transformation (). But why is this non-linearity so important?
Without an activation function (or with a "linear" activation function, ), a neural network, no matter how many layers it has, is just a stack of linear operations. A sequence of linear transformations can always be collapsed into a single linear transformation. This means your "deep" network would be no more powerful than a simple linear regression model, incapable of learning the complex, non-linear patterns found in data like images, text, or economic trends.
Activation functions are the key that unlocks the expressive power of neural networks. Let's start with a video that explains this fundamental concept.
Activation Functions In Neural Networks Explained | Deep Learning Tutorial
The following video from AssemblyAI provides an excellent overview of why activation functions are necessary and introduces some of the most common ones.
Watch the segment from 00:28 to 01:54. Focus on the core argument: without non-linearity, a deep network is just a stacked linear regression model.
A Tour of Common Activation Functions
Now that you understand their purpose, let's explore the zoo of popular activation functions. Each has distinct properties that affect how a network learns.

To get a hands-on feel for these functions, their gradients, and their impact on training, we'll use a fantastic interactive notebook from the University of Amsterdam's Deep Learning course.
Tutorial 3: Activation Functions — UvA DL Notebooks v1.2 ...
This resource, 'Tutorial 3: Activation Functions', provides PyTorch implementations and visualizations that will give you a practical understanding of how these functions behave.
First, read the section 'Common activation functions'. You'll see implementations for Sigmoid, Tanh, ReLU, Leaky ReLU, ELU, and Swish. Since you've worked with Python and NumPy, the PyTorch code should be quite readable. Next, study the section 'Visualizing activation functions'. Pay close attention to the plots that show both the function and its gradient. This is the most important part: observe where the gradients are strong, weak (close to zero), or constant.
Let's summarize the key players and their characteristics:
-
Sigmoid ():
- Pros: Squashes numbers to a probability-like range of [0, 1]. Historically popular and useful in the output layer for binary classification.
- Cons: Suffers from the vanishing gradient problem. As you can see from its gradient plot, for very large or very small inputs, the gradient is almost zero. In deep networks, this can cause the gradients flowing back to the early layers to become vanishingly small, effectively halting learning. Also, its output is not zero-centered.
-
Tanh (Hyperbolic Tangent):
- Pros: Squashes numbers to [-1, 1]. Its output is zero-centered, which can help with optimization.
- Cons: Like Sigmoid, it saturates at the extremes and suffers from the vanishing gradient problem.
-
ReLU (Rectified Linear Unit, ):
- Pros: The current default for hidden layers. It's computationally very efficient. It does not saturate for positive inputs, which means it doesn't suffer from vanishing gradients on that side. This property greatly accelerates the convergence of SGD.
- Cons: The "Dying ReLU" problem. If a neuron's input is always negative, it will always output zero. Consequently, the gradient flowing through it will forever be zero. The neuron "dies" and plays no further part in learning.
-
Leaky ReLU, ELU, Swish:
- These are all variants designed to solve the Dying ReLU problem by ensuring the function has a small, non-zero gradient for negative inputs.
- Leaky ReLU: Introduces a small slope for negative values (e.g., ).
- ELU (Exponential Linear Unit): Uses an exponential curve for negative values.
- Swish: A smooth function that is non-monotonic, sometimes performing slightly better than ReLU.
The UvA notebook you just read has a great analysis of how these functions affect gradient flow and the practical issue of dead neurons.
Tutorial 3: Activation Functions — UvA DL Notebooks v1.2 ...
Let's continue with the UvA notebook to see a practical demonstration of these issues.
Read the sections 'Visualizing the gradient flow after initialization' and 'Finding dead neurons in ReLU networks'. The gradient visualization clearly shows the vanishing gradient issue with Sigmoid. The dead neuron analysis gives you a concrete way to measure and understand this potential pitfall of ReLU.
Test your understanding!
You are building a deep neural network for a multi-class classification problem with 10 classes. Which activation functions would you most likely choose for the hidden layers and the final output layer, and why?
Show answer
-
Hidden Layers: I would choose ReLU or one of its variants (like Leaky ReLU). For deep networks, it's crucial to avoid the vanishing gradient problem that plagues Sigmoid and Tanh. ReLU's constant positive gradient helps learning signals propagate effectively.
-
Output Layer: For multi-class classification, the standard choice is the Softmax function. It takes a vector of raw scores (logits) and transforms them into a probability distribution where all outputs are between 0 and 1 and sum to 1. This allows you to interpret the network's output as the probability of the input belonging to each of the 10 classes.
2. Weight Initialization Strategies
In our scratch implementation, we initialized weights to small random numbers. This seems intuitive, but just like with activation functions, the exact way we do this has profound implications. Proper initialization can speed up convergence and improve the final performance of your model.
The Problem with Simple Initializations
Let's consider two "naive" approaches and why they fail.
1. Zero Initialization: What if we set all weights to 0? In this case, every neuron in a hidden layer would get the same input, perform the same calculation, and receive the same gradient during backpropagation. All neurons would learn the exact same thing, making our multi-neuron layer equivalent to a single neuron. This failure to break symmetry renders the network useless.

2. Large Random Initialization: Okay, so we need randomness. What if we just draw weights from a standard normal distribution? In a deep network, this can lead to exploding or vanishing activations.
To understand this better, let's turn to an article that demonstrates this issue with code.
Weight Initialization Techniques in Neural Networks
The article 'Weight Initialization Techniques in Neural Networks' from Pinecone provides clear, intuitive explanations and code examples for the pitfalls of naive initialization.
Read the sections 'Zero or Constant Initialization' and 'Random Initialization'. The first section formalizes the symmetry problem. The second section is key: observe the experiment results. When weights are too small, activations shrink to zero (vanishing). When weights are too large, activations in Sigmoid/Tanh saturate at -1 or 1, killing the gradients (vanishing gradients again).
Smart Initialization: Glorot and He
The core idea behind modern initialization is to set the initial random weights' variance carefully to ensure that the variance of the activations and gradients remains roughly constant across all layers. This keeps the signal flowing smoothly forward and backward.
The two most important methods are named after their creators: Glorot (also known as Xavier) and He. The choice between them depends on the activation function you use.
Let's get a quick and clear summary of these techniques from a video.
Weight Initialization for Deep Feedforward Neural Networks
This AssemblyAI video concisely explains Glorot, He, and LeCun initializations, linking each one to the activation functions they are designed for.
Watch the segment from 01:35 to 03:17. Pay close attention to the formula for the variance in each case and the recommended activation function.
Here's the breakdown:
-
Glorot (Xavier) Initialization:
- Goal: Keep the variance of activations the same across layers. It balances the variance for the forward pass and the backward pass.
- Formula: Draws weights from a distribution with zero mean and variance , where is the number of input units and is the number of output units.
- Best for: Tanh and Sigmoid activations. These functions are symmetric around zero, which this initialization scheme assumes.
-
He Initialization:
- Goal: Designed specifically for ReLU activations.
- Formula: Draws weights from a distribution with zero mean and variance . The factor of 2 is crucial; it accounts for the fact that ReLU sets about half of the activations to zero, which would otherwise halve the variance.
- Best for: ReLU and its variants (Leaky ReLU, ELU).
To see the motivation behind these formulas in more detail, we can refer back to the Pinecone article.
Weight Initialization Techniques in Neural Networks
This article provides the reasoning behind the formulas for Glorot and He initialization.
Read the sections 'Xavier or Glorot Initialization' and 'He Initialization'. You don't need to follow every mathematical step, but focus on the logic: Glorot considers both fan_in and fan_out to stabilize forward and backward passes, while He adds a factor of 2 to compensate for ReLU's behavior.
Conclusion
In this lesson, we demystified two critical hyperparameters that you used in the last lesson. You now have a solid framework for making intelligent choices about them.
Key Takeaways:
- Activation functions are essential for introducing the non-linearity that allows neural networks to learn complex patterns.
- ReLU and its variants are the standard choice for hidden layers because they mitigate the vanishing gradient problem, though one must be mindful of the "dying ReLU" issue. Sigmoid/Tanh are often reserved for specific output layers (binary classification) or older architectures.
- Weight initialization is crucial for breaking neuron symmetry and ensuring stable gradient flow during training.
- The choice of initialization is directly linked to the activation function: use He initialization for ReLU-based networks and Glorot (Xavier) initialization for Tanh/Sigmoid-based networks.
Preview of the Next Lesson:
So far, we have built the machinery for a single update step. We know how to initialize a network, pass data forward, and use backpropagation to find the gradients. The next question is: how do we use our dataset to drive these updates? In the upcoming lesson, we will "Apply batch gradient descent and stochastic gradient descent (SGD) and compare their training dynamics." We'll explore the trade-offs between updating our model after every single example versus after seeing a whole batch of them.