Matrix Multiplication and Linear Transformations
The single operation that powers every neural network. Understand this deeply and every layer in every model makes intuitive sense.
Quick answer
Matrix multiplication combines two matrices by taking dot products of rows and columns: each output element is a row of the left matrix dotted with a column of the right. The shape rule is (m × n) @ (n × p) → (m × p); the inner dimensions must match.
- It is not element-wise; it is rows against columns.
- The inner dimensions must match, or it is undefined.
- Every neural network layer is a matrix multiply plus a bias.
A = [[1, 2, 3], [4, 5, 6]] # 2 x 3
B = [[7, 8], [9, 10], [11, 12]] # 3 x 2
result = [[sum(A[i][k] * B[k][j] for k in range(3)) for j in range(2)] for i in range(2)]
for row in result: # 2 x 2
print(row)Output
[58, 64] [139, 154]
Every neural network is just matrix multiplications stacked on top of each other.
GPT-4 has 1.8 trillion parameters. Claude has hundreds of billions. Stable Diffusion generates photorealistic images. All of them — at the fundamental mechanical level — are doing the same thing: multiplying matrices, adding bias vectors, applying an activation function, and repeating this hundreds of times.
That's not a simplification. When you strip away the marketing language and the paper jargon, a "transformer layer" is a handful of matrix multiplications. A "convolutional layer" is a specific type of matrix multiplication. Even the attention mechanism — the breakthrough behind every modern LLM — is three matrix multiplications and a softmax.
What this page teaches:
How matrix multiplication actually works
Matrix multiplication is not multiplying matching elements. That's element-wise multiplication — a different operation entirely. Matrix multiplication is: for each position in the result, take one row from the left matrix and one column from the right matrix, compute their dot product, and put that number in the result at that position.
That description sounds abstract. Here's the concrete picture.
Step-by-step walkthrough with a small example
Let's multiply a (2×3) matrix by a (3×2) matrix. We'll compute every single element of the result so nothing is hidden.
Let's compute all four positions of the result so you see the full pattern:
The rule that makes everything click
You can only multiply two matrices if their inner dimensions match. The result takes the outer dimensions.
Linear transformations — what "transformation" really means
"Matrix multiplication" is the mechanical description. "Linear transformation" is the geometric meaning. When you multiply a vector by a matrix, you're transforming it — moving it to a new position in space, rotating it, scaling it, or projecting it into a different number of dimensions.
This is not just abstract geometry. In ML, transformations are how models change the representation of data at each layer. A neural network takes raw pixels (a 224×224×3 tensor) and transforms them — layer by layer — into a representation that makes classification easy. Each layer is one transformation. The learned weights are the transformation matrix.
The three things a matrix can do to a vector
Stretch or shrink values. A weight matrix with large values amplifies the input; small values suppress it. This is how neural networks learn which features matter.
Rearrange the direction of the data in space. A rotation matrix changes orientation without changing length. Transformers use rotary position embeddings (RoPE) this way.
Change the number of dimensions. Going from (n,) to (m,) where m < n compresses. Where m > n expands. Expanding = adding capacity. Compressing = extracting essence.
Why "linear" — what that constraint means
A transformation is "linear" if it satisfies two properties: scaling an input scales the output by the same amount, and adding two inputs before transforming gives the same result as transforming each separately and adding. In plain English: lines stay lines, the origin stays fixed.
This is why activation functions exist in neural networks. If you stack 100 linear layers with no activation function between them, the whole stack collapses into one single linear transformation — no matter how many layers you add. Activations (ReLU, GELU, sigmoid) introduce non-linearity, which is what gives neural networks the power to learn curved, complex patterns.
The key insight: linear layers (matrix multiplications) give neural networks their capacity — the ability to transform data into useful representations. Non-linear activation functions give neural networks their power — the ability to learn patterns that no straight line could capture. You need both. One without the other is useless.
A neural network layer — demystified completely
A fully-connected (dense) neural network layer is, at its core, one matrix multiplication plus one vector addition plus one activation function. That's the complete recipe. Nothing is hidden.
The weight matrix W starts as random numbers. During training, gradient descent adjusts every value in W to make the final predictions better. After thousands of iterations, W has been shaped into a transformation that extracts the most useful features from the input for the task at hand.
That's the entire learning process in one sentence: finding the matrix values that make the transformation useful.
Matrix shapes define the network architecture
Every architectural decision in a neural network — how many layers, how many neurons per layer, how wide or narrow it is — is just a choice about matrix shapes. When a paper says "a feedforward layer with hidden dimension 512" it means the weight matrix W has 512 columns. That's it. The entire architecture is encoded in the shapes.
Batched matrix multiplication — how Transformers work
Standard matrix multiplication works on 2D matrices. But in deep learning, data is often 3D or 4D — batches of sequences, batches of images, multi-head attention. NumPy and PyTorch both support batched matmul: apply matrix multiplication to every "slice" simultaneously.
The attention mechanism in every Transformer uses this extensively. When the model processes a sentence of 128 tokens with 12 attention heads, it's doing 12 separate matrix multiplications simultaneously — one per attention head — using batched matmul on a 3D tensor.
Singular Value Decomposition — every matrix has a hidden structure
Any matrix can be decomposed into three simpler matrices: U, Σ (Sigma), and Vᵀ. This is called Singular Value Decomposition (SVD). It's one of the most important operations in all of applied mathematics — and it underpins PCA, recommender systems, data compression, and the initialisation of weight matrices in neural networks.
The decomposition says: any linear transformation (matrix) can be broken into three sequential transformations — a rotation, a scaling, and another rotation. The scaling factors are called singular values and they reveal how much information each "direction" in the data carries.
SVD and PCA — the connection
PCA (Principal Component Analysis) — which has its own full module in this track — is literally SVD applied to a mean-centred dataset. When sklearn fits a PCA model, it runs SVD under the hood. The "principal components" are the columns of V. The "explained variance ratio" comes from squaring the singular values. Once you understand SVD, PCA is just a specific application of it.
Every matrix shape error — explained and fixed
Shape errors are the most common errors in all of deep learning. You will hit these constantly. Knowing how to read them and fix them in under 30 seconds separates fast learners from frustrated ones.
Why GPUs exist — and what vectorisation means
A naive matrix multiplication with two loops runs at roughly one multiplication per clock cycle. Multiplying two (1000×1000) matrices requires 1 billion multiply-add operations. At 1 billion per second (1 GHz), that's one second per multiplication — far too slow for training.
GPUs have thousands of small cores that can each do one multiplication simultaneously. A modern GPU can do trillions of multiplications per second. That's why the same matrix multiply that takes 1 second on a CPU takes 0.1 milliseconds on a GPU. Deep learning didn't become practical because of better algorithms — it became practical because GPUs could do matrix multiplication at scale.
The practical rule: if you find yourself writing a for loop over rows or columns of a NumPy array, stop. There is almost certainly a vectorised operation that does the same thing. The entire skill of "writing efficient ML code" is learning to express computations as matrix operations instead of loops. This track shows you how to do that at every step.
Before a model architecture gets approved, someone does this exact arithmetic
A machine learning engineer proposes doubling a model's hidden dimension because early experiments show better accuracy. Before that change gets approved, someone — usually the same engineer, sometimes a tech lead in a design review — has to answer a very concrete question: what does this cost, in latency and in dollars? The answer comes directly out of the matrix shapes from this page, not out of intuition.
The standard estimate: multiplying an (m × n) matrix by an (n × p) matrix costs roughly 2 × m × n × p floating-point operations (FLOPs) — two, because each output element needs one multiplication and one addition per term in the dot product. Multiply that by how many times per second the model needs to run, and you have a real infrastructure cost estimate before a single GPU-hour is billed.
Where SVD shows up in this same conversation: when a proposed layer is too expensive as a full (n × p) matrix, teams often reach for a low-rank approximation instead — this is the entire idea behind LoRA (Low-Rank Adaptation), the dominant technique for cheaply fine-tuning large language models. Instead of updating the full weight matrix, LoRA learns two much smaller matrices whose product approximates the update, exactly the U-Σ-Vᵀ decomposition from the SVD section above, applied to make fine-tuning a multi-billion parameter model affordable on a single GPU.
Five things people get wrong about matrix multiplication
That operation exists and is useful, but it is called element-wise multiplication, and it is a completely different result from matrix multiplication. Matrix multiplication takes a row from the left matrix and a column from the right matrix and computes their dot product for each position in the result — as walked through step by step earlier on this page. The two operations even use different NumPy syntax on purpose: @ or np.matmul() for real matrix multiplication, plain * for element-wise. Mixing them up does not raise an error when shapes happen to allow both — it just silently produces the wrong numbers.
Two same-shaped square matrices are exactly the dangerous case, because both A @ B and A * B run without error and return a same-shaped result — just a numerically different one. There is no shape mismatch to warn you. This is precisely the bug in the 'Result has unexpected shape' entry in the errors section above: the code runs fine, the shapes look fine, and the output is simply the wrong computation, often not discovered until model accuracy quietly underperforms for reasons nobody can explain.
Matrix multiplication is not commutative, and the callout above states this directly: A @ B and B @ A are generally different, and one of the two often is not even a valid operation at all once the shapes stop lining up in reverse. The reason is structural, not a quirk of notation — the left matrix's rows drive the result's rows and the right matrix's columns drive the result's columns, so swapping the order changes which vectors get dotted with which. Order is not a stylistic choice in matrix multiplication; it changes the answer.
The 'Matrix shapes define the network architecture' section above makes the reverse point directly: when a paper describes 'a hidden dimension of 512,' that sentence is entirely describing the number of columns in a weight matrix. Narrow versus wide versus deep are not abstract design philosophies competing with the shapes — they are literally different choices of matrix shape, and the resulting parameter count and compute cost follow mechanically from that choice, as the FLOPs estimate above shows.
GPUs move the practical ceiling enormously higher, but they do not remove it — the efficiency section above shows the same operation still taking measurably longer as matrix sizes grow, and the FLOPs formula (2 × m × n × p) still applies whether the hardware is a laptop CPU or a data-centre GPU cluster. This is exactly why the industry spends serious engineering effort on techniques like low-rank approximation (SVD) and LoRA rather than simply 'throwing more GPU at it' — at the scale of models like GPT-4, the multiplication cost is large enough that shrinking it meaningfully changes what is financially feasible to train or serve.
Matrix multiplication — 5 questions interviewers actually ask
To compute result[i, j], take row i of the left matrix and column j of the right matrix, multiply them element by element, and sum the products — a single dot product. Repeat that for every (i, j) position and you have the full result. The shape rule that makes this possible: for an (m × n) matrix times an (n × p) matrix, the inner dimensions — both n — must match, since a row and a column can only be dotted if they have the same length. The result takes the outer dimensions, (m × p).
It is not commutative because the operation is structurally asymmetric: the left matrix's rows and the right matrix's columns get dotted together, so swapping the order changes which vectors are being combined, and often changes whether the shapes are even compatible at all. It matters constantly in practice — a layer computes X @ W, not W @ X, because X's rows are examples and W's columns are output neurons; reversing the order would either throw a shape error or silently compute something with a completely different meaning.
The standard estimate is 2 × m × n × p FLOPs for an (m × n) times (n × p) multiplication — two operations, a multiply and an add, per term in each output's dot product. In practice this is exactly what gets computed before approving an architecture change: doubling a hidden dimension roughly doubles both the FLOPs and the parameter count of that layer, which translates directly into serving latency and GPU cost at inference time. Teams run this arithmetic before committing to a more expensive architecture, not after.
A fully-connected layer is Z = X @ W + b followed by an activation function, and nothing more is hidden inside it. If X is a batch of examples with shape (batch, in_features) and W is the layer's weight matrix with shape (in_features, out_features), the matmul produces Z with shape (batch, out_features) — every example transformed into a new representation simultaneously, in one operation. The activation function is then applied element-wise to Z without changing its shape at all.
Singular Value Decomposition breaks any matrix into three simpler matrices, U, Σ, and Vᵀ, equivalent to a rotation, a scaling, and another rotation — and the singular values in Σ rank how much information each direction of the data carries. A concrete production application is LoRA (Low-Rank Adaptation), the standard technique for cheaply fine-tuning large language models: instead of updating a full, enormous weight matrix, LoRA learns two much smaller matrices whose product approximates the needed update, using far fewer parameters — the same low-rank idea that underlies SVD-based compression and PCA.
You now understand the engine of every neural network.
Matrix multiplication is the operation. Linear transformation is the meaning. The weight matrix is the thing being learned. Gradient descent updates those weights. That's the complete loop of machine learning — and you now understand two of the four parts deeply.
The next lesson covers derivatives and gradients — the mechanism that tells gradient descent which directionto move the weights. Once you have matmul and gradients, backpropagation (how neural networks train) becomes completely obvious. No magic. Just chain rule applied to matrix multiplications.
The mathematical engine behind backpropagation — explained visually before a single formula appears.
🎯 Key Takeaways
- ✓Matrix multiplication is not element-wise — it is row × column dot products. Each element of the result is a dot product of one row from the left matrix and one column from the right matrix.
- ✓Shape rule: (m × n) @ (n × p) → (m × p). Inner dimensions must match. The result takes the outer dimensions. If they don't match, print shapes and transpose.
- ✓A linear transformation changes the direction or dimension of data without curving it. Matrix multiplication IS a linear transformation.
- ✓One neural network layer = X @ W + b followed by an activation. The weight matrix W starts random and is updated by gradient descent during training.
- ✓Stacking linear layers without activation functions collapses to a single linear transformation — no matter how many layers you add. Non-linear activations (ReLU, GELU) are what give depth its power.
- ✓SVD decomposes any matrix into U Σ Vᵀ — three simpler transformations. The singular values reveal how much information each direction carries. PCA is SVD applied to centred data.
- ✓Never loop over matrix elements in Python. Vectorised NumPy operations using @ are 100,000× faster. The entire skill of efficient ML code is expressing computation as matrix operations.
Quick check
Can you multiply a (4 × 3) matrix by a (4 × 2) matrix?
HELP US IMPROVE
How was this lesson?
Your note is shared with the Chaduvuko team for review. Please don’t include personal or sensitive information.
Spotted a mistake or something unclear? Suggest a fix on GitHub or email us. Select the text first and it is quoted for you.
Discussion
0Have a better approach? Found something outdated? Share it — your knowledge helps everyone learning here.
