Md. Asif Uddin
Problem I.5.B03

A shape and parameter audit of a four-layer MLP

shape▲△△

Exact integer counts; memory in decimal megabytes.

STATEMENT

Audit a classifier layer by layer: the shape of every matrix, the shape of every intermediate, the parameter count of each layer and of the whole model, and the memory that model occupies in fp32. Then answer the question the audit exists to answer — where is the money.

GIVEN

An MLP with widths

(d0,d1,d2,d3,d4)=(784, 512, 256, 128, 10)(d_0, d_1, d_2, d_3, d_4) = (784,\ 512,\ 256,\ 128,\ 10)

Four affine layers, ReLU after the first three, no activation on the head. One example at a time, so the input is a row xR1×784\vec{x} \in \R^{1 \times 784}.

FIND

A table of W()\mat{W}^{(\ell)} and b()\vec{b}^{(\ell)} shapes, the shape of each a()\vec{a}^{(\ell)}, per-layer and total parameter counts, the fp32 size, and the share of the total held by the first layer.

STRATEGY

Count with equation (I.5.2), one layer at a time. The two habits worth building are writing the bias into the count rather than waving it away, and checking that each layer’s output shape is the next layer’s input shape before adding anything up.

SOLUTION

Step 1 — shapes. A layer holds W()Rd1×d\mat{W}^{(\ell)} \in \R^{d_{\ell-1} \times d_\ell} and b()R1×d\vec{b}^{(\ell)} \in \R^{1 \times d_\ell}, and the row passing through is (1×d1)(d1×d)(1×d)(1 \times d_{\ell-1})(d_{\ell-1} \times d_\ell) \to (1 \times d_\ell).

\ellW()\mat{W}^{(\ell)}b()\vec{b}^{(\ell)}a()\vec{a}^{(\ell)}
1784×512784 \times 5121×5121 \times 5121×5121 \times 512
2512×256512 \times 2561×2561 \times 2561×2561 \times 256
3256×128256 \times 1281×1281 \times 1281×1281 \times 128
4128×10128 \times 101×101 \times 101×101 \times 10

Every inner dimension meets. The batch dimension is untouched throughout, which is what makes the same weights work for one example or ten thousand.

Step 2 — parameters, layer by layer. d1d+dd_{\ell-1}d_\ell + d_\ell:

784512+512=401408+512=401920784 \cdot 512 + 512 = 401\,408 + 512 = 401\,920

512256+256=131072+256=131328512 \cdot 256 + 256 = 131\,072 + 256 = 131\,328

256128+128=32768+128=32896256 \cdot 128 + 128 = 32\,768 + 128 = 32\,896

12810+10=1280+10=1290128 \cdot 10 + 10 = 1\,280 + 10 = 1\,290

Step 3 — the total.

N=401920+131328+32896+1290=567434N = 401\,920 + 131\,328 + 32\,896 + 1\,290 = 567\,434

Step 4 — memory. At 4 bytes per fp32 parameter, 567434×4=2269736567\,434 \times 4 = 2\,269\,736 bytes: 2.272.27 MB, or 2.1652.165 MiB. Training with Adam multiplies that by roughly four, since the optimiser carries two moment estimates alongside the gradient — a point Chapter I.7 makes properly.

Step 5 — where the money is.

401920567434=70.83%\frac{401\,920}{567\,434} = 70.83\%

The first layer holds seven parameters in ten. It is not the widest layer and it is not doing the classification; it is simply the one facing the input, and the input is 784 numbers wide. This is the general shape of the bill: an MLP pays for its interface with the data.

Answer

\ellshapeparameters
1784×512784 \times 512401920401\,920
2512×256512 \times 256131328131\,328
3256×128256 \times 1283289632\,896
4128×10128 \times 1012901\,290
total567434\mathbf{567\,434}

22697362\,269\,736 bytes in fp32, which is 2.272.27 MB. The first layer holds 70.83%70.83\% of the model.

Check — sanity

The chain of shapes closes. 78451225612810784 \to 512 \to 256 \to 128 \to 10, with each layer’s output width equal to the next layer’s input width, and the final width equal to the number of classes. A single mismatch anywhere would make the model unrunnable, so this check is free and catches most typing errors.

The biases are a rounding error, and are still counted. 512+256+128+10=906512 + 256 + 128 + 10 = 906 of 567434567\,434, or 0.16%0.16\%. They are cheap, which is a reason to keep them rather than a reason to omit them from the count — and I.5.X03 shows what dropping them costs.

The layer sizes fall as the widths multiply out. 401920:131328:32896:1290401\,920 : 131\,328 : 32\,896 : 1\,290 is close to 4:1.3:0.33:0.0134 : 1.3 : 0.33 : 0.013, matching the products 784512784 \cdot 512, 512256512 \cdot 256, 256128256 \cdot 128, 12810128 \cdot 10. Parameters track the product of adjacent widths, exactly as (I.5.2) says.

Where this breaks

The count is a count of parameters, and parameters are only one of three things that occupy memory. Activations scale with the batch: at batch 1024 the intermediates alone are 1024×(512+256+128+10)×4=37109761024 \times (512 + 256 + 128 + 10) \times 4 = 3\,710\,976 bytes, more than the model. Optimiser state scales with the parameters. Gradients match the parameters again. Quoting a parameter count as though it were a memory requirement is the most common way to underestimate what a model needs by an order of magnitude — and the ratio is worst exactly where it matters, which is during training rather than inference.

Variation

Re-audit with widths (784,2048,10)(784, 2048, 10) — a wider model with one hidden layer instead of three. Compare the totals before computing them, then check the prediction. Which is larger, and by how much?

Draws on