TAI BUI
Back to curriculum
BuildPython

Multi-Layer Networks and Forward Pass

One neuron draws a line. Stack enough of them and you can draw anything.

TIME: ~90 minutesPREREQUISITES: Phase 01 (Math Foundations), Lesson 03.01 (The Perceptron)

Learning Objectives

  • Build a multi-layer network from scratch with Layer and Network classes that perform a complete forward pass
  • Trace matrix dimensions through each layer of a network and identify shape mismatches
  • Explain how stacking nonlinear activations enables a network to learn curved decision boundaries
  • Solve the XOR problem using a 2-2-1 architecture with hand-tuned sigmoid weights

The Problem

A single neuron is a line drawer. That's it: one straight line through your data. Every real problem in AI (image recognition, language understanding, playing Go) needs curves. Stacking neurons into layers is how you get them.

In 1969, Minsky and Papert proved this limitation was fatal: a single-layer network cannot learn XOR. Not "struggles to learn" — mathematically cannot. The XOR truth table puts [0,1] and [1,0] on one side, [0,0] and [1,1] on the other, and no single line separates them.

That result killed neural network funding for over a decade. The fix was obvious in hindsight: stop using one layer. Stack neurons into layers instead, so the first layer carves the input space into new features and the second layer combines those features into decisions no single line could make.

That stack is the multi-layer network, the foundation of every deep learning model in production today. The forward pass (data flowing from input through hidden layers to output) is the first thing you need to build before anything else works.

Warm-up0/3
1. A single perceptron can only draw one straight decision boundary. If red points sit inside a circle and blue points sit outside it, can one straight line ever separate them perfectly?Warm-up
A square plot of 70 points: red points inside a dashed circle at the centre, blue points scattered outside it, with a legend for red, blue and the circle edge.
Points are illustrative.
2. If you pipe the output of one function into a second function, and that second function's output into a third, what happens to the original input overall?Warm-up
A chain: an input of 3 goes into function 1 (add 1), whose result goes into function 2 (double it), whose result goes into function 3 (subtract 5), ending in a box with a question mark.
Example functions are illustrative.
3. A matrix has 3 rows and 2 columns. You multiply it by a vector with 2 entries. How many entries does the result have?Warm-up
A matrix with 3 rows and 2 columns (2 1 / 0 3 / 1 4) multiplied by a vector with entries 1 and 2, equal to a dashed box with a question mark asking how many entries the result has.
Entries are illustrative.
Check: The Problem0/2
1. What is a decision boundary?Define
The input plane from -0.5 to 1.5 on both axes shaded by what the lesson's XOR network outputs: a diagonal band between two dashed lines is shaded as 1, the rest as 0, with the four XOR points marked in red (target 1) and blue (target 0).
Network output computed with the lesson's Layer and Network classes; lines drawn at x1 + x2 = 0.5 and 1.5.
2. Why can a network with a second layer solve XOR when a single neuron cannot?Explain
A square plot of the four XOR input points (0,0), (0,1), (1,0), (1,1): (0,1) and (1,0) in red for target 1, (0,0) and (1,1) in blue for target 0.

The Concept

Layers: Input, Hidden, Output

A multi-layer network has three types of layers:

Input layer: not really a layer at all, just your raw data. Two features means two input nodes, and no computation happens here.

Hidden layers: where the work happens. Each neuron takes every output from the previous layer, applies weights and a bias, then passes the result through an activation function. It's called "hidden" because you never see these values directly in the training data.

Output layer: the final answer. One neuron with sigmoid for binary classification, one neuron per class for multi-class.

Mermaid diagram

This is a 2-3-1 network: two inputs, three hidden neurons, one output. Every connection carries a weight, and every neuron except the input carries a bias.

Each layer produces a vector of numbers called a hidden state. For text, hidden states increase dimensionality, encoding a word as 768 numbers to capture semantic meaning. For images, they go the other way, reducing millions of pixels down to a manageable representation. The hidden state is where the learning lives.

Neurons and Activations

Each neuron does three things:

  1. Multiply every input by its corresponding weight
  2. Sum all the products and add a bias
  3. Pass the sum through an activation function

For now, the activation is sigmoid:

sigmoid(z) = 1 / (1 + e^(-z))

Sigmoid squashes any number into the range (0, 1). Large positive inputs push toward 1. Large negative inputs push toward 0. Zero maps to 0.5. This smooth curve is what makes learning possible: unlike the perceptron's hard step, sigmoid has a gradient everywhere.

Forward Pass: How Data Flows

The forward pass pushes input data through the network, layer by layer, until it reaches the output. No learning happens during the forward pass. It is pure computation: multiply, add, activate, repeat.

Mermaid diagram

At each layer, three operations happen in sequence:

z = W * input + b       (linear transformation)
a = sigmoid(z)           (activation)

The output of one layer becomes the input to the next. That is the entire forward pass.

Matrix Dimensions

Tracking dimensions is the single most important debugging skill in deep learning. Here is the 2-3-1 network:

StepOperationDimensionsResult Shape
Inputx--(2,)
Hidden linearW1 * x + b1W1: (3, 2), b1: (3,)(3,)
Hidden activationsigmoid(z1)--(3,)
Output linearW2 * h + b2W2: (1, 3), b2: (1,)(1,)
Output activationsigmoid(z2)--(1,)

The rule: weight matrix W at layer k has shape (neurons_in_layer_k, neurons_in_layer_k_minus_1). Rows match the current layer. Columns match the previous layer. If the shapes do not line up, you have a bug.

Universal Approximation Theorem

In 1989, George Cybenko proved something remarkable: a neural network with a single hidden layer and enough neurons can approximate any continuous function to any desired accuracy.

This does not mean one hidden layer is always best. It means the architecture is theoretically capable. In practice, deeper networks (more layers, fewer neurons per layer) learn the same functions with far fewer total parameters than shallow-wide networks. That is why deep learning works.

The intuition: each neuron in the hidden layer learns one "bump" or feature. Enough bumps placed in the right locations can approximate any smooth curve. More neurons, more bumps, better approximation.

Mermaid diagram

Composability

Neural networks are composable. You can stack them, chain them, run them in parallel. An encoder network compresses an input down into a representation; a decoder network expands a representation back out into an output. A Whisper model uses an encoder to process audio and a separate decoder to generate text. Modern LLMs are decoder-only. BERT is encoder-only. T5 is encoder-decoder. The architecture choice defines what the model can do.

Check: The Concept0/19
1. What is a forward pass?Define
The 2-2-1 XOR network with weights on every connection and biases beside the neurons, run on input [1, 0]: hidden neurons show 0.99995 and 0.99995, output 0.99995.
Values from the lesson's Layer and Network classes.
2. What makes a layer "hidden"?Define
A fully connected network with 2 nodes in layer 1, 3 neurons in layer 2 and 1 neuron in layer 3, next to a training-data table with columns x1, x2 and y holding the four XOR rows.
Layer sizes are the lesson's 2-3-1 example.
3. Which description matches a multi-layer network?Define
The lesson's 2-2-1 network: two input nodes x1 and x2, two hidden neurons with biases -10 and 30, and one output neuron with bias -30, with weights 20 or -20 on every connection.
4. What is the role of an activation function in a layer?Define
Hidden neuron h1 of the XOR network on input [0, 1]: weights 20 and 20 with bias -10 give 10, and sigmoid(10) gives 0.99995, followed by a table of the same quantities for all four XOR inputs.
Values computed with the lesson's sigmoid.
5. What does sigmoid(z) = 1 / (1 + e^(-z)) do to its input?Define
A plot of sigmoid(z) for z from -6 to 6 with five marked points, and a table of z = -4, -2, 0, 2, 4 against sigmoid(z) = 0.018, 0.119, 0.500, 0.881, 0.982.
Computed with the lesson's sigmoid.
6. What does a layer's weight matrix contain?Define
A layer with inputs x1 and x2 fully connected to neurons h1, h2 and h3, next to the 3 by 2 grid of numbers its Layer object stores as weights.
Weights drawn by the lesson's Layer class with random.seed(1).
7. What is the bias vector for?Define
The XOR network's hidden layer with weight matrix [[20, 20], [-20, -20]] times the input vector [0, 0] plus the bias vector [-10, 30], equal to a dashed box z = ?.
8. What is the linear transformation step in a layer?Define
Code excerpts from the lesson: the sigmoid function with its clamp to [-500, 500], and the forward method of Layer, which sums weighted inputs, adds the bias and appends sigmoid of the result.
9. What does the Universal Approximation Theorem guarantee?Define
A plot of the target curve y = sin(x) on 0 to 2 pi, next to a dashed box for a network with an unknown number of hidden sigmoid neurons and an unknown output.
Target curve only; the network is not shown.
10. Why does a layer's weight matrix have rows matching the current layer and columns matching the previous layer?Explain
A previous layer with three outputs a1, a2, a3 fully connected to one current neuron n1, beside a dashed box reading W: shape (?, ?).
11. What happens to the weights during a forward pass?Explain
A Python session printing the XOR network's weights [[20, 20], [-20, -20]] and [[20, 20]] and biases [-10, 30] and [-30], then running xor_net.forward([0, 1]) which returns 0.9999545 (about 0.99995), then a line reading weights now: ?.
Output of the lesson's Layer and Network classes.
12. Why does the universal approximation theorem not mean one very wide hidden layer is the best design?Explain
A 2-N-1 network with one hidden layer of N neurons next to a table: N = 3 has 13 parameters, N = 8 has 33, N = 100 has 401 and N = 1,000 has 4,001.
Counted with the lesson's count_parameters method.
13. Why does the sigmoid activation function make learning possible, unlike the step function used in perceptrons?Explain
Two curves of output against z from -6 to 6: a blue step function jumping from 0 to 1 at z = 0, and an orange smooth sigmoid curve.
14. How does hidden-state dimensionality typically change across layers for text versus images, per the lesson?Explain
Two rows. Text: the word cat goes through layer 1, layer 2 and layer 3. Images: a small pixel grid standing for a photo goes through layer 1, layer 2 and layer 3. Each layer's hidden state is a vector with an unknown number of entries.
Inputs are illustrative; the number of entries in each hidden state is what the question asks about.
15. The lesson notes that neural networks are composable across named architectures. Which pairing correctly matches an architecture with its structure?Explain
Three layouts: encoder only (input, encoder, representation), decoder only (input, decoder, output) and encoder plus decoder (input, encoder, decoder, output), with one-line roles of encoder and decoder, and four model names to place: BERT, T5, Whisper, modern LLMs.
Role wording is from the lesson's Composability section; no model is assigned to a layout.
16. For a layer with 3 neurons receiving input from 2 neurons, what is the shape of the weight matrix?Apply
A layer of three neurons n1, n2, n3 each connected to both of two input neurons i1 and i2, beside a dashed box reading weight matrix shape: ?.
17. A 2-3-1 network's second layer is built with a weight matrix of shape (3, 1), and the forward pass misbehaves. What is the bug?Analyze
The lesson's dimension table for a 2-3-1 network with W2 built as (3, 1): input (2,), hidden linear W1 (3, 2) with b1 (3,) giving (3,), hidden activation (3,), and output linear W2 (3, 1) with b2 (1,) giving a question mark; a 2-3-1 network drawing below.
Layout follows the lesson's dimension table.
18. You need a network for a complicated function but have a tight parameter budget. Which design fits the lesson's guidance?Design
A task card (approximate a complicated function with a tight parameter budget), the rule that a layer's parameters are weights plus biases, and a table: 2-3-1 has 13, 2-8-1 has 33, 784-256-128-10 has 235,146 and 784-512-256-128-10 has 567,434 parameters.
Counts from the lesson's count_parameters method.
19. You are building a classifier with five classes. How should the output layer be designed?Design
A network with 2 inputs and 8 hidden neurons feeding a dashed output layer marked ? neurons, next to a list of the five classes, A to E.
Class names and hidden width are illustrative.

Build It

Pure Python. No numpy. Every matrix operation written from scratch.

Step 1: Sigmoid Activation

Sigmoid needs a safety valve before anything else: for a large enough input, math.exp alone overflows. Clamp the input first, so the function never blows up.

python
import math
 
def sigmoid(x):
    x = max(-500.0, min(500.0, x))
    return 1.0 / (1.0 + math.exp(-x))

The clamp to [-500, 500] prevents that overflow: math.exp(500) is large but finite, while math.exp(1000) is infinity.

Step 2: Layer Class

The most important operation in all of deep learning is matrix multiplication. Every layer, every attention head, every forward pass — it's matmuls all the way down. A linear layer takes an input vector, multiplies it by a weight matrix, and adds a bias vector: y = Wx + b. That single equation is 90% of the compute in a neural network.

A layer holds a weight matrix and a bias vector. Its forward method takes an input vector and returns the activated output.

python
class Layer:
    def __init__(self, n_inputs, n_neurons, weights=None, biases=None):
        if weights is not None:
            self.weights = weights
        else:
            import random
            self.weights = [
                [random.uniform(-1, 1) for _ in range(n_inputs)]
                for _ in range(n_neurons)
            ]
        if biases is not None:
            self.biases = biases
        else:
            self.biases = [0.0] * n_neurons
 
    def forward(self, inputs):
        self.last_input = inputs
        self.last_output = []
        for neuron_idx in range(len(self.weights)):
            z = sum(
                w * x for w, x in zip(self.weights[neuron_idx], inputs)
            )
            z += self.biases[neuron_idx]
            self.last_output.append(sigmoid(z))
        return self.last_output

The weight matrix has shape (n_neurons, n_inputs). Each row is one neuron's weights across all inputs. The forward method loops through neurons, computes the weighted sum plus bias, applies sigmoid, and collects the results.

Step 3: Network Class

A network is a list of layers. The forward pass chains them: output of layer k feeds into layer k+1.

python
class Network:
    def __init__(self, layers):
        self.layers = layers
 
    def forward(self, inputs):
        current = inputs
        for layer in self.layers:
            current = layer.forward(current)
        return current

That is the entire forward pass. Four lines of logic. Data goes in, flows through every layer, comes out the other side.

Step 4: XOR with Hand-Tuned Weights

In Lesson 01, we solved XOR by combining OR, NAND, and AND perceptrons. Now do the same thing with our Layer and Network classes. The 2-2-1 architecture: two inputs, two hidden neurons, one output.

python
hidden = Layer(
    n_inputs=2,
    n_neurons=2,
    weights=[[20.0, 20.0], [-20.0, -20.0]],
    biases=[-10.0, 30.0],
)
 
output = Layer(
    n_inputs=2,
    n_neurons=1,
    weights=[[20.0, 20.0]],
    biases=[-30.0],
)
 
xor_net = Network([hidden, output])
 
xor_data = [
    ([0, 0], 0),
    ([0, 1], 1),
    ([1, 0], 1),
    ([1, 1], 0),
]
 
for inputs, expected in xor_data:
    result = xor_net.forward(inputs)
    predicted = 1 if result[0] >= 0.5 else 0
    print(f"  {inputs} -> {result[0]:.6f} (rounded: {predicted}, expected: {expected})")

The large weights (20, -20) make sigmoid act like a step function. The first hidden neuron approximates OR. The second approximates NAND. The output neuron combines them into AND, which is XOR.

Step 5: Circle Classification

A harder problem: classify 2D points as inside or outside a circle of radius 0.5 centered at the origin. This requires a curved decision boundary, something no single perceptron can draw.

python
import random
import math
 
random.seed(42)
 
data = []
for _ in range(200):
    x = random.uniform(-1, 1)
    y = random.uniform(-1, 1)
    label = 1 if (x * x + y * y) < 0.25 else 0
    data.append(([x, y], label))
 
circle_net = Network([
    Layer(n_inputs=2, n_neurons=8),
    Layer(n_inputs=8, n_neurons=1),
])

With random weights, the network will not classify well, but the forward pass still runs regardless. That's the point — the forward pass is just computation. Learning the right weights is backpropagation, coming in Lesson 03.

python
correct = 0
for inputs, expected in data:
    result = circle_net.forward(inputs)
    predicted = 1 if result[0] >= 0.5 else 0
    if predicted == expected:
        correct += 1
 
print(f"Accuracy with random weights: {correct}/{len(data)} ({100*correct/len(data):.1f}%)")

Random weights give poor accuracy, often worse than guessing the majority class. After training (Lesson 03), this same architecture with 8 hidden neurons will draw a curved boundary that separates inside from outside.

Check: Build It0/9
1. In the Build It sigmoid implementation, why does the code clamp x to the range [-500, 500] before computing math.exp(-x)?Explain
The lesson's sigmoid function with its clamp line to [-500, 500], and a Python session calling it: sigmoid(0) is 0.5, sigmoid(-1000) is a tiny positive number near 7e-218, and sigmoid(1000) is 1.0.
Output of the lesson's sigmoid.
2. In Step 4's hand-tuned XOR network, why are the hidden layer weights set to large values like 20.0 and -20.0 instead of small ones?Explain
The hand-tuned XOR network: weights 20 or -20 on the hidden connections, hidden biases -10 and 30, output weights 20 and 20 with bias -30, above the XOR truth table.
3. In the Step 4 XOR network, roughly what are the two hidden neuron outputs for the input [0, 0]?Apply
The 2-2-1 XOR network with weights and biases on it and input [0, 0] entered; the hidden neurons h1, h2 and the output neuron show question marks.
4. What does the Step 4 XOR network predict for the input [1, 1]?Apply
The 2-2-1 XOR network with weights and biases on it and input [1, 1] entered; the hidden neurons h1, h2 and the output neuron show question marks.
5. In circle_net = Network([Layer(n_inputs=2, n_neurons=8), Layer(n_inputs=8, n_neurons=1)]), what is the shape of the second layer's weights?Apply
The lesson's circle_net code with two Layer objects, and a drawing of the 2-8-1 network it builds: 2 inputs, 8 hidden neurons, 1 output neuron, beside a dashed box for the second layer's weights marked with a question mark.
6. In the Step 5 data, which label does the point (0.3, 0.3) receive?Apply
The lesson's Step 5 data-generation loop with the line label = 1 if (x * x + y * y) < 0.25 else 0, and a dashed box for the data point ([0.3, 0.3], ?).
7. A learner scales every XOR weight and bias down by a factor of 40. Now the four outputs barely differ and none is near 0 or 1. What is the best explanation?Analyze
Two tables: the XOR network's parameters before and after dividing by 40 (weights 0.5 and -0.5, biases -0.25 and 0.75, output weights 0.5 and bias -0.75), and the four outputs before and after, where the after values are all close to 0.5.
Outputs computed with the lesson's Layer and Network classes.
8. In the Step 5 circle classification example, why does the network with 8 hidden neurons and random weights fail to classify points accurately?Analyze
The input plane from -1 to 1 shaded by what the lesson's 8-hidden-neuron circle network predicts with its random starting weights, with the true circle of radius 0.5 drawn dashed, and the measured accuracy 35/200 (17.5%).
Predictions from the lesson's Layer and Network classes, seeds 42 and 7.
9. A sigmoid neuron's weights grow very large. Its output is pinned near 0 or 1 and its weights barely change anymore. Which explanation fits?Analyze
A table of one sigmoid neuron with input x = 1 and bias 0 as its weight grows through 1, 5, 20 and 50: z equals the weight and the output rises from 0.731058579 to 0.993307149 to 0.999999998 to 1.000000000.

Use It

PyTorch does everything above in four lines:

python
import torch
import torch.nn as nn
 
model = nn.Sequential(
    nn.Linear(2, 8),
    nn.Sigmoid(),
    nn.Linear(8, 1),
    nn.Sigmoid(),
)
 
x = torch.tensor([[0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0]])
output = model(x)
print(output)

nn.Linear(2, 8) is your Layer class: weight matrix of shape (8, 2), bias vector of shape (8,). nn.Sigmoid() is your sigmoid function applied element-wise. nn.Sequential is your Network class: chain layers in order.

The difference is speed and scale. PyTorch runs on GPUs, handles batches of millions of samples, and automatically computes gradients for backpropagation. But the forward pass logic is identical to what you just built from scratch.

Check: Use It0/2
1. In the Use It section, what do nn.Linear and nn.Sequential correspond to in the from-scratch code built earlier in the lesson?Explain
Two code cards side by side: the lesson's from-scratch circle_net built from Network and two Layer objects (2 inputs to 8 neurons, 8 inputs to 1 neuron), and the PyTorch nn.Sequential with nn.Linear(2, 8), nn.Sigmoid(), nn.Linear(8, 1), nn.Sigmoid().
Both snippets are from the lesson.
2. Which nn.Sequential reproduces a 2-4-1 network with sigmoid after each layer?Design
A 2-4-1 network: two inputs, a hidden layer of four neurons followed by sigmoid, and one output neuron followed by sigmoid.

Ship It

This lesson produces a reusable prompt for designing network architectures:

  • outputs/prompt-network-architect.md

Use it when you need to decide how many layers, how many neurons per layer, and which activation functions to use for a given problem.

Author's Note

The hand-tuned XOR weights in Step 4 feel like a magic trick: weights of 20 and -20 appearing out of nowhere to make sigmoid snap like a step function. That's deliberate. Large weights sharpen a decision, which is exactly why a well-trained classifier's confident predictions cluster near 0 and 1 instead of hovering at 0.5, and gradient descent finds weights like these on its own once you hand it a loss function.

But this trick also previews a real failure mode. A sigmoid neuron whose weights grow too large saturates: its gradient collapses toward zero and it stops learning. You'll meet that problem directly in Lesson 07 (regularization) and Lesson 08 (weight initialization). This lesson's hand-tuned 20s are the friendly version of the same thing.

Exercises

  1. Build a 2-4-2-1 network (two hidden layers) and run the forward pass on XOR data with random weights. Print the intermediate hidden layer outputs to see how the representation transforms at each layer.

  2. Change the hidden layer size in the circle classifier from 8 to 2, then to 32. Run the forward pass with random weights each time. Does the number of hidden neurons change the output range or distribution? Why?

  3. Implement a count_parameters method on the Network class that returns the total number of trainable weights and biases. Test it on a 784-256-128-10 network (the classic MNIST architecture). How many parameters does it have?

  4. Build a forward pass for a 3-4-4-2 network. Feed it RGB color values (normalized to 0-1) and observe the two outputs. This is the architecture for a simple color classifier with two classes.

  5. Replace sigmoid with a "leaky step" function: return 0.01 * z if z < 0, else 1.0. Run the forward pass on XOR with the same hand-tuned weights from Step 4. Does it still work? Why is the smooth sigmoid preferred over hard cutoffs?

Key Terms

TermWhat people sayWhat it actually means
Forward pass"Running the model"Pushing input through every layer (multiply by weights, add bias, activate) to produce an output
Hidden layer"The middle part"Any layer between input and output whose values are not directly observed in the data
Multi-layer network"A deep neural network"Layers of neurons stacked sequentially, where each layer's output feeds the next layer's input
Activation function"The nonlinearity"A function applied after the linear transformation that introduces curves into the decision boundary
Sigmoid"The S-curve"sigma(z) = 1/(1+e^(-z)), squashes any real number to (0,1), smooth and differentiable everywhere
Weight matrix"The parameters"A matrix W of shape (current_layer_neurons, previous_layer_neurons) containing learnable connection strengths
Bias vector"The offset"A vector added after the matrix multiply that lets neurons activate even when all inputs are zero
Universal approximation"Neural nets can learn anything"A single hidden layer with enough neurons can approximate any continuous function, but "enough" can mean billions
Linear transformation"The matrix multiply step"z = W * x + b, the computation before activation, which maps inputs to a new space
Decision boundary"Where the classifier switches"The surface in input space where the network output crosses the classification threshold

Further Reading

  • Michael Nielsen, "Neural Networks and Deep Learning", Chapter 1-2 (http://neuralnetworksanddeeplearning.com/): the clearest free explanation of forward passes and network structure, with interactive visualizations
  • Cybenko, "Approximation by Superpositions of a Sigmoidal Function" (1989): the original universal approximation theorem paper, surprisingly readable
  • 3Blue1Brown, "But what is a neural network?" (https://www.youtube.com/watch?v=aircAruvnKk): a 20-minute visual walkthrough of layers, weights, and forward passes that builds the right mental model
  • Goodfellow, Bengio, Courville, "Deep Learning", Chapter 6 (https://www.deeplearningbook.org/): the standard reference for multi-layer networks, free online

Code

Ships With