Neural networks
Dense Layers: Many Neurons, One Matrix
Understand fully connected layers as affine matrix transformations and learn to reason about dimensions, parameters, and connectivity.
By the end you can
- Translate a collection of neurons into matrix notation
- Determine the weight and bias shapes of a dense layer
- Calculate parameter counts for fully connected transformations
- Explain the benefits and costs of all-to-all connectivity
From repeated equations to one matrix multiply
Writing one weighted sum per output unit is conceptually clear but computationally awkward. Stack the weight vectors as rows or columns, follow a declared convention, and the layer becomes one matrix operation.
With batch-first notation, a common form is Y=XW+b. The orientation is not a matter of taste. The two most-used frameworks store the same layer transposed relative to each other. Keras 3's Dense(units) holds a kernel of shape (input_dim, units) — I×O — and its documentation states the operation outright: “Dense implements the operation: output = activation(dot(input, kernel) + bias)”. PyTorch's torch.nn.Linear(in_features, out_features) computes y = xA^T + b, and holds weight with shape (out_features, in_features) — O×I, the transpose of the Keras layout. NVIDIA's own performance guide records the same split. It adopts “the convention used by PyTorch and Caffe where A contains the weights and B the activations” and notes that “In TensorFlow, matrices take the opposite roles”. Both conventions implement the same affine map. Only the declared shapes tell you which one you are holding, which is why shape reasoning beats memorising a layout.
Much of the matrix is redundant, and that was shown before it was exploited. Denil and colleagues reported it at NIPS 2013: the parameters of a neural network are predictable enough from one another that “in the best case we are able to predict more than 95% of the weights of a network without any drop in accuracy”. Full connectivity is a statement that any coordinate may matter to any output. It is rarely a statement that all of them do.
Keras stores the kernel as (input_dim, units); PyTorch stores weight as (out_features, in_features). One affine map, two transposed spellings of it.
Case
Three quarters of VGG-16’s weights sat in a single dense layer
A single dense weight matrix can dominate a whole network. In VGG-16, the first fully connected layer, fc6, holds 103 million of the network's 138 million weights. It accounts for 206 million of the network's 30.9 billion floating-point operations. In AlexNet the three fully connected layers hold 38 million, 17 million and 4 million of its 61 million weights, and the five convolutional layers hold the remaining 2 million. Han and colleagues reported those per-layer counts at NIPS 2015. Pruning the connections that carried little weight reduced “the number of parameters of AlexNet by a factor of 9x, from 61 million to 6.7 million, without incurring accuracy loss”. A dense layer is expensive in memory long before it is expensive in arithmetic.
Figure
Visual
Shape arithmetic for a batch-first layer
Suppose a batch contains B examples. Each carries I input features, and the layer emits O features.
Those three integers are a hardware decision as well as a bookkeeping one. Forward propagation, activation-gradient computation and weight-gradient computation “are directly expressed as matrix-matrix multiplications”, NVIDIA's guide for these layers says, and its Quick Start Checklist gives the instruction plainly: “Choose the batch size and the number of inputs and outputs to be divisible by 4 (TF32) / 8 (FP16) / 16 (INT8) to run efficiently on Tensor Cores.” The worked example is an output width of 33,708 — a vocabulary. At that width “Tensor Cores cannot be applied and performance reduces drastically”; adding four padding tokens to reach 33,712 restores them. PyTorch's own performance tuning guide gives the same instruction: “set sizes to multiples of 8 (to map onto dimensions of Tensor Cores)”. Four columns out of thirty-three thousand decide whether the multiply runs on the fast path at all. B, I and O are chosen, not merely recorded.
- 1
Input X: B×I
Rows are examples; columns are input coordinates.
- 2
Weights W: I×O
Each output receives one coefficient from every input.
- 3
Product XW: B×O
Matrix multiplication produces every example–output pair.
- 4
Bias b: O
Broadcasting adds one learned offset per output coordinate.
- 5
Output Y: B×O
The layer preserves batch size and changes feature width.
Example
Counting trainable values
Parameter counts reveal memory use and capacity before a model is trained, and they depend on exactly two of the three numbers above: the input width and the output width.
- A 20-to-50 dense layer has 20×50=1,000 weights.
- Adding one bias per output contributes 50 more parameters.
- The total is 1,050 trainable scalars when bias is enabled.
- Doubling both input and output widths multiplies the weight count by four.
- Batch size changes activation memory and compute, but not the layer’s parameter count.
Position
A parameter count states what a model cost, not what it can do
Parameter count is the number every model announcement leads with, and this lesson has just shown it to be the loosest description of a network available. It is a memory figure wearing the clothes of a capability claim.
Read the earlier results again with that in mind. In VGG-16 one fully connected layer holds 103 million of the network's 138 million weights while accounting for 206 million of its 30.9 billion floating-point operations. Three quarters of the parameters. Well under one percent of the arithmetic. In AlexNet, pruning took 61 million parameters down to 6.7 million without incurring accuracy loss. Denil and colleagues had already reported that in their best case more than 95% of the weights in these layers could be predicted from the remaining 5%, without any drop in accuracy either.
Those two networks are mostly convolutional. The cleanest version of the argument was run on a network that is nothing but dense layers. Lenet-300-100 on MNIST carries 266K weights, all of them in dense layers of width 300, 100 and 10. Frankle and Carbin pruned it in 2019 and found a winning ticket retaining 21.1% of the weights that “reaches higher test accuracy faster than the original network”. Their abstract states the general finding: “We consistently find winning tickets that are less than 10-20% of the size of several fully-connected and convolutional feed-forward architectures for MNIST and CIFAR10.” Zhou and colleagues at Uber AI dissected the algorithm independently at NeurIPS 2019. They reproduced its behaviour, and additionally found masks alone giving 86% on MNIST on an untrained network.
None of that says a large model is a bad model. It says the count describes how much room was provisioned, not how much of the room is used. A number that can fall by a factor of nine, or to a fifth of itself, while the behaviour holds is not measuring the behaviour. So a system described by its size has not been described yet. Ask what it was trained on, what the objective rewarded, and what it does that a smaller one does not.
Pruning took AlexNet from 61 million parameters to 6.7 million with no accuracy lost. Nearly nine tenths of the headline number was slack.
Comparison
What full connectivity buys—and charges
Dense layers make few assumptions about which coordinates should interact. That flexibility can become expensive.
The flexibility is not a design preference; there is a theorem underneath it. Finite linear combinations of a fixed sigmoidal function, composed with affine functionals, can uniformly approximate any continuous function of n real variables on the unit hypercube. Cybenko proved that in 1989, for the single-hidden-layer case. His abstract claims exactly that much and no more: “Our results settle an open question about representability in the class of single hidden layer neural networks.” The result is still stated in that form in the peer-reviewed literature. Kidger and Lyons open their 2020 paper with the sentence “The classical Universal Approximation Theorem holds for neural networks of arbitrary width and bounded depth.” Approximation in principle, at whatever width it takes, is the guarantee. Efficient learning from finite data is not.
The charge is still being paid at the largest scale, on top of compact representations rather than raw pixels. Every encoder and decoder layer of the transformer carries a position-wise fully connected sublayer; Vaswani and colleagues specified it in 2017 with “dmodel = 512, and the inner-layer has dimensionality dff = 2048”. OpenAI's GPT-3 paper keeps the same ratio across all eight of its models: “we always have the feedforward layer four times the size of the bottleneck layer”. That includes the 175-billion-parameter model, 96 layers at dmodel = 12288.
Benefit
Every output can combine every input coordinate immediately.
- Flexible global interactions
- Simple implementation
- Useful after compact representations
- Common in task-specific heads
Cost
Parameter count grows as input width multiplied by output width.
- High memory for wide inputs
- No built-in locality
- May ignore known structure
- Can overfit small datasets
Bias broadcasting is convenient, not magical
Frameworks often add a length-O bias vector to every row of a B×O matrix, and broadcasting repeats the operation conceptually without storing B copies of the bias.
This is not a neural-network convenience. It is a general array rule with a published definition. NumPy's user guide compares shapes element-wise, starting from the trailing dimension and working left, and states the test in one line: “Two dimensions are compatible when they are equal, or one of them is 1.” Anything else raises a ValueError. The peer-reviewed description of the library, published in Nature in 2020, records the same mechanism. It adds that “one or both arrays are virtually duplicated (that is, without copying any data in memory), so that the shapes of the operands match”.
Read that rule again and notice what it never consults. It compares two integers for equality-or-1. It has no access to what either axis is supposed to mean. A tensor may run happily while adding an offset along an unintended axis.
The wider failure is measured, not hypothetical. One study collected 175 TensorFlow bugs — 87 from Stack Overflow and 88 from GitHub — and classified 24 of them, 15 and 9 respectively, as “Unaligned Tensor” faults. Zhang and colleagues gave the definition in 2018: “A bug spotted in computation graph construction phase when the shape of the input tensor does not match what it is expected is called an unaligned tensor bug.” A separate group built SFData in 2021, “a set of 146 buggy programs with crashing tensor shape faults”, and opens by stating that among DL library misuses “tensor shape faults are most prevalent”.
The compatibility rule tests two integers for equality-or-1. Successful execution proves shape compatibility, not that the chosen axis is meaningful.
Analogy
A mixing desk with many output channels
On a mixing desk, each output channel can draw a different amount from every input microphone. The weight matrix records all routing strengths, and each channel has its own baseline offset.
Dense layers combine abstract coordinates, not necessarily independent sources. Later nonlinearities also change how each channel should be interpreted.
Matrix notation compresses many routing decisions into one operation.
Key idea
When a dense layer is a poor first choice
Flattening a large image or long sequence into one vector destroys explicit spatial or temporal structure; a dense layer can still fit patterns, but it must learn relationships that convolutions, recurrence, or attention encode more naturally.
Use dense connectivity where global mixing is justified, especially after a structured encoder has produced a compact representation. That is not a marginal use: as the previous section's specifications show, the layer that is a poor first choice on raw pixels is the widest matrix inside every transformer block.
Removing the convolutional prior has a measured price, and it is paid in data. MLP-Mixer is “an architecture based exclusively on multi-layer perceptrons”, with neither convolution nor attention, applied within image patches and across them. Tolstikhin and colleagues reported at NeurIPS 2021 that “when trained from scratch on ImageNet, Mixer-B/16 achieves a reasonable top-1 accuracy of 76.44%”, against the from-scratch state of the art they cite as 86.5% for NFNet. Pre-trained first on the 300-million-image JFT set, Mixer-H/14 reaches 87.94%. The architecture without the prior is not incapable. It is hungrier.
Architecture should spend parameters where useful interactions are expected.
Steps
A paper-and-pencil shape check
Perform this check before running code. It has a measured reason to exist, not a stylistic one. Of the 175 catalogued TensorFlow bugs above, 24 were unaligned tensors, and the broadcasting rule will not catch the ones that happen to be compatible.
Two of the five steps carry the specifics this lesson has already fixed. Step 4 counts parameters, and it is also where the widths are checked for divisibility by 4, 8 or 16 if the multiply is to reach Tensor Cores. Step 5 is the framework convention, and it has two documented answers: Keras 3 stores the kernel as (input_dim, units), PyTorch stores weight as (out_features, in_features).
1. Label axes
Write the semantic meaning of every dimension.
2. Align contraction
Match the input feature width with the first weight dimension.
3. Predict output
Keep the batch axis and replace input width with output width.
4. Count parameters
Multiply connected widths and add optional biases.
5. Verify framework convention
Confirm whether the library stores weights as I×O or O×I.
Key takeaways
- A dense layer computes many affine units efficiently through matrix multiplication.
- Shape conventions vary — Keras 3 stores the kernel (input_dim, units), PyTorch stores weight (out_features, in_features) — but contracted and preserved axes follow linear algebra.
- Parameter count equals input width times output width plus optional output biases. On Tensor Core hardware those widths should be divisible by 4 (TF32), 8 (FP16) or 16 (INT8).
- Broadcasting compatibility tests only whether two dimensions are equal or one of them is 1. It reduces storage while hiding axis mistakes that remain numerically valid; 24 of 175 catalogued TensorFlow bugs were unaligned tensors.
- Full connectivity carries Cybenko's 1989 approximation guarantee for the single-hidden-layer case, at a parameter cost quadratic in width.
- Dense layers are strongest when their inputs already form a compact, meaningful representation — which is why they remain the widest matrices in a transformer block, at four times the model width.