Sythra

Article

What matrices actually do in machine learning

Your dataset is a matrix, a model is a matrix, and training is matrix multiplication repeated. Six operations, drawn out, with the ML job each one does.

vaibhavkothari· Aug 26, 2026· 7 min readBeginner
ShareXLinkedIn
What matrices actually do in machine learning cover

Open any machine learning paper and you drown in symbols. Open the code and it is four lines of NumPy.

The gap is smaller than it looks. Almost everything in machine learning is one of six matrix operations, and each one does a job you can describe in a sentence.

Your data is already a matrix

A spreadsheet is a matrix. Rows are examples, columns are featuresThe input measurements the model learns from:

X — 4 houses, 3 features4×3
1200315180048950222240053

Size in square feet, bedrooms, age in years. Every dataset you will ever load has this shape:

import numpy as np

X = np.array([
    [1200, 3, 15],
    [1800, 4, 8],
    [950, 2, 22],
    [2400, 5, 3],
])

print(X.shape)        # (4, 3)  ->  4 samples, 3 features

Read (4, 3) as "four rows of data, three measurements each." Getting into the habit of reading shapes out loud removes most of the mystery from ML code.

Operation 1 — Multiplication: apply a model

A linear model has one weight per feature. Multiplying gives a prediction for every row at once:

X4×3
1200315180048950222240053
×
w3×1
1005000-800
=
predictions4×1
12300020160095000264400

The first prediction, written out:

1200×100 + 3×5000 + 15×(-800) = 120000 + 15000 - 12000 = 123000
w = np.array([[100], [5000], [-800]])
predictions = X @ w
print(predictions.ravel())     # [123000 201600  95000 264400]

That is the entire idea of a linear model: one weight per feature, multiply, add up. A neural network layer is exactly this, followed by a nonlinear squash — and repeated a few times.

Notice what you did not write: a loop over houses. Matrix multiplication handles all four rows in one operation, which is why GPUs make deep learning fast. They are multiplication machines.

Operation 2 — Transpose: swap what you are comparing

.T flips rows and columns:

X4×3
1200315180048950222240053
X.T3×4
1200180095024003425158223

Each row of X.T is now one feature across all houses, instead of one house across all features. That is the setup for asking feature-level questions:

covariance = X.T @ X
print(covariance.shape)     # (3, 3)  ->  one cell per feature pair

X.T @ X gives a 3×3 table of how each feature relates to each other feature. This single expression is at the heart of linear regression, PCA, and correlation analysis. When you see X.T @ X in code, it means "compare features to features."

Operation 3 — Addition: shift everything

Adding a row vector applies one number per column, across every row:

X2×3
1200315180048
+
bias1×3
1001-5
=
result2×3
1300410190053
bias = np.array([100, 1, -5])
print(X[:2] + bias)

That is broadcastingNumPy stretching a smaller array across a bigger one automatically. It is the + b in every neural network layer:

output = X @ W + b

One line, two operations, an entire layer.

Operation 4 — Elementwise multiply: mask and weight

* multiplies matching positions. It looks similar to @ and does something completely different:

values
123456
·
mask
101011
=
result
103056
values = np.array([[1, 2, 3], [4, 5, 6]])
mask = np.array([[1, 0, 1], [0, 1, 1]])
print(values * mask)

This is how dropoutRandomly switching off some neurons during training so the model cannot over-rely on any one works — multiply by a random 0/1 mask. It is also how you apply sample weights, ignore padded positions in a sequence, and zero out invalid entries. Whenever you want to keep some numbers and silence others, this is the operation.

Operation 5 — The identity: multiply by nothing

The identity matrix has 1s on the diagonal and 0s elsewhere:

I3×3
100010001

A @ I = A. It is the matrix equivalent of multiplying by 1, and it earns its place in one very practical spot — ridge regressionLinear regression with a penalty that keeps the weights small:

n_features = X.shape[1]
alpha = 1.0

w = np.linalg.solve(X.T @ X + alpha * np.eye(n_features), X.T @ y)

Adding alpha * np.eye(n) nudges the diagonal upwards. That small nudge is what stops the solution exploding when two features are nearly identical — regularisation, in one term. The connection to overfitting is direct: bigger alpha means smaller weights means less memorising.

Operation 6 — Solve: fit the model in one line

Linear regression has a closed-form answer, and it is pure matrix algebra:

import numpy as np

X = np.array([[1, 1200], [1, 1800], [1, 950], [1, 2400]], dtype=float)
y = np.array([123000, 201600, 95000, 264400], dtype=float)

w = np.linalg.solve(X.T @ X, X.T @ y)
print(w)        # [intercept, price per square foot]

That column of 1s at the front is the intercept trick: it lets the constant term ride along as just another feature.

Use np.linalg.solve, not np.linalg.inv(...) @ .... Solving is faster and numerically stabler than inverting. For anything remotely real, prefer the least-squares routine, which copes when the matrix is singular:

w, residuals, rank, singular = np.linalg.lstsq(X, y, rcond=None)

Reading shapes like sentences

Once shapes make sense, unfamiliar ML code becomes readable:

CodeShapesRead as
X @ W(32, 10) @ (10, 4) → (32, 4)32 samples, 10 features in, 4 out
X.T @ X(100, 5) → (5, 5)Every feature against every feature
X @ X.T(100, 5) → (100, 100)Every sample against every sample
X - X.mean(0)(100, 5) − (5,)Centre each feature
W1 @ W2(10, 8) @ (8, 4)Stack two layers

The middle number always cancels. That cancellation is the computation: 10 features meet 10 weights and collapse into 4 outputs.

A two-layer network, entirely in NumPy

import numpy as np

rng = np.random.default_rng(42)

X = rng.normal(size=(32, 10))          # 32 samples, 10 features

W1 = rng.normal(size=(10, 16)) * 0.1
b1 = np.zeros(16)
W2 = rng.normal(size=(16, 1)) * 0.1
b2 = np.zeros(1)

hidden = np.maximum(0, X @ W1 + b1)     # ReLU
output = hidden @ W2 + b2

print(hidden.shape, output.shape)       # (32, 16) (32, 1)

Six lines. Two matrix multiplies, two broadcast additions, one maximum for the nonlinearity. Every deep learning framework is an optimised, differentiable version of exactly this, and np.maximum(0, z) is the only part that is not linear algebra — without it, stacked layers would collapse back into a single matrix.

FAQ

Why does machine learning use matrices?

Because a dataset is naturally a table of rows and columns, and matrix multiplication applies a model to every row at once. That removes loops and maps directly onto hardware built for parallel multiplication.

What does X.T @ X mean in machine learning?

It produces a square matrix with one cell per pair of features, describing how features relate to each other. It appears in linear regression, PCA and correlation analysis.

What is the difference between matrix multiplication and elementwise multiplication?

Matrix multiplication combines rows with columns and changes the shape. Elementwise multiplication multiplies values in matching positions and keeps the shape, and is used for masks, dropout and sample weights.

Why add the identity matrix in ridge regression?

Adding alpha * np.eye(n) raises the diagonal, which keeps the solution stable when features are nearly duplicated and shrinks the weights. That shrinkage is the regularisation.

Do I need to learn linear algebra before machine learning?

You need shapes, matrix multiplication, transpose and broadcasting. That is enough to read most model code. Eigenvalues and decompositions can wait until you need PCA.

Next reading on Sythra Articles

Keep reading

Related articles

Newsletter

Get new articles

Free essays on learning, Python, and ML — no account required. We’ll only email when there’s something worth reading.