Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Forward Pass in Multi-Layer Networks

Introduction

In our previous lesson, we assembled the architectural blueprint of a feedforward neural network, understanding it as a composite function built from layers. Each layer performs an affine transformation followed by a non-linear activation. We established what a network is. Now, we will explore how it operates.

This lesson focuses on the forward pass, the fundamental process of taking an input and propagating it through the network's layers to produce a final prediction. This mechanism is not only how a trained model performs inference on new data but also constitutes the first of two critical stages in the training cycle. For you, with an eye towards performance and low-level engineering, understanding this data flow is paramount, as it's the core computational workload that systems like GPUs are designed to accelerate.

The Flow of Information

At its heart, the forward pass is a sequential chain of computations. The output of one layer becomes the input for the next. Let's visualize this with a simple three-layer network.

A diagram of a feedforward neural network with an input layer, a hidden layer, and an output layer. The equations show how activations are computed and passed from one layer to the next.

This diagram illustrates the entire process. Let's break down the notation, aligning it with the formalism from our last lesson where the superscript [l] denotes the -th layer:

  1. Input Layer (Layer 1): We start with the input vector, denoted as . In the diagram, this is "Layer 1".

  2. Hidden Layer (Layer 2):

    • An affine transformation is applied to the input . The diagram shows this as x . w(1) + b(1) = z(2). In our notation, this is . The matrix contains the weights connecting the input to the hidden layer, and is the bias vector for the hidden layer.
    • A non-linear activation function, , is applied element-wise to to produce the hidden layer's activations, . The diagram labels this f(Σz(2)) = a(2). The summation symbol here is slightly confusing; it's more accurate to think of this as the function being applied to each element of the vector individually.
  3. Output Layer (Layer 3):

    • The process repeats. The activations from the hidden layer, , are now the input to the output layer. The affine transformation is .
    • A final activation function is applied to get the network's prediction, .

This flow from input to output, without any loops, is why these are called feedforward networks.

The Forward Pass and the Computation Graph

In our last lesson, we introduced the concept of a computation graph. The forward pass is nothing more than evaluating this graph from the input nodes to the final output node. Each node performs a simple, local computation, and the directed edges dictate the flow of data.

This perspective is crucial for implementing complex models. Instead of a monolithic equation, we have a modular structure of operations. The following video offers a clear, step-by-step walkthrough of a forward pass on a simple computation graph.

Lecture 6: Backpropagation

This excerpt from a Michigan Online lecture on backpropagation begins by perfectly illustrating the forward pass.

First, watch the section on computational graphs to solidify the concept of representing a model's calculations as a directed graph. Then, pay close attention to the explanation of the forward pass. Notice how it's a simple left-to-right evaluation of the graph, where the output of one operation becomes the input to the next.

The video demonstrates a key principle: the forward pass populates the graph with concrete numerical values based on a specific input. The structure of the graph itself is defined by our network architecture, but the values within it are determined by the input data and the current parameters (weights and biases).

From Single Vectors to Batches: A Performance Perspective

So far, we have considered processing a single input vector . From a systems programming viewpoint, this is inefficient. The overhead of launching a computation for one small vector is high. To leverage the parallel processing power of modern hardware, especially GPUs, we process a batch of inputs simultaneously.

Instead of an input vector (where is the number of features), we work with an input matrix , where is the batch size.

This changes our layer computation from matrix-vector products to matrix-matrix products. For a layer with neurons, taking input activations :

  1. Affine Transformation:

    • is the weight matrix of size .
    • The product results in a matrix of size .
    • is the bias vector of size . Through an operation called broadcasting, this bias vector is added to every row of the preceding product matrix. This avoids an explicit loop and is highly optimized in numerical computing libraries.
  2. Non-linear Activation:

    • The activation function is applied element-wise to the entire matrix , producing the final activation matrix .

This batched approach replaces many small operations with a few large matrix operations, which is exactly what GPUs are built for. The following article details this process, including a clever technique called the "bias trick" that formally incorporates the bias into the weight matrix.

Forward Pass and Backpropagation (Backward Pass)

This article provides an excellent breakdown of a batched forward pass, using augmented matrices to handle the bias term.

Please read the sections covering the forward pass: Batch Processing: Understand the motivation for batching and the "bias trick." First Layer: Follow the dimensional analysis as the input matrix is transformed by the first layer's weights. Second Hidden Layer: See how the output of the first layer becomes the input for the second. Output Layer: Observe the final transformation to produce the network's predictions.

A Concrete Example

Let's solidify this with a numerical example. Consider a 2-layer network: a hidden layer with 2 neurons (using ReLU) and an output layer with 1 neuron (using Sigmoid).

  • Input:
  • Hidden Layer (l=1):
    • Weights: (Note: I've transposed the matrix from the thought block to match the convention here, where columns correspond to input features)
    • Bias:
  • Output Layer (l=2):
    • Weights:
    • Bias:

Step 1: Forward Pass through Hidden Layer

First, the affine transformation:

Next, apply the ReLU activation function element-wise:

The output of the hidden layer is . Notice how the ReLU activation "turned off" the first neuron.

Step 2: Forward Pass through Output Layer

The input is now . We perform the next affine transformation:

Finally, apply the Sigmoid activation to get the prediction:

The final prediction of our network for the input is approximately 0.557.

Implementation From First Principles

For someone interested in low-level engineering, seeing the code that executes this process is illuminating. The following C++ code implements the forward pass for a single feedforward node.

C++ Neural Network in a Weekend - Jeremy's Blog

This article provides a 'from-scratch' C++ implementation of a neural network. We'll focus on the forward pass method.

Read the C++ implementation of the FFNode::forward method. As you read the nested loops that compute the dot product and add the bias, consider this question: From a performance engineering perspective, what is the major bottleneck in this loop-based implementation, especially when running on a GPU? How does the batched matrix multiplication approach we discussed (\mathbf{Z} = \mathbf{A}\mathbf{W} + \mathbf{b}) address this?

Answer The primary bottleneck is the heavy reliance on sequential, fine-grained operations (individual multiplications and additions within loops). This implementation exhibits poor data locality and offers little opportunity for massive parallelism. A GPU excels at performing a single instruction on multiple data points simultaneously (SIMD). The batched matrix multiplication approach perfectly aligns with this paradigm. It coalesces the thousands of small multiply-add operations into a single, large, highly-optimized kernel (`GEMM` - General Matrix-Matrix Multiplication), which can fully saturate the GPU's computational resources. The C++ code is excellent for understanding the logic, but high-performance libraries like cuBLAS (for CUDA) or OneMKL (for CPUs) are used in practice to execute these large matrix operations.

Conclusion

In this lesson, we have demystified the forward pass, tracing the path of data from input to prediction. We've seen how a sequence of simple, local computations—affine transformations and non-linear activations—combine to produce the network's output.

Key Takeaways:

  • The forward pass is a sequential evaluation of the network's layers to compute a prediction from an input .
  • From a graph perspective, it is a left-to-right traversal of the computation graph.
  • For performance, inputs are processed in batches, transforming the computation into a series of large matrix-matrix multiplications.
  • This process is the core of inference and the first half of the training loop.

Preview of the Next Lesson:

Our network has made a prediction, . But is it a good prediction? To train the network, we must quantify its error. In the next lesson, we will introduce loss functions, which measure the discrepancy between the predicted output () and the true target (). We will then derive the gradient of the loss with respect to the network's final output, which is the crucial first step of the backward pass, or backpropagation.

Can't find a good explanation? Sign up and we'll make it for you

Sign up