Forward and Backward Propagation for Single and Multiple Training Examples

Machine Learning Notes · appendix B of 2 · Forward and backward, entry by entry · superseded by reference Part IV ← Appendix A end of the appendices →

This document develops the mechanics of a feed-forward neural network in full: how an input is transformed into a prediction (forward propagation), and how the resulting error is used to update the parameters (backward propagation). Each result is presented twice. We first derive it for a single training example, where the reasoning is most transparent, and then state it in vectorized form for a batch of $m$ examples, which corresponds to a practical implementation. A single, fixed set of symbols is used throughout and is summarized in Table 1.1.

Overview. In the forward direction, every layer performs two operations: a linear transformation followed by a nonlinearity. Backward propagation reverses this chain, reusing the quantities cached during the forward pass. Tracking the shape of each matrix is often sufficient to reconstruct the formulas, since in most cases only one product is dimensionally consistent.

Contents
  1. Notation Conventions
  2. Forward Propagation: Single Training Example
  3. Forward Propagation: $m$ Training Examples (Vectorized)
  4. Backward Propagation: Single Training Example
  5. Backward Propagation: $m$ Training Examples (Vectorized)
  6. Initial Gradient at the Final Layer (Binary Classification)

Notation Conventions

Before introducing the equations, we fix a consistent naming scheme. A neural-network expression must identify four things at once: the layer, the neuron, the training example, and whether the quantity is a scalar, a vector, or a matrix. The following conventions encode all four unambiguously:

The two superscripts are the most important to distinguish: $[l]$ denotes the layer and $(i)$ denotes the training example, while a subscript ($j$ or $k$) selects an individual neuron. For example, $a_j^{[l]{}(i)}$ is a single scalar — the activation of neuron $j$ in layer $l$ for example $i$.

Throughout, it is helpful to move between two equivalent views of the same object. In the single-example view, a quantity is a column vector, which is convenient for applying the chain rule. In the vectorized view, these columns are placed side by side to form a matrix, one column per example. Every batched equation below is simply its single-example counterpart with the examples arranged as columns.

A complete summary of the notation is given in Table 1.1.

ConceptSingle elementVector / MatrixDescription
Indices and Dimensions
Layer index$[l]$Layer number, $l = 1, 2, \ldots, L$
Example index$(i)$Training example, $i = 1, 2, \ldots, m$
Neuron index$j$ or $k$Neuron in a layer, $j = 1, \ldots, n_l$
Neurons in layer $l$$n_l$Number of neurons in layer $l$
Number of examples$m$Total training examples in batch
Forward Propagation — Activations
Input (layer 0)$x_k^{(i)}$$\mathbf{x}^{(i)} \in \mathbb{R}^{n_0},\ \mathbf{X} \in \mathbb{R}^{n_0 \times m}$Input feature $k$ for example $i$
Activation$a_j^{[l]{}(i)}$$\mathbf{a}^{[l]{}(i)} \in \mathbb{R}^{n_l},\ \mathbf{A}^{[l]} \in \mathbb{R}^{n_l \times m}$Activation of neuron $j$ in layer $l$ for example $i$
Pre-activation$z_j^{[l]{}(i)}$$\mathbf{z}^{[l]{}(i)} \in \mathbb{R}^{n_l},\ \mathbf{Z}^{[l]} \in \mathbb{R}^{n_l \times m}$Weighted sum + bias for neuron $j$, layer $l$, example $i$
Forward Propagation — Parameters
Weight$w_{j,k}^{[l]}$$\mathbf{W}^{[l]} \in \mathbb{R}^{n_l \times n_{l-1}}$Weight from neuron $k$ in layer $l-1$ to neuron $j$ in layer $l$
Weight vector (row)$\mathbf{w}_j^{[l]} \in \mathbb{R}^{1 \times n_{l-1}}$Row $j$ of $\mathbf{W}^{[l]}$: all weights feeding into neuron $j$ in layer $l$, i.e. $\mathbf{w}_j^{[l]} = [w_{j,1}^{[l]}, w_{j,2}^{[l]}, \ldots, w_{j,n_{l-1}}^{[l]}]$
Bias$b_j^{[l]}$$\mathbf{b}^{[l]} \in \mathbb{R}^{n_l}$Bias for neuron $j$ in layer $l$
Activation function$g^{[l]}(\cdot)$Nonlinearity applied at layer $l$ (e.g., ReLU, sigmoid)
Backward Propagation — Gradients w.r.t. Activations
Activation gradient$da_j^{[l]{}(i)}$$d\mathbf{a}^{[l]{}(i)} \in \mathbb{R}^{n_l},\ d\mathbf{A}^{[l]} \in \mathbb{R}^{n_l \times m}$$\frac{\partial L}{\partial a_j^{[l]{}(i)}}$: gradient w.r.t. activation of neuron $j$, layer $l$, example $i$
Pre-activation gradient$dz_j^{[l]{}(i)}$$d\mathbf{z}^{[l]{}(i)} \in \mathbb{R}^{n_l},\ d\mathbf{Z}^{[l]} \in \mathbb{R}^{n_l \times m}$$\frac{\partial L}{\partial z_j^{[l]{}(i)}}$: gradient w.r.t. pre-activation of neuron $j$, layer $l$, example $i$
Backward Propagation — Gradients w.r.t. Parameters
Weight gradient$\frac{\partial L}{\partial w_{j,k}^{[l]}}$$d\mathbf{W}^{[l]} \in \mathbb{R}^{n_l \times n_{l-1}}$Gradient w.r.t. weight from neuron $k$ (layer $l-1$) to neuron $j$ (layer $l$)
Bias gradient$\frac{\partial L}{\partial b_j^{[l]}}$$d\mathbf{b}^{[l]} \in \mathbb{R}^{n_l}$Gradient w.r.t. bias of neuron $j$ in layer $l$
Loss and Output
Prediction$\hat{y}^{(i)} = a^{[L]{}(i)}$$\mathbf{Y} = \mathbf{A}^{[L]} \in \mathbb{R}^{n_L \times m}$Network output for example $i$
True label$y^{(i)}$$\mathbf{Y} \in \mathbb{R}^{n_L \times m}$Ground truth label for example $i$
Loss (single)$L^{(i)}$Loss for training example $i$
Cost (batch)$J$$J = \frac{1}{m}\sum_{i=1}^{m} L^{(i)}$
Special Notation
Hadamard product$\odot$Element-wise multiplication
Ones vector$\mathbf{1}_m \in \mathbb{R}^m$Vector of ones for broadcasting
Transpose$(\cdot)^T$Matrix/vector transpose
Table 1.1: Comprehensive notation for neural network forward and backward propagation. Subscript $j$ or $k$ denotes neuron index, superscript $[l]$ denotes layer, and superscript $(i)$ denotes training example.

The table distinguishes the cost $J$ from the per-example losses $L^{(i)}$: the cost is the average of the losses over the batch. Training minimizes $J$, but each example contributes its own loss and its own gradient, so the batch gradient is the average of the per-example gradients. This averaging is the origin of the factor $\tfrac{1}{m}$ that appears in the vectorized parameter gradients derived below.


Forward Propagation: Single Training Example

Forward propagation computes the network's prediction. Beginning with the input $\mathbf{x}^{(i)}$ (equivalently, $\mathbf{a}^{[0]{}(i)}$), each layer transforms the activations of the previous layer into its own, until the final layer produces the output $\hat{y}^{(i)}$. Every layer applies the same two operations: a linear transformation followed by a nonlinear activation function. We first establish the exact dimensions of each object, since these dimensions determine the form of nearly every equation that follows.

A concrete instance of such a network is drawn in Figure 2.1, using exactly the symbols of Table 1.1 and shown for a single training example $i$, so every activation carries the example superscript $(i)$. The input is layer $0$ (features $x_k^{(i)} = a_k^{[0]{}(i)}$, here $n_0 = 3$); the two hidden layers are $l = 1, 2$ with activations $a_j^{[l]{}(i)}$ (here $n_1 = n_2 = 4$); and the output is layer $L = 3$ with $\hat y_j^{(i)} = a_j^{[L]{}(i)}$ ($n_3 = 2$). The weights $w_{j,k}^{[l]}$ and biases $b_j^{[l]}$ carry no $(i)$, since the same parameters are shared across all examples. Every neuron in layer $l-1$ connects to every neuron in layer $l$, and each connection carries a weight $w_{j,k}^{[l]}$ with row $j$ = destination neuron in layer $l$ and column $k$ = source neuron in layer $l-1$ — the same convention laid out for $\mathbf{W}^{[l]}$ in Equation 2.2. To keep the picture readable, only the weights feeding the top neuron of each layer are labelled (the first row of each weight matrix $\mathbf{W}^{[l]}$); every other edge carries its own $w_{j,k}^{[l]}$ in the same way. Each layer also has a dashed bias unit ($1$) whose edges broadcast a bias $b_j^{[l]}$ to every neuron of that layer, one component of the bias vector $\mathbf{b}^{[l]}$ of Equation 2.3; the bias edges of every layer are labelled $b_j^{[l]}$ to show the full bias vector $\mathbf{b}^{[l]}$. The number of weight connections is the product of adjacent layer sizes, $4\cdot 3 + 4\cdot 4 + 2\cdot 4 = 36$.

Figure 2.1: A fully-connected feed-forward network in the notation of the table above, drawn for a single training example $i$ (every activation carries the superscript $(i)$): input layer $0$, two hidden layers ($l=1,2$), and output layer $L=3$. Each solid edge is a weight $w_{j,k}^{[l]}$ from source neuron $k$ in layer $l-1$ to destination neuron $j$ in layer $l$; only the first row of each $\mathbf{W}^{[l]}$ is labelled. Each dashed bias unit broadcasts a bias $b_j^{[l]}$ to every neuron of its layer; weights and biases are shared across examples and so carry no $(i)$.

Inside a Single Neuron

The network figure draws each neuron as a single circle, which hides the two operations every neuron actually performs: a linear step — scale each incoming activation by its weight and add the bias — followed by a nonlinearity $g$. Figure 2.2 zooms in on one neuron, neuron $3$ of hidden layer $1$ ($a_3^{[1]{}(i)}$), and unfolds it. Reading left to right, each input $a_k^{[0]{}(i)}$ is multiplied ($\times$) by its weight $w_{3,k}^{[1]}$; the three products are added ($+$) together with the bias $b_3^{[1]}$ to form the pre-activation

$$ z_3^{[1](i)} = \sum_{k=1}^{3} w_{3,k}^{[1]}\, a_k^{[0](i)} + b_3^{[1]} = w_{3,1}^{[1]} a_1^{[0](i)} + w_{3,2}^{[1]} a_2^{[0](i)} + w_{3,3}^{[1]} a_3^{[0](i)} + b_3^{[1]}, \tag{2.1} $$
Equation 2.1: Pre-activation of neuron $a_3^{[1]{}(i)}$

and finally the activation function is applied to obtain the neuron's output $a_3^{[1]{}(i)} = g(z_3^{[1]{}(i)})$. Every other neuron in the network — in every layer — works in exactly this way; the full network is just this same multiply–sum–activate unit repeated and wired together.

Figure 2.2: Inside hidden neuron $a_3^{[1]{}(i)}$: the single circle of the network figure expanded into its elementary operations. Each input $a_k^{[0]{}(i)}$ is scaled by a weight $w_{3,k}^{[1]}$ (blue $\times$ nodes); the scaled inputs and the bias $b_3^{[1]}$ (orange) are added ($+$) into the pre-activation $z_3^{[1]{}(i)}$, and the nonlinearity $g$ turns it into the activation $a_3^{[1]{}(i)} = g(z_3^{[1]{}(i)})$. Weight edges are blue and the bias edge is orange, matching the full-network figure above.

Detailed Matrix and Vector Structures

Before stating the forward equations, we give the explicit structure of each matrix and vector used in layer $l$.

Weight Matrix $\mathbf{W}^{[l]}$

The weight matrix $\mathbf{W}^{[l]} \in \mathbb{R}^{n_l \times n_{l-1}}$ holds the connection strengths from layer $l-1$ (with $n_{l-1}$ neurons) to layer $l$ (with $n_l$ neurons):

$$ \mathbf{W}^{[l]} = \begin{bmatrix} w_{1,1}^{[l]} & w_{1,2}^{[l]} & \cdots & w_{1,n_{l-1}}^{[l]} \\ w_{2,1}^{[l]} & w_{2,2}^{[l]} & \cdots & w_{2,n_{l-1}}^{[l]} \\ \vdots & \vdots & \ddots & \vdots \\ w_{n_l,1}^{[l]} & w_{n_l,2}^{[l]} & \cdots & w_{n_l,n_{l-1}}^{[l]} \end{bmatrix} \in \mathbb{R}^{n_l \times n_{l-1}} \tag{2.2} $$
Equation 2.2: Weight matrix

Read the matrix by its indices:

The index order follows the convention row = destination, column = source. Row $j$ of $\mathbf{W}^{[l]}$ therefore contains all incoming weights for neuron $j$, which is the weight vector $\mathbf{w}_j^{[l]}$ defined in Table 1.1. The dimensions follow directly: mapping an $n_{l-1}$-dimensional input to an $n_l$-dimensional output requires $n_l$ rows and $n_{l-1}$ columns.

Bias Vector $\mathbf{b}^{[l]}$

The bias vector $\mathbf{b}^{[l]} \in \mathbb{R}^{n_l}$ provides one adjustable offset per neuron in layer $l$:

$$ \mathbf{b}^{[l]} = \begin{bmatrix} b_1^{[l]} \\ b_2^{[l]} \\ \vdots \\ b_{n_l}^{[l]} \end{bmatrix} \in \mathbb{R}^{n_l} \tag{2.3} $$
Equation 2.3: Bias vector

Here Entry $b_j^{[l]}$ is the bias added to the pre-activation of neuron $j$ in layer $l$. The bias shifts the neuron's threshold, allowing it to activate when the weighted input is zero or to remain inactive until the input is sufficiently large; without it, the neuron's response would be constrained to pass through the origin.

Input Matrix $\mathbf{X}$

The input matrix collects the raw feature vectors $\mathbf{x}^{(i)}$ of all $m$ examples as columns. It is the base case $\mathbf{X} = \mathbf{A}^{[0]} \in \mathbb{R}^{n_0 \times m}$ from which forward propagation starts, so the first layer receives $\mathbf{X}$ in place of a previous layer's activations:

$$ \mathbf{X} = \mathbf{A}^{[0]} = \begin{bmatrix} x_1^{(1)} & x_1^{(2)} & \cdots & x_1^{(m)} \\ x_2^{(1)} & x_2^{(2)} & \cdots & x_2^{(m)} \\ \vdots & \vdots & \ddots & \vdots \\ x_{n_0}^{(1)} & x_{n_0}^{(2)} & \cdots & x_{n_0}^{(m)} \end{bmatrix} \in \mathbb{R}^{n_0 \times m} \tag{2.4} $$
Equation 2.4: Input matrix

Its layout is the same as every activation matrix:

Pre-activation Matrix $\mathbf{Z}^{[l]}$ (Vectorized)

The pre-activation of a neuron is its weighted sum of inputs plus bias, computed before the nonlinearity is applied. For $m$ training examples, these values are collected in the matrix $\mathbf{Z}^{[l]} \in \mathbb{R}^{n_l \times m}$:

$$ \mathbf{Z}^{[l]} = \begin{bmatrix} z_1^{[l]{}(1)} & z_1^{[l]{}(2)} & \cdots & z_1^{[l]{}(m)} \\ z_2^{[l]{}(1)} & z_2^{[l]{}(2)} & \cdots & z_2^{[l]{}(m)} \\ \vdots & \vdots & \ddots & \vdots \\ z_{n_l}^{[l]{}(1)} & z_{n_l}^{[l]{}(2)} & \cdots & z_{n_l}^{[l]{}(m)} \end{bmatrix} \in \mathbb{R}^{n_l \times m} \tag{2.5} $$
Equation 2.5: Pre-activation matrix

Notice the layout convention that will hold for every activation-like matrix:

$$ z_j^{[l]{}(i)} = \sum_{k=1}^{n_{l-1}} w_{j,k}^{[l]}\, a_k^{[l-1]{}(i)} + b_j^{[l]} \tag{2.6} $$
Equation 2.6: Pre-activation entry

Equation Equation 2.6 is a dot product followed by a shift: for each neuron $k$ in the previous layer, its activation $a_k^{[l-1]{}(i)}$ is multiplied by the connection strength $w_{j,k}^{[l]}$, the products are summed, and the bias $b_j^{[l]}$ is added. Equivalently, it is row $j$ of $\mathbf{W}^{[l]}$ applied to the input column, which is why the matrix form below is compact.

Forward Equations

With the dimensions established, the forward pass for a single layer reduces to two equations. Consider a layer $l$, and let

The layer applies its two operations in sequence.

Linear step

Combine the inputs with the weights and add the bias; this is Equation 2.6 written for all neurons simultaneously:

$$ \mathbf{z}^{[l]{}(i)} = \mathbf{W}^{[l]}\mathbf{a}^{[l-1]{}(i)} + \mathbf{b}^{[l]}, \qquad \mathbf{z}^{[l]{}(i)} \in \mathbb{R}^{n_l} \tag{2.7} $$
Equation 2.7: Linear step (single example)

The dimensions are consistent: $(n_l \times n_{l-1})\times(n_{l-1}\times 1)$ yields an $n_l \times 1$ column, and adding $\mathbf{b}^{[l]}\in\mathbb{R}^{n_l}$ preserves that size.

Activation step

Pass each score through the nonlinearity:

$$ \mathbf{a}^{[l]{}(i)} = g^{[l]}\!\left(\mathbf{z}^{[l]{}(i)}\right), \tag{2.8} $$
Equation 2.8: Activation step (single example)

where $g^{[l]}$ is applied element-wise to the components of $\mathbf{z}^{[l]{}(i)}$. The nonlinearity is essential to the model's expressiveness: a stack of purely linear layers is equivalent to a single linear map, so without $g$ the network could represent only linear functions.

Cache

During forward propagation, we store the intermediate values required to compute gradients during the backward pass:

$$ \text{cache}^{[l]} = \left( \mathbf{a}^{[l-1]{}(i)},\ \mathbf{W}^{[l]},\ \mathbf{b}^{[l]},\ \mathbf{z}^{[l]{}(i)} \right) \tag{2.9} $$
Equation 2.9: Cache (single example)

Caching is essential rather than a convenience. Backpropagation reuses precisely these quantities — $\mathbf{a}^{[l-1]{}(i)}$ for the weight gradient, $\mathbf{z}^{[l]{}(i)}$ for the slope of the nonlinearity, and $\mathbf{W}^{[l]}$ to pass the signal to the previous layer — so they are stored during the forward pass rather than recomputed.


Forward Propagation: $m$ Training Examples (Vectorized)

Processing one example at a time is conceptually clear but inefficient. In practice the entire batch is propagated through the network simultaneously, replacing $m$ small matrix–vector products with a single matrix–matrix product. The underlying mathematics is unchanged: the per-example columns are simply stacked into matrices.

The input matrix is formed by placing each example's activation column side by side:

$$ \mathbf{A}^{[l-1]} = \begin{bmatrix} \mathbf{a}^{[l-1]{}(1)} & \mathbf{a}^{[l-1]{}(2)} & \cdots & \mathbf{a}^{[l-1]{}(m)} \end{bmatrix} \in \mathbb{R}^{n_{l-1} \times m} \tag{3.1} $$
Equation 3.1: Activation matrix (definition)

Understanding the structure of $\mathbf{A}^{[l-1]}$

Written out entry by entry:

$$ \mathbf{A}^{[l-1]} = \begin{bmatrix} a_1^{[l-1]{}(1)} & a_1^{[l-1]{}(2)} & \cdots & a_1^{[l-1]{}(m)} \\ a_2^{[l-1]{}(1)} & a_2^{[l-1]{}(2)} & \cdots & a_2^{[l-1]{}(m)} \\ \vdots & \vdots & \ddots & \vdots \\ a_{n_{l-1}}^{[l-1]{}(1)} & a_{n_{l-1}}^{[l-1]{}(2)} & \cdots & a_{n_{l-1}}^{[l-1]{}(m)} \end{bmatrix} \in \mathbb{R}^{n_{l-1} \times m} \tag{3.2} $$
Equation 3.2: Activation matrix (full structure)

The layout is the same as always — rows are features/neurons, columns are examples:

Bias Broadcasting

When all $m$ examples are processed together, the product $\mathbf{W}^{[l]}\mathbf{A}^{[l-1]}$ already has the correct shape ($n_l \times m$), but the same bias vector $\mathbf{b}^{[l]}$ must be added to every column, since every example uses the same biases. This is written as the outer product $\mathbf{b}^{[l]}\mathbf{1}_m^T$, which copies $\mathbf{b}^{[l]}$ across all $m$ columns:

$$ \mathbf{b}^{[l]}\mathbf{1}_m^T = \begin{bmatrix} b_1^{[l]} \\ b_2^{[l]} \\ \vdots \\ b_{n_l}^{[l]} \end{bmatrix} \begin{bmatrix} 1 & 1 & \cdots & 1 \end{bmatrix} = \begin{bmatrix} b_1^{[l]} & b_1^{[l]} & \cdots & b_1^{[l]} \\ b_2^{[l]} & b_2^{[l]} & \cdots & b_2^{[l]} \\ \vdots & \vdots & \ddots & \vdots \\ b_{n_l}^{[l]} & b_{n_l}^{[l]} & \cdots & b_{n_l}^{[l]} \end{bmatrix} \in \mathbb{R}^{n_l \times m} \tag{3.3} $$
Equation 3.3: Bias broadcasting

The factor $\mathbf{1}_m^T$ (a row of $m$ ones) formally expresses the repetition of the bias across all $m$ columns. In NumPy and similar libraries it is not written explicitly; the addition is handled automatically by broadcasting:

Z = W @ A_prev + b   # b is broadcast across columns

Vectorized forward equations

The two operations now apply to the entire batch simultaneously.

Linear step

The single matrix product $\mathbf{W}^{[l]}\mathbf{A}^{[l-1]}$ computes the scores for all examples simultaneously; we then add the broadcast bias from Equation 3.3:

$$ \mathbf{Z}^{[l]} = \mathbf{W}^{[l]}\mathbf{A}^{[l-1]} + \mathbf{b}^{[l]}\mathbf{1}_m^T, \tag{3.4} $$
Equation 3.4: Linear step (vectorized)

where $\mathbf{1}_m \in \mathbb{R}^m$ is a vector of ones and $\mathbf{Z}^{[l]} \in \mathbb{R}^{n_l \times m}$. Column $i$ of this equation is exactly the single-example Equation 2.7 — the batch version is just all those columns computed together. In code the broadcast is implicit:

Z = W @ A_prev + b

Activation step

Apply the nonlinearity element-wise to every entry:

$$ \mathbf{A}^{[l]} = g^{[l]}\!\left(\mathbf{Z}^{[l]}\right), \tag{3.5} $$
Equation 3.5: Activation step (vectorized)

where the activation function is again applied element-wise.

Cache

As before, we store what backprop will need — now in matrix form:

$$ \text{cache}^{[l]} = \left( \mathbf{A}^{[l-1]},\ \mathbf{W}^{[l]},\ \mathbf{b}^{[l]},\ \mathbf{Z}^{[l]} \right) \tag{3.6} $$
Equation 3.6: Cache (vectorized)

Backward Propagation: Single Training Example

Forward propagation determines the network's prediction; backward propagation determines how to adjust the network to reduce the error. Its purpose is to compute the gradient of the loss with respect to every weight and bias, which gradient descent then uses to update the parameters.

Backpropagation is based on the chain rule. Because the loss depends on a weight only through a chain of intermediate quantities ($w \to z \to a \to \cdots \to L$), its derivative is the product of the local derivatives along that chain. Backpropagation organizes this computation so that factors shared between derivatives are reused rather than recomputed.

We assume that the gradient of the loss $L$ with respect to this layer's activations for example $i$ is already known; it is the signal supplied by the subsequent layer:

$$ d\mathbf{a}^{[l]{}(i)} = \frac{\partial L}{\partial \mathbf{a}^{[l]{}(i)}} \tag{4.1} $$
Equation 4.1: Activation gradient (given)

From this single incoming signal, three quantities must be produced: the gradients of this layer's parameters, and the gradient passed to the preceding layer:

$$ d\mathbf{W}^{[l]} = \frac{\partial L}{\partial \mathbf{W}^{[l]}}, \qquad d\mathbf{b}^{[l]} = \frac{\partial L}{\partial \mathbf{b}^{[l]}}, \qquad d\mathbf{a}^{[l-1]{}(i)} = \frac{\partial L}{\partial \mathbf{a}^{[l-1]{}(i)}} \tag{4.2} $$
Equation 4.2: Backward targets

The first two ($d\mathbf{W}^{[l]}, d\mathbf{b}^{[l]}$) update the current layer; the third ($d\mathbf{a}^{[l-1]{}(i)}$) becomes the incoming signal for the preceding layer, which is what propagates the error backward. Using the cached quantities $\mathbf{a}^{[l-1]{}(i)}, \mathbf{W}^{[l]}, \mathbf{b}^{[l]}, \mathbf{z}^{[l]{}(i)}$, we first record the shape of each gradient, then derive the four from the chain rule and state them in vector form.

Gradient shapes

Before deriving the gradients, we fix the shape of each one. A gradient always has the same shape as the thing it differentiates, so the three objects produced by a layer's backward pass — $d\mathbf{W}^{[l]}$, $d\mathbf{Z}^{[l]}$, $d\mathbf{b}^{[l]}$ — mirror the forward $\mathbf{W}^{[l]}$, $\mathbf{Z}^{[l]}$, $\mathbf{b}^{[l]}$ exactly.

Weight Gradient Matrix $d\mathbf{W}^{[l]}$

The gradient of the loss with respect to the weights, $d\mathbf{W}^{[l]} \in \mathbb{R}^{n_l \times n_{l-1}}$, has the same shape as $\mathbf{W}^{[l]}$ — one partial derivative per weight:

$$ d\mathbf{W}^{[l]} = \begin{bmatrix} \frac{\partial L}{\partial w_{1,1}^{[l]}} & \frac{\partial L}{\partial w_{1,2}^{[l]}} & \cdots & \frac{\partial L}{\partial w_{1,n_{l-1}}^{[l]}} \\ \frac{\partial L}{\partial w_{2,1}^{[l]}} & \frac{\partial L}{\partial w_{2,2}^{[l]}} & \cdots & \frac{\partial L}{\partial w_{2,n_{l-1}}^{[l]}} \\ \vdots & \vdots & \ddots & \vdots \\ \frac{\partial L}{\partial w_{n_l,1}^{[l]}} & \frac{\partial L}{\partial w_{n_l,2}^{[l]}} & \cdots & \frac{\partial L}{\partial w_{n_l,n_{l-1}}^{[l]}} \end{bmatrix} \in \mathbb{R}^{n_l \times n_{l-1}} \tag{4.3} $$
Equation 4.3: Weight gradient matrix

Each Entry $\frac{\partial L}{\partial w_{j,k}^{[l]}}$ measures how the loss responds to a small change in that single weight; this is the quantity gradient descent uses to update the weight. Over the full batch, the per-example contributions are averaged:

$$ \frac{\partial L}{\partial w_{j,k}^{[l]}} = \frac{1}{m}\sum_{i=1}^{m} dz_j^{[l]{}(i)} \cdot a_k^{[l-1]{}(i)} \tag{4.4} $$
Equation 4.4: Weight gradient (averaged)

Pre-activation Gradient Matrix $d\mathbf{Z}^{[l]}$

The gradient with respect to the pre-activations, $d\mathbf{Z}^{[l]} \in \mathbb{R}^{n_l \times m}$, is the central quantity in backpropagation: the error signal at each neuron's pre-activation.

$$ d\mathbf{Z}^{[l]} = \begin{bmatrix} dz_1^{[l]{}(1)} & dz_1^{[l]{}(2)} & \cdots & dz_1^{[l]{}(m)} \\ dz_2^{[l]{}(1)} & dz_2^{[l]{}(2)} & \cdots & dz_2^{[l]{}(m)} \\ \vdots & \vdots & \ddots & \vdots \\ dz_{n_l}^{[l]{}(1)} & dz_{n_l}^{[l]{}(2)} & \cdots & dz_{n_l}^{[l]{}(m)} \end{bmatrix} \in \mathbb{R}^{n_l \times m} \tag{4.5} $$
Equation 4.5: Pre-activation gradient matrix

Its Entry $dz_j^{[l]{}(i)} = \frac{\partial L}{\partial z_j^{[l]{}(i)}}$ is the gradient of the loss with respect to the pre-activation of neuron $j$ in layer $l$ for example $i$, obtained by passing the activation gradient back through the nonlinearity:

$$ dz_j^{[l]{}(i)} = da_j^{[l]{}(i)} \cdot g^{[l]\prime}\!\left(z_j^{[l]{}(i)}\right) \tag{4.6} $$
Equation 4.6: Pre-activation gradient entry

where $g^{[l]\prime}$ is the derivative of the activation function. This relation is derived from the chain rule in Section 4.2 below; it is the product of the upstream gradient and the local slope of the nonlinearity.

Bias Gradient Vector $d\mathbf{b}^{[l]}$

The gradient with respect to the biases, $d\mathbf{b}^{[l]} \in \mathbb{R}^{n_l}$, has one entry per neuron:

$$ d\mathbf{b}^{[l]} = \begin{bmatrix} \frac{\partial L}{\partial b_1^{[l]}} \\ \frac{\partial L}{\partial b_2^{[l]}} \\ \vdots \\ \frac{\partial L}{\partial b_{n_l}^{[l]}} \end{bmatrix} \in \mathbb{R}^{n_l} \tag{4.7} $$
Equation 4.7: Bias gradient vector

Each Entry $\frac{\partial L}{\partial b_j^{[l]}}$ measures how much the loss changes with respect to the bias of neuron $j$. Because the bias is added directly to the score, its gradient is simply the score's gradient, summed (averaged) over the examples:

$$ \frac{\partial L}{\partial b_j^{[l]}} = \frac{1}{m}\sum_{i=1}^{m} dz_j^{[l]{}(i)} \tag{4.8} $$
Equation 4.8: Bias gradient (averaged)

In NumPy: db = (1/m) * np.sum(dZ, axis=1, keepdims=True), which averages $d\mathbf{Z}$ over the example axis.

The chain rule, component by component

Every equation in this section follows from one principle: a parameter influences the loss $L$ only through the pre-activation $z_j^{[l]}$, which determines the activation $a_j^{[l]}$ and, through it, the rest of the network. The chain rule multiplies the local derivatives along this path. Each result is derived below for a single scalar component, with the example index $(i)$ suppressed for clarity; the vector and matrix forms then follow by collecting components.

The pre-activation gradient comes from the activation $a_j^{[l]} = g^{[l]}(z_j^{[l]})$, which depends on $z_j^{[l]}$ alone, so the chain rule contributes a single factor:

$$ dz_j^{[l]} = \frac{\partial L}{\partial z_j^{[l]}} = \frac{\partial L}{\partial a_j^{[l]}}\,\frac{\partial a_j^{[l]}}{\partial z_j^{[l]}} = da_j^{[l]}\, g^{[l]\prime}(z_j^{[l]}). $$

Collecting this over all neurons $j$ yields the Hadamard product in Equation 4.9.

The weight gradient uses the linear step $z_j^{[l]} = \sum_{k} w_{j,k}^{[l]} a_k^{[l-1]} + b_j^{[l]}$: the weight $w_{j,k}^{[l]}$ enters only $z_j^{[l]}$, with $\partial z_j^{[l]}/\partial w_{j,k}^{[l]} = a_k^{[l-1]}$. Therefore

$$ \frac{\partial L}{\partial w_{j,k}^{[l]}} = \frac{\partial L}{\partial z_j^{[l]}}\,\frac{\partial z_j^{[l]}}{\partial w_{j,k}^{[l]}} = dz_j^{[l]}\, a_k^{[l-1]}, $$

and stacking over all $j$ and $k$ gives the outer product in Equation 4.10.

The bias gradient is the simplest case: the bias enters $z_j^{[l]}$ additively, so $\partial z_j^{[l]}/\partial b_j^{[l]} = 1$ and the factor collapses,

$$ \frac{\partial L}{\partial b_j^{[l]}} = \frac{\partial L}{\partial z_j^{[l]}}\,\frac{\partial z_j^{[l]}}{\partial b_j^{[l]}} = dz_j^{[l]}, $$

which is Equation 4.11.

The previous-activation gradient is the one case that requires a sum. The activation $a_k^{[l-1]}$ feeds into every pre-activation $z_j^{[l]}$ of the current layer, so the loss depends on it through all $n_l$ neurons, and the multivariable chain rule adds the contribution of each path:

$$ da_k^{[l-1]} = \frac{\partial L}{\partial a_k^{[l-1]}} = \sum_{j=1}^{n_l} \frac{\partial L}{\partial z_j^{[l]}}\,\frac{\partial z_j^{[l]}}{\partial a_k^{[l-1]}} = \sum_{j=1}^{n_l} dz_j^{[l]}\, w_{j,k}^{[l]}. $$

The sum over $j$ is exactly the $k$-th entry of $(\mathbf{W}^{[l]})^{T} d\mathbf{z}^{[l]}$, which is Equation 4.12. This is the general pattern to remember: when a quantity affects the loss through several paths, the chain rule adds each path's contribution.

Backward equations (single example)

The same four results, now written directly in vector and matrix form.

Through the activation

The signal is first propagated back through the nonlinearity. Since $\mathbf{a}^{[l]{}(i)} = g^{[l]}(\mathbf{z}^{[l]{}(i)})$ element-wise, the chain rule introduces a factor of the slope $g^{[l]\prime}$ evaluated at each neuron's pre-activation:

$$ d\mathbf{z}^{[l]{}(i)} = d\mathbf{a}^{[l]{}(i)} \odot g^{[l]\prime}\!\left(\mathbf{z}^{[l]{}(i)}\right), \tag{4.9} $$
Equation 4.9: Through the activation (single example)

where $g^{[l]\prime}$ denotes the derivative of the activation function, applied element-wise. The Hadamard product $\odot$ appears, rather than a matrix product, because the nonlinearity acts on each neuron independently: neuron $j$'s pre-activation affects only neuron $j$'s activation. This is Equation 4.6 in vector form.

Gradient with respect to weights

Since $\mathbf{z}^{[l]{}(i)} = \mathbf{W}^{[l]}\mathbf{a}^{[l-1]{}(i)} + \mathbf{b}^{[l]}$, each weight $w_{j,k}$ is multiplied by the input $a_k^{[l-1]{}(i)}$, so its gradient is the score's gradient scaled by that input. Collecting all $j,k$ gives an outer product:

$$ d\mathbf{W}^{[l]} = d\mathbf{z}^{[l]{}(i)}\left(\mathbf{a}^{[l-1]{}(i)}\right)^T, \tag{4.10} $$
Equation 4.10: Weight gradient (single example)

which has shape $n_l \times n_{l-1}$, matching $\mathbf{W}^{[l]}$ as any weight gradient must. (Shape check: $(n_l\times1)\times(1\times n_{l-1})=n_l\times n_{l-1}$.) The outer product expresses each weight's gradient as the product of the error at its output neuron and the activation of its input neuron.

Gradient with respect to biases

Because the bias is added directly to the pre-activation, a unit change in $b_j$ changes $z_j$ by one unit, so the local derivative is $1$. The bias gradient is therefore equal to the pre-activation gradient:

$$ d\mathbf{b}^{[l]} = d\mathbf{z}^{[l]{}(i)}, \tag{4.11} $$
Equation 4.11: Bias gradient (single example)

since each component of $\mathbf{b}^{[l]}$ is added directly to the corresponding component of $\mathbf{z}^{[l]{}(i)}$.

Gradient with respect to previous activations

Finally, the signal is passed to the preceding layer. Each input activation $a_k^{[l-1]{}(i)}$ influences every pre-activation $z_j^{[l]{}(i)}$ through the weight $w_{j,k}$; these contributions are collected by the transpose of $\mathbf{W}^{[l]}$:

$$ d\mathbf{a}^{[l-1]{}(i)} = \left(\mathbf{W}^{[l]}\right)^T d\mathbf{z}^{[l]{}(i)} \tag{4.12} $$
Equation 4.12: Previous-activation gradient (single example)

The transpose reflects the same weights used in the opposite direction: in the forward pass, $\mathbf{W}^{[l]}$ maps layer $l-1$ to layer $l$; in the backward pass, $(\mathbf{W}^{[l]})^T$ maps the error in layer $l$ back to layer $l-1$. (Shape check: $(n_{l-1}\times n_l)\times(n_l\times1)=n_{l-1}\times1$.)

These four equations are sufficient to implement backpropagation for a single layer and a single training example. Applying them from the final layer to the first — feeding each layer's Equation 4.12 into the preceding layer's Equation 4.9 — propagates the error through the entire network.


Backward Propagation: $m$ Training Examples (Vectorized)

As in the forward pass, the batch is processed at once by stacking the per-example columns. The four steps are unchanged except for a factor of $\tfrac{1}{m}$, which arises because the cost is the average loss over the batch (see Table 1.1).

We stack the incoming activation gradients into a matrix:

$$ d\mathbf{A}^{[l]} = \begin{bmatrix} d\mathbf{a}^{[l]{}(1)} & d\mathbf{a}^{[l]{}(2)} & \cdots & d\mathbf{a}^{[l]{}(m)} \end{bmatrix} \in \mathbb{R}^{n_l \times m} \tag{5.1} $$
Equation 5.1: Activation gradient matrix (definition)

Understanding the structure of $d\mathbf{A}^{[l]}$

The full matrix structure is:

$$ d\mathbf{A}^{[l]} = \begin{bmatrix} da_1^{[l]{}(1)} & da_1^{[l]{}(2)} & \cdots & da_1^{[l]{}(m)} \\ da_2^{[l]{}(1)} & da_2^{[l]{}(2)} & \cdots & da_2^{[l]{}(m)} \\ \vdots & \vdots & \ddots & \vdots \\ da_{n_l}^{[l]{}(1)} & da_{n_l}^{[l]{}(2)} & \cdots & da_{n_l}^{[l]{}(m)} \end{bmatrix} \in \mathbb{R}^{n_l \times m} \tag{5.2} $$
Equation 5.2: Activation gradient matrix (full structure)

where:

Using the cached matrices $\mathbf{Z}^{[l]}, \mathbf{A}^{[l-1]}, \mathbf{W}^{[l]}$, we compute the batched versions of the four steps.

Vectorized backward equations

Through the activation

Applied element-wise, exactly as in the single-example case, but over the entire matrix:

$$ d\mathbf{Z}^{[l]} = d\mathbf{A}^{[l]} \odot g^{[l]\prime}\!\left(\mathbf{Z}^{[l]}\right) \tag{5.3} $$
Equation 5.3: Through the activation (vectorized)

Gradient with respect to weights

The single-example outer product in Equation 4.10 becomes a matrix product that sums over the examples automatically: the product $d\mathbf{Z}^{[l]}(\mathbf{A}^{[l-1]})^T$ accumulates each example's contribution, and the factor $\tfrac{1}{m}$ converts the sum into an average:

$$ d\mathbf{W}^{[l]} = \frac{1}{m} d\mathbf{Z}^{[l]}\left(\mathbf{A}^{[l-1]}\right)^T \tag{5.4} $$
Equation 5.4: Weight gradient (vectorized)

This is Equation 4.4 in matrix form. (Shape check: $(n_l\times m)\times(m\times n_{l-1}) = n_l\times n_{l-1}$ — the $m$ contracts away, which is the summation over examples.)

Gradient with respect to biases

Averaging Equation 4.11 over the batch means summing $d\mathbf{Z}^{[l]}$ across its columns, which the product with $\mathbf{1}_m$ performs:

$$ d\mathbf{b}^{[l]} = \frac{1}{m} d\mathbf{Z}^{[l]}\mathbf{1}_m, \tag{5.5} $$
Equation 5.5: Bias gradient (vectorized)

which corresponds, in NumPy, to summing over the example axis:

db = (1/m) * np.sum(dZ, axis=1, keepdims=True)

Gradient with respect to previous activations

The signal passed to the preceding layer, identical to Equation 4.12 but applied to all columns at once. No factor of $\tfrac{1}{m}$ appears here, because this is a gradient of activations rather than an averaged parameter update; each example retains its own signal:

$$ d\mathbf{A}^{[l-1]} = \left(\mathbf{W}^{[l]}\right)^T d\mathbf{Z}^{[l]} \tag{5.6} $$
Equation 5.6: Previous-activation gradient (vectorized)

As before, these four equations are sufficient to implement backpropagation for a single layer in the fully vectorized setting. Applying them from layer $L$ back to layer $1$ yields the gradients for the entire network.


Initial Gradient at the Final Layer (Binary Classification)

One element remains to be specified. Every backward step above assumes that $d\mathbf{a}^{[l]{}(i)}$, the gradient of the loss with respect to the layer's activations, is already available. The initial such gradient — the gradient of the loss with respect to the network's output — is obtained directly from the loss function.

Consider binary classification with a sigmoid output layer $L$ having $n_L = 1$ neuron (a single output). For a single training example $i$, the single output is the predicted probability:

$$ a_1^{[L]{}(i)} = \hat{y}^{(i)}, \qquad y^{(i)} \in \{0, 1\}, \tag{6.1} $$
Equation 6.1: Binary output

where:

The appropriate loss for a predicted probability is the logistic (binary cross-entropy) loss, which is large when the prediction is both confident and incorrect:

$$ L^{(i)} = -\left( y^{(i)} \log a_1^{[L]{}(i)} + \left(1 - y^{(i)}\right) \log\left(1 - a_1^{[L]{}(i)}\right) \right) \tag{6.2} $$
Equation 6.2: Binary cross-entropy loss

Only one of the two terms is active per example: if $y^{(i)}=1$ the loss is $-\log \hat{y}^{(i)}$ (penalizing a low predicted probability of the true class), and if $y^{(i)}=0$ it is $-\log(1-\hat{y}^{(i)})$. Differentiating Equation 6.2 with respect to the output activation gives:

$$ \frac{\partial L^{(i)}}{\partial a_1^{[L]{}(i)}} = -\frac{y^{(i)}}{a_1^{[L]{}(i)}} + \frac{1 - y^{(i)}}{1 - a_1^{[L]{}(i)}} \tag{6.3} $$
Equation 6.3: Loss derivative w.r.t. output

This quantity is the required initial gradient. We denote it:

$$ da_1^{[L]{}(i)} = -\frac{y^{(i)}}{a_1^{[L]{}(i)}} + \frac{1 - y^{(i)}}{1 - a_1^{[L]{}(i)}}, \tag{6.4} $$
Equation 6.4: Initial gradient (single example)

This serves as the starting point for backpropagation at the final layer: substituting it into Equation 4.9 (with $g^{[L]}$ the sigmoid) initiates the backward pass.

Simplification for the sigmoid. When the output nonlinearity is the sigmoid, $g^{[L]\prime} = a(1-a)$. Substituting Equation 6.4 into Equation 4.9, the denominators cancel and the expression reduces to $dz_1^{[L]{}(i)} = a_1^{[L]{}(i)} - y^{(i)}$ — the prediction minus the target. This is a principal reason the sigmoid and the cross-entropy loss are commonly paired.

General case: multiple output neurons ($n_L > 1$)

For multi-class classification or multiple outputs, the final layer $L$ has $n_L$ neurons, each with its own initial gradient. The activation and gradient for neuron $j$ in layer $L$ for example $i$ are:

$$ a_j^{[L]{}(i)} \quad\text{and}\quad da_j^{[L]{}(i)} = \frac{\partial L^{(i)}}{\partial a_j^{[L]{}(i)}}, \qquad j = 1, 2, \ldots, n_L \tag{6.5} $$
Equation 6.5: Multiple output neurons

Vectorized form for all $m$ examples

Stacking predictions and labels in the usual layout (neurons in rows, examples in columns), let $\mathbf{A}^{[L]}, \mathbf{Y} \in \mathbb{R}^{n_L \times m}$:

$$ \mathbf{A}^{[L]} = \begin{bmatrix} a_1^{[L]{}(1)} & a_1^{[L]{}(2)} & \cdots & a_1^{[L]{}(m)} \\ a_2^{[L]{}(1)} & a_2^{[L]{}(2)} & \cdots & a_2^{[L]{}(m)} \\ \vdots & \vdots & \ddots & \vdots \\ a_{n_L}^{[L]{}(1)} & a_{n_L}^{[L]{}(2)} & \cdots & a_{n_L}^{[L]{}(m)} \end{bmatrix}, \qquad \mathbf{Y} = \begin{bmatrix} y_1^{(1)} & y_1^{(2)} & \cdots & y_1^{(m)} \\ y_2^{(1)} & y_2^{(2)} & \cdots & y_2^{(m)} \\ \vdots & \vdots & \ddots & \vdots \\ y_{n_L}^{(1)} & y_{n_L}^{(2)} & \cdots & y_{n_L}^{(m)} \end{bmatrix} \tag{6.6} $$
Equation 6.6: Output and label matrices

where:

For binary classification with $n_L = 1$, both matrices reduce to row vectors in $\mathbb{R}^{1 \times m}$ — one row of predictions and one row of labels.

The initial gradient for the whole batch is then given by a single element-wise expression:

$$ d\mathbf{A}^{[L]} = -\frac{\mathbf{Y}}{\mathbf{A}^{[L]}} + \frac{1 - \mathbf{Y}}{1 - \mathbf{A}^{[L]}} = \begin{bmatrix} da_1^{[L]{}(1)} & da_1^{[L]{}(2)} & \cdots & da_1^{[L]{}(m)} \\ da_2^{[L]{}(1)} & da_2^{[L]{}(2)} & \cdots & da_2^{[L]{}(m)} \\ \vdots & \vdots & \ddots & \vdots \\ da_{n_L}^{[L]{}(1)} & da_{n_L}^{[L]{}(2)} & \cdots & da_{n_L}^{[L]{}(m)} \end{bmatrix}, \tag{6.7} $$
Equation 6.7: Output-layer gradient matrix

where the divisions and operations are applied element-wise, and each entry is just Equation 6.4 written per neuron and per example:

$$ da_j^{[L]{}(i)} = -\frac{y_j^{(i)}}{a_j^{[L]{}(i)}} + \frac{1 - y_j^{(i)}}{1 - a_j^{[L]{}(i)}} \tag{6.8} $$
Equation 6.8: Output-layer gradient entry

This $d\mathbf{A}^{[L]}$ is the input to the backward step of the final layer. The layer-by-layer backward equations of Section 5 are then applied recursively from layer $L$ down to layer $1$. This completes the forward–backward cycle: the network computes a prediction, the error is measured, the gradient is initialized at the output, and it is propagated backward to update every parameter.

Machine Learning Notes · appendix B of 2 · Forward and backward, entry by entry · superseded by reference Part IV ← Appendix A end of the appendices →