I believe it makes most sense for me to start my first-ever blog with Neural Networks, as this is where I want to specialize.

Before going into technical details and all the mathematics behind of Neural Networks, I want to talk a bit about what it actually is. It’s a cheap shot to “copy” human brain, using a unit called artificial neurons to process data and recognize patterns. And for our “Hello, World!” I will be talking about MNIST dataset neural network, a machine learning model trained to recognize handwritten digits ranging from 0 to 9. It’s important to keep in mind that this is a Feedforward Neural Network (FNN).

This system has four key parts. First is the input layer, where the model takes in raw data, such as pixels of a handwritten digit (or words from text, though not for MNIST). Second are the hidden layers, which sit between the input and output where the computer does the math to find patterns. Third is the output layer, which, as name suggests, gives the final answer/prediction. Fourth are the weights and biases. And yeah, it would be really fun and easy if this was how things simply played out, but (un)fortunately, this is a really high-level overview of what’s actually happening. So let me go a bit more into detail.

It is also important to understand what values neurons hold. Normalized MNIST inputs are between 0 and 1, but a neuron’s value depends on its activation function. For example, ReLU outputs can be any non-negative number, while the output probabilities produced by softmax are between 0 and 1.

  • Input Layer: A 28×2828 \times 28 pixel image is flattened into a one-dimensional vector of 784 values. Each hidden neuron receives all 784 input values through separate weighted connections. For hidden neuron jj, the weighted sum before activation is

zj=i=1784wjixi+bj,z_j = \sum_{i=1}^{784} w_{ji}x_i + b_j,

where xix_i is input pixel ii, wjiw_{ji} is the weight connecting input ii to neuron jj, and bjb_j is that neuron’s bias.

  • Hidden Layer: The raw value zjz_j is passed through a non-linear activation function such as ReLU:

aj=ReLU(zj)=max(0,zj).a_j = \operatorname{ReLU}(z_j) = \max(0, z_j).

Here, aja_j is the neuron’s activation. Without non-linear activation functions, multiple layers would collapse into a single transformation and the network could only model linear decision boundaries. This process repeats through every hidden layer in the network and becomes more expressive as the network gets deeper.

Diagram of a deep neural network with an input layer, three hidden layers, and an output layer.

Image source: IBM, “What Is a Neural Network?”.

  • Output Layer: The final hidden layer connects to an output layer consisting of 10 nodes, one for each digit from 0 to 9. Its raw outputs are called logits. To distinguish them from the hidden neuron’s value zjz_j, let oio_i represent output logit ii. Softmax converts each output logit into a normalized probability y^i\hat{y}_i:

y^i=softmax(o)i=eoik=09eok.\hat{y}_i = \operatorname{softmax}(\mathbf{o})_i = \frac{e^{o_i}}{\sum_{k=0}^{9} e^{o_k}}.

The probabilities are each between 0 and 1 and sum to 1. The class with the highest probability becomes the model’s prediction (i.e., the model assigns a 95% probability to digit 2):

c^=arg maxi{0,,9}y^i.\hat{c} = \operatorname*{arg\,max}_{i \in \{0, \ldots, 9\}} \hat{y}_i.

During training, a loss function compares this distribution with the ground-truth label. For MNIST, a common choice is cross-entropy loss:

L(y,y^)=i=09yilog(y^i).\mathcal{L}(\mathbf{y}, \hat{\mathbf{y}}) = -\sum_{i=0}^{9} y_i \log(\hat{y}_i).

Here, yiy_i is component ii of the true one-hot label vector (1 for the correct digit and 0 for every other digit), and y^i\hat{y}_i is the predicted probability. If tt is the true class, the sum simplifies to

L=log(y^t).\mathcal{L} = -\log(\hat{y}_t).

Backpropagation uses the chain rule to determine how much each weight and bias contributed to the loss. Gradient descent then updates all the model’s parameters, represented by θ\theta, in the direction that reduces the loss. The update from iteration nn to iteration n+1n+1 is

θn+1=θnηθL(θn),\theta_{n+1} = \theta_n - \eta \nabla_{\theta}\mathcal{L}(\theta_n),

where θn\theta_n contains the current parameter values, θn+1\theta_{n+1} contains the updated values, L(θn)\mathcal{L}(\theta_n) is the loss produced by the current parameters, and η\eta is the learning rate. The term ηθL(θn)\eta \nabla_{\theta}\mathcal{L}(\theta_n) is subtracted from θn\theta_n because the gradient points in the direction in which the loss increases most quickly. Subtracting it moves the parameters in the opposite direction, toward lower loss and, hopefully, a local minimum.

In practice, training begins with randomized weight initialization, while biases are often initialized to zero. Rather than using the entire dataset for every update, training usually processes one mini-batch at a time. For each mini-batch, the network performs a forward pass, calculates the loss, and uses backpropagation to calculate the gradient of the loss with respect to every parameter. The optimizer then applies the update above. Once the model has processed every mini-batch in the training set, it has completed one epoch.

This process usually repeats for a fixed number of epochs, or early stopping ends training when performance on a validation set no longer improves. Convergence does not necessarily mean the parameters stop moving or the model reaches the minimum possible error. Neural-network loss surfaces are non-convex, so training may instead reach a useful solution where further improvements are small. The learning rate is where things can go wrong: if it is too large, training can become unstable and simply “miss” the local minimum; if it is too small, training can take too long and waste resources.