A matrix is a rectangular arrangement of numbers in rows and columns. The entry in row and column is written .
The previous lesson used matrices as a container for the coefficients of a system. This one covers the layer underpinning all the computation that follows: what a matrix is as a data structure and which operations it admits.
Shape and indexing
In NumPy a matrix is a two-dimensional ndarray. The .shape attribute returns the pair and determines which operations are legal:
import numpy as np
A = np.array([[1, 2, 3],
[4, 5, 6]])
A.shape # (2, 3)
A.ndim # 2
A[0, 2] # 3
Indexing starts at , so the entry of mathematical notation is read as A[i-1, j-1]. The offset is a frequent source of error when transcribing a formula into code.
The set is itself a vector space of dimension : matrices add and scale entry by entry, and those two operations are enough to give the set the structure of a vector space. A matrix is, in that sense, a vector with a shape imposed on it.
Memory layout
The elements occupy a contiguous block of memory. The shape is metadata: it states how to traverse that block, not how it is stored. NumPy uses C order by default, which varies the last dimension fastest, that is, row by row.
A.reshape(6) # array([1, 2, 3, 4, 5, 6])
A.ravel() # the same, and without copying where possible
A.reshape(6).base is A # True: a view, not a copy
reshape neither modifies nor moves any data: it reinterprets the same block under a different shape, provided the number of elements is preserved. The operation therefore costs constant time.
That distinction has practical consequences in machine learning. Flattening a batch of pixel images to feed a dense layer copies nothing:
imgs = np.random.rand(128, 28, 28) # 128 images
X = imgs.reshape(128, 784) # view: 128 vectors of 784 components
X.shape # (128, 784)
A value of in one dimension tells NumPy to infer it from the others: imgs.reshape(128, -1) produces the same result without writing .
Addition and broadcasting
Two matrices of the same shape add entry by entry. When the shapes differ, NumPy applies broadcasting: it aligns both shapes from the right and, for each pair of dimensions, requires that they match or that one of them equals , in which case it is logically repeated along that axis.
| A.shape | 64 | 8 |
|---|---|---|
| B.shape | — | 8 |
| result | 64 | 8 |
(64, 8)
- dimensions match
- stretched from 1
- incompatible
The rule applies without materialising the repetition: no extra memory is reserved for the implicit copies. That detail explains why adding a bias vector to an entire batch costs the same memory as the batch itself.
X = np.zeros((64, 8)) # batch of 64 examples, 8 features
b = np.arange(8) # one bias per feature
(X + b).shape # (64, 8): b is added to each of the 64 rows
The (2, 3) + (3, 2) case in the figure is the most common error in machine learning code. No dimension equals and no pair matches, so the operation is illegal. Reading .shape before operating prevents most of these failures.
The matrix product
Given and , the product is defined entry by entry as
Each entry of is the dot product of a row of with a column of . The figure walks through that computation cell by cell:
C11 = 1·1 + 2·3 = 7
Step 1 / 4
The definition imposes the compatibility condition: the number of columns of must equal the number of rows of . The inner dimensions cancel and the outer ones determine the shape of the result.
A = np.ones((2, 3))
C = np.ones((2, 2))
A @ C
# ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0
@ versus *
@ implements the matrix product; * implements the Hadamard product, which multiplies entry by entry and requires shapes compatible under broadcasting. They are distinct operations returning distinct results from the same inputs.
M = np.array([[1, 2],
[3, 4]])
M @ M # array([[ 7, 10], matrix product
# [15, 22]])
M * M # array([[ 1, 4], Hadamard
# [ 9, 16]])
The confusion is silent when both matrices are square: the code does not fail, it returns a numerically plausible result, and the error surfaces much later.
Transpose and identity
The transpose swaps rows and columns, . In NumPy it is obtained with .T, which returns a view and does not copy.
A = np.ones((2, 3))
(A @ A.T).shape # (2, 3) @ (3, 2) → (2, 2)
(A.T @ A).shape # (3, 2) @ (2, 3) → (3, 3)
Both expressions are legal and produce matrices of different sizes, which shows immediately that the product is not commutative. The next lesson treats the transpose in detail.
The identity matrix has ones on the diagonal and zeros elsewhere, and is the neutral element of the product:
I = np.eye(3)
M3 = np.random.randn(3, 3)
np.allclose(I @ M3, M3) # True
Application: the linear layer
A dense layer of a neural network applies an affine transformation to each example: a product with a weight matrix followed by the addition of a bias.
In practice examples are not processed one at a time. A batch of examples is arranged as a matrix , one example per row, and the whole layer is evaluated with a single product:
X = np.random.randn(64, 3) # batch of 64 examples, 3 features
W = np.random.randn(3, 8) # layer from 3 to 8 units
b = np.random.randn(8) # one bias per output unit
Y = X @ W + b # (64, 3) @ (3, 8) → (64, 8), and b by broadcasting
Y.shape # (64, 8)
That line brings together the three operations of this lesson. The product X @ W transforms all 64 examples at once; broadcasting adds the same bias to the 64 rows without replicating it in memory; and the row-wise layout is what makes the dimensions fit.
The convention of placing examples in rows explains the shape of in the code, transposed with respect to the formula. Both conventions coexist in the literature, and checking .shape is the reliable way to tell which one a given implementation uses.
The cost of is operations, all independent of one another. That independence is what allows them to be distributed across thousands of cores, and is the technical reason deep learning runs on GPUs: training a network is, for the most part, a succession of matrix products.
Exercise. For X of shape (64, 3) and W of shape (3, 8), determine the shape of X.T @ X and of W @ W.T before running them. Explain why X @ X is illegal and which product is.