Artificial Neural Networks
By the end: For a small fully connected network you write the shape of every weight, bias and activation for a given batch size, compute the parameter count by formula, and trace one example through the forward pass by hand.
At a glance
A network is logistic regression repeated side by side and stacked, with a bend between layers; shapes follow the widths, never the batch.
One unit
A weighted sum plus a bias, then a bend: logistic regression is one unit.
One layer
H = ReLU(XW + b) for the whole batch at once; W is (inputs, units).
Shapes
Batch first, width second. Only activations carry the batch dimension.
Parameter count
Sum of inputs x outputs + outputs per layer; the 2-3-1 network has 13.
No bend no depth
Without an activation, stacked layers collapse into one linear map.
Recall
Part II starts with a five-minute neural-prerequisite diagnostic: matrix shapes, dot products, derivatives and the chain rule, logistic regression as a one-layer network, and overfitting. Answer without a framework and rate your confidence for each item. It is not a grade: it feeds your evidence profile. If the shape, dot-product or derivative items go wrong, work the worked example in this lesson's deep dive before the application block, and the derivative primer on Day 32 before Day 33.
Concept
You already know one Neuron (unit): logistic regression is a weighted sum plus a bias, . A network puts many of them side by side (a layer) and stacks layers, with a bend such as ReLU, , between them. Without the bend the stack is still one linear model. The brain analogy is historical; treat a unit as a small regression.
Predict first
A 2-3-1 network gets a batch of 64 instead of 4. How many parameters does it have now?
Text description of the figure
A table that traces a small fully connected network layer by layer: the input, weight, bias and output shape and the parameter count of each dense layer, with a bar chart of parameters per layer. Changing the batch size changes only the activation shapes; changing a layer width changes the weights on both sides of that layer and the running parameter count.
Neuron
Weighted sum of the inputs plus a bias: z = w . x + b.
Layer
Many neurons side by side share the same input: XW + b.
Activation
A bend such as ReLU lets units switch on for some inputs only.
Stack
The output of one layer is the input of the next.
- Input X(4, 2): 4 rows, 2 features
- Z1 = X W1 + b1(4, 3) pre-activations
- to H = ReLU(Z1)
- W1 and b1(2, 3) and (3,): 9 learned
- every row: back to Z1 = X W1 + b1
- H = ReLU(Z1)(4, 3) same shape, negatives to 0
- Score = H W2 + b2(4, 1) one raw score per row
- W2 and b2(3, 1) and (1,): 4 learned
- every row: back to Score = H W2 + b2
Top row carries data (it grows with the batch). Bottom row holds the 13 parameters (they never do).
| Array | Batch of 4 | Batch of 64 | Learned numbers |
|---|---|---|---|
| X (input) | (4, 2) | (64, 2) | 0 (data) |
| W1, b1 | (2, 3), (3,) | (2, 3), (3,) | 9 |
| Z1 and H | (4, 3) | (64, 3) | 0 (activations) |
| W2, b2 | (3, 1), (1,) | (3, 1), (1,) | 4 |
| Score | (4, 1) | (64, 1) | 0 |
| Total parameters | 13 | 13 | 13 |
Activation memory grows with the batch; model size does not.
Unit 1
1 x 1 + 2 x 0.5 + 0 = 2.0
Unit 2
1 x (-1) + 2 x 1 + 0.5 = 1.5
Unit 3
1 x 0.5 + 2 x (-1) + 0.1 = -1.4
ReLU
(2.0, 1.5, -1.4) becomes (2.0, 1.5, 0): unit 3 is off for this row
Output
2.0 x 1 + 1.5 x (-1) + 0 x 2 - 0.5 = 0.0
Each hidden value uses one column of W1. The matrix product does this for every row at once.
| Criterion | No activation | ReLU between layers |
|---|---|---|
| What it computes | (X W1 + b1) W2 + b2 | ReLU(X W1 + b1) W2 + b2 |
| Decision surface | One flat hyperplane | Piecewise linear: bends where a unit switches on or off |
| Equivalent to | One linear layer: W = W1 W2 = (3, -2.5), b = -0.8 | Nothing simpler; each unit adds a possible bend |
| Parameters | 13, but only 3 effective numbers | 13, all of them shaping the function |
Composing affine maps gives an affine map. Depth adds expressive power only through the nonlinearity.
Validation log loss 0.386, accuracy 76%
Validation log loss 0.317, accuracy 82%
Validation log loss 0.261, accuracy 92%
- decision boundary (probability 0.5)
- dashed: a hidden unit's fold line, where it switches on or off
Computed: seeded two-moons data (144 training, 96 validation points), networks trained with Adam (rate 0.03, 200 epochs, batches of 16) by the same code as the Day 35 lab. The boundary can only bend on a dashed fold line.
Same data, same metric. Without a hidden layer the boundary is one straight line; with ReLU units it is built from straight pieces joined on the dashed fold lines.
- A Dense (fully connected) layer computes for a whole batch: is , is , is .
- The Batch dimension stays first and unchanged; the bias reaches every row by Broadcasting.
- Parameters per layer: . The batch size never enters.
- Key terms: Tensor, Forward pass. The pictures trace the 2-3-1 network; Day 32 turns its raw score into a probability and Day 33 trains its 13 numbers.
Assumptions
Rows are examples and columns are features (
X @ W, as in numpy and PyTorch'snn.Linear); texts that write transpose every shape.Every unit sees every input; convolutional and recurrent layers (Days 36-37) share weights and count differently.
Parameter count measures size, not useful capacity; activation, data and training decide what is learned.
Application and workplace decision
Before running, write on paper:
- the shapes of
Z1,Handoutfor a batch of 4, and the parameter count; - row 0 by hand, : hidden pre-activation is .
Then run the trace. Its last two lines test two claims: the count does not depend on the batch, and two layers without ReLU equal one linear layer.
Python
import numpy as np
# 2-3-1 network: 2 inputs -> 3 hidden ReLU units -> 1 output score (weights chosen by hand)
W1 = np.array([[1.0, -1.0, 0.5],
[0.5, 1.0, -1.0]]) # (inputs, hidden) = (2, 3)
b1 = np.array([0.0, 0.5, 0.1]) # (3,): one bias per hidden unit
W2 = np.array([[1.0], [-1.0], [2.0]]) # (hidden, outputs) = (3, 1)
b2 = np.array([-0.5]) # (1,)
def forward(X):
Z1 = X @ W1 + b1 # (batch, 2) @ (2, 3) + (3,) -> (batch, 3)
H = np.maximum(0, Z1) # ReLU keeps the shape
return Z1, H, H @ W2 + b2 # (batch, 3) @ (3, 1) + (1,) -> (batch, 1)
X = np.array([[1.0, 2.0], [0.0, 1.0], [2.0, -1.0], [-1.0, 0.5]]) # batch of 4 bookings, 2 scaled features
Z1, H, out = forward(X)
for name, a in [("X", X), ("Z1", Z1), ("H", H), ("out", out)]:
print(f"{name:>3} shape {a.shape}")
print("row 0 by hand: Z1 =", Z1[0], "H =", H[0], "score =", out[0, 0])
n_formula = 2 * 3 + 3 + 3 * 1 + 1
n_arrays = sum(p.size for p in (W1, b1, W2, b2))
print(f"parameters: formula {n_formula}, summed array sizes {n_arrays}")
X64 = np.random.default_rng(0).normal(size=(64, 2))
print("batch of 64 -> output shape", forward(X64)[2].shape, "| parameters still", n_arrays)
# Without the ReLU, two layers collapse into one linear layer: W = W1 @ W2, b = b1 @ W2 + b2
lin_two = (X64 @ W1 + b1) @ W2 + b2
lin_one = X64 @ (W1 @ W2) + (b1 @ W2 + b2)
print("no-ReLU network equals a single linear layer:", np.allclose(lin_two, lin_one))
Mini-assessment
Technical depth
A dense layer, symbol by symbolThe batch matrix product, the shape rule, the parameter formula derived by counting, and the network as one formula with its cost.
- inputs, one row per example: (B, n_in)
- weights, one column per unit: (n_in, n_out)
- one bias per unit, broadcast to every row: (n_out,)
- batch size; it appears only in X and H
- weights connecting layer l-1 to layer l
- one bias per unit of layer l
Forgetting the bias is the most common counting slip.
One unit
A unit reading n_in inputs needs n_in weights and 1 bias: n_in + 1 numbers
One layer
n_out such units side by side: n_out x (n_in + 1) = n_in n_out + n_out, the entries of W plus b
The stack
Each layer's n_in is the previous layer's width, so the total is the sum over layers
The 2-3-1 network
Hidden: 3 x (2 + 1) = 9. Output: 1 x (3 + 1) = 4. Total 13, matching the summed array sizes in the code
Why no batch
No step mentioned the batch size: parameters are properties of the architecture, activations of the data
The formula is nothing but counting the entries of every W and b once.
- first affine map, (B, 2) to (B, 3): one column of W1 per hidden unit
- applied entry by entry; it has no parameters and costs almost nothing next to the products
- second affine map, (B, 3) to (B, 1): the raw score Day 32 turns into a probability
- one multiply-add per weight per row: 2 x 3 + 3 x 1 = 9 per row, 36 for the batch of 4
- the batch: cost grows linearly with it, while the parameter count does not move
The 50-512-512-1 proposal needs 288,256 multiply-adds per row (about 0.58 million floating-point operations). Training adds a backward pass of roughly twice that: one product for each input gradient and one for each weight gradient (Day 33).
Worked example: trace row 2 yourselfUse this if the Day 31 diagnostic flagged shapes or dot products.
Unit 1
2 x 1 + (-1) x 0.5 + 0 = 1.5
Unit 2
2 x (-1) + (-1) x 1 + 0.5 = -2.5
Unit 3
2 x 0.5 + (-1) x (-1) + 0.1 = 2.1
ReLU
(1.5, -2.5, 2.1) becomes (1.5, 0, 2.1): unit 2 is off
Output
1.5 x 1 + 0 x (-1) + 2.1 x 2 - 0.5 = 5.2; check it against out[2]
Why the bend creates powerEach ReLU unit adds one possible bend; summed hinges build curves, and in two dimensions fold lines build boundaries.
- Sum of three ReLU units
- Linear layer (a straight line)
Output = ReLU(x) - 2 ReLU(x - 1) + ReLU(x - 2), exact: zero up to 0, a ramp to a peak of 1 at x = 1, back to zero at 2, flat after. Each unit adds one bend: with one input, D ReLU units give at most D + 1 linear pieces. The flat line is the constant 0.25 (the mean of the tent on this range).
Its only bend sits at the left edge: a straight line, largest gap 1.08
3 bends; largest gap to the target 0.27
6 bends; largest gap to the target 0.14
12 bends; largest gap to the target 0.03
- target curve (dashed)
- network output = sum of the hinges
- each unit's weighted hinge v · ReLU(x − k)
Computed: target sin(1.3x) + 0.3x on 121 points; the bends are placed evenly and the output weights are fitted by least squares (a trained network would also move the bends). Output = b + Σ v·ReLU(x − k).
Each unit contributes v times ReLU(x minus k), a hinge at k; the output layer adds them. More units, more bends, a closer fit.
- Two ReLU hinges
- Best straight line
Illustrative curves: they show the pattern, not measured results.
Hypothetical grid numbers. Load rises for heating below a comfort band and for cooling above it: one ReLU unit per side gives exactly this shape. The least-squares straight line in temperature is almost flat and misses both arms (RMSE about 12.5 index points). The curve is 40 + 2 ReLU(15 - T) + 3 ReLU(T - 22).
Pieces on the input range [0, 1], counted numerically. Shallow: D units cost 3D + 1 parameters, at most D + 1 pieces. Deep (one construction): each 2-unit layer turns its input u into the tent 2 ReLU(u) - 4 ReLU(u - 0.5), doubling the pieces; K layers cost 6K + 1. Stacking folds the input again and again (Prince, Understanding Deep Learning, ch. 4).
Universal approximation
One wide ReLU layer can approximate any continuous function on a bounded region.
Existence is not learning
The result says suitable weights exist, not that training finds them.
Generalization
Whether the fit holds on new rows is a validation question (Day 35).
Depth in practice
Deeper networks often reach the same accuracy with far fewer units than one wide layer.
Symmetries, initialization and what a unit meansadvancedMany weight settings compute the same function, which is why zero initialization fails and single weights are not features.
Permutation symmetry
Swap two hidden units with their weights in and out: same function. 3 units give 3! = 6 identical copies.
Scaling symmetry
Multiply a ReLU unit's inputs by c > 0 and divide its output weight by c: same function.
Why not start at zero
Equal starting weights give every hidden unit the same gradient, so they stay copies forever.
Units are not features
Because of these symmetries, one unit's weights mean nothing on their own; read behaviour, not single weights.
Random start breaks the tie
Random weights make units differ; the scale is set from the activation on Day 34 (Glorot, He).
Two seeds
Two runs can reach the same validation loss with completely different hidden weights.
Both symmetries were checked numerically on the 2-3-1 weights (np.allclose on 64 random rows); the zero-start case gave identical gradient columns.
Counting a real proposal, and its memoryWhere 289,281 parameters come from, what training memory adds on top, and why activations often dominate.
- Layer 1: 50 x 512 + 51226,112
- Layer 2: 512 x 512 + 51291% of the total+262,656
- Output: 512 x 1 + 1+513
- Total parameters289,281
Total 289,281 parameters for 8,000 loan rows, about 36 per row. Hidden-to-hidden layers grow with width squared.
- Weights: 4 bytes x 289,2811.157 MB
- Gradients: one per weight+1.157 MB
- Adam: two moments per weightfirst and second moment (Day 33)+2.314 MB
- Parameter-related memory4.628 MB
16 bytes per parameter, about 4.6 MB: tiny here. Activations are not in this bridge: they grow with batch size times layer widths and often dominate. The same arithmetic decides whether a large model fits on one accelerator (Days 40 and 44).
- ParametersFixed by the widths; the 13 or 289,281 numbers themselves.
- GradientsOne per trainable parameter (Day 33).
- Optimizer stateAdam keeps two extra numbers per parameter (Day 33).
- ActivationsEvery layer's output for every row, kept for the backward pass: grows with batch and width.
For large models and batches, activations often dominate. Days 40 and 44 put numbers on it.
Defaults you inherit from a libraryscikit-learn's MLPClassifier, checked against the installed version, and the settings that surprise people.
| Setting | Default | What it means |
|---|---|---|
| hidden_layer_sizes | (100,) | One hidden layer of 100 units |
| activation, solver | relu, adam | ReLU hidden units, Adam with learning_rate_init 0.001 |
| alpha | 0.0001 | L2 penalty added to the loss, so it interacts with Adam (not decoupled weight decay, Day 34) |
| batch_size | auto = min(200, n_samples) | Mini-batches of up to 200 rows |
| max_iter, early_stopping | 200, False | Up to 200 epochs; no validation-based stopping unless switched on |
| weight init | uniform, bound sqrt(6 / (fan_in + fan_out)) | Glorot-style even for ReLU (bound sqrt(2 / ...) for logistic units) |
Read from the installed scikit-learn 1.9.1 signature and source. Inputs are not scaled for you; put a StandardScaler in the pipeline. Practitioner heuristic for tabular data: switch on early_stopping and add the scaler before touching the widths.
When an expert would not start with an MLPA decision flow from data type to first model, and five misreadings to avoid in a design review.
- Tabular rows?
- yes: to Gradient boosting first
no - Pixels or sequence?
- yes: to CNN or Transformer
no - Small data, probs?
- yes: to L2 logistic regression
- no: to Small MLP, scaled inputs
- Gradient boosting firstthen an MLP only if it adds
- CNN or Transformerweights shared over space or time (Days 36-39)
- L2 logistic regressioncalibrated scores; the unit a network repeats
- Small MLP, scaled inputscount parameters, compare on one split
A practitioner heuristic, not a law; Day 35 shows how to let a fair comparison decide.
More parameters, more power
Only through the nonlinearity, and only if training finds it and validation confirms it.
Neurons are brain cells
A unit is a weighted sum and a bend; the analogy is historical.
Batch size changes the model
It changes activation memory and gradient noise, never the parameter count.
Deeper is always better
One wide layer can approximate; depth is an efficiency argument, tested on data.
Unit 7 detects fraud
Units are not identifiable (symmetries above); probe behaviour on inputs instead.
Conventions and higher-dimensional tensorsadvancedXW versus Wx, and how the batch-first habit carries over to images and text.
| Criterion | Rows are examples | Column vectors |
|---|---|---|
| Formula | H = XW + b | h = Wx + b |
| Input shape | (batch, n_in) | (n_in,) per example, or (n_in, batch) |
| Weight shape | (n_in, n_out) | (n_out, n_in) |
| Where you see it | numpy code, this course | many textbooks; PyTorch nn.Linear stores weight as (out, in), computes x W^T + b |
A quick .T that makes shapes line up can silently swap which dimension is the batch. Transpose only when you can say why. PyTorch prints nn.Linear's weight as the transpose of this course's W (docs-pytorch-linear).
| Data | Typical layout | What a dense layer acts on |
|---|---|---|
| Tabular rows | (batch, features) | features |
| Images (PyTorch) | (batch, channels, height, width) | usually after flattening or pooling (Day 36) |
| Text or sequences | (batch, sequence length, embedding size) | the last dimension, at every position (Day 39) |
Dense layers act on the last dimension and carry every other dimension through.
Memory anchor
Batch first, widths second; parameters never see the batch; no activation, no depth.
Useful for
Reading or reviewing model code - checking that every layer's shapes and parameter count match the design before training, whether it scores loans, hospital readmissions or sensor defects.
Estimating model size and activation memory before asking for compute.
Explaining to stakeholders what "a neural network" actually computes, starting from logistic regression they already trust.
Not suitable for
Deciding whether a network will beat a classical model - shapes and counts tell you size, not validation performance (Day 35).
Counting parameters of convolutional, recurrent or attention layers with the dense formula - each shares weights differently (Days 36-39).
Traps
Transposed conventions
A formula written as (weights first, column vectors) is copied into code that uses X @ W (rows are examples). Shapes no longer line up, and a quick .T makes it run while silently mixing up which dimension is the batch.
How to avoid it
Write the shape of every array as a comment or an assert with the batch first, and transpose only when you can say why.
Depth without nonlinearity
Layers are added without an activation between them (or the activation is applied to the wrong tensor), and the network silently stays a linear model.
How to avoid it
Check that each hidden layer is followed by an activation; compare against logistic regression - an exact tie is a warning sign.
Senior reasoning
A senior reads a network as a sequence of shapes before reading it as an architecture. They count parameters to estimate size, compare that size with the amount of data, and know that the count says nothing about whether the network will generalize. They start from the linear model the network generalizes, insist on a classical baseline on the same split, and treat an unexplained tie or a shape that depends on the batch as a bug to find, not a result to report.
Interview question
"Walk me through the forward pass of a two-layer network for a batch of examples, with shapes." How would you answer?
Recall cards scheduled after completion
- A dense layer maps n_in inputs to n_out units. Give the shapes of W, b and the output for a batch of B, and its parameter count.
- Why does a network of linear layers without activations have no more expressive power than one linear layer?
Prerequisites and related concepts
References
- Dive into Deep Learningcore
Multilayer perceptrons as stacked affine layers with nonlinearities, the batch-first matrix form, and the collapse of linear layers without activations. (Chapter 5.1 (Multilayer Perceptrons))
- Understanding Deep Learningoptional
Shallow and deep networks built from linear functions and ReLU activations, with parameter counting and the regions a ReLU network carves out. (Chapters 3-4 (shallow and deep neural networks))
- Deep Learningoptional
Feedforward networks as compositions of functions, why the hidden nonlinearity matters, and the universal approximation result mentioned in the deep dive. (Chapter 6 (Deep Feedforward Networks), 6.4.1)
Optional sections
Deep dive
Worked example (use this if the diagnostic flagged shapes or dot products). Take row 2 of the batch, . Hidden unit 1: . Unit 2: . Unit 3: . ReLU gives . Output: . Each hidden value used one column of ; this is exactly what the matrix product does for all rows at once. Check it by printing out[2].
Capacity, honestly. A network with one hidden layer and enough ReLU units can approximate any continuous function on a bounded region to any accuracy (the universal approximation results, Goodfellow et al. 6.4.1). That says such weights exist, not that training will find them or that they will generalize from your data. In practice, deeper networks often reach the same accuracy with far fewer units than a single wide layer, which is one reason depth became standard.
Memory beyond parameters. Training stores parameters, their gradients, optimizer state (Adam keeps two extra numbers per parameter, Day 33) and the activations of every layer for the backward pass. Activations scale with batch size and layer widths; for large models and batches they often dominate. The training memory calculator (Days 40 and 44) makes this explicit.
Tensors beyond two dimensions. Images arrive as (batch, channels, height, width) in PyTorch, text as (batch, sequence length, embedding size). Dense layers act on the last dimension; every other dimension is carried through, which is why the batch-first habit from today generalizes.
Interview practice
"Our readmission network has 10 million parameters and the logistic regression has 50. Surely the network is the more powerful model?" How do you respond to a hospital analytics manager who says this?