Part 01 · Mathematics for AI

Vectors and Matrices

Linear AlgebraVectorsDot productMatrix multiplicationNumPy

To a model, a word, a picture or a user is just a list of numbers. Vectors, the dot product that measures how alike two of them are, and the matrix multiply that every layer runs.

⏱ 1-2 hours · Station 1 of 92

What it is and why it matters

A streaming app wants to recommend one movie to you. It can't understand "I like action, a bit of romance, no slapstick". It can only work with numbers. So it describes every movie with a few numbers, say how much action, comedy and romance it has, and describes your taste with numbers in the same three slots:

Dhaka Attack  [0.9, 0.1, 0.2]
your taste    [0.8, 0.0, 0.4]

A list of numbers like this is a vector. That's the whole idea behind the first step of every AI system: a word, a photo, a song or a customer becomes a vector. Once things are vectors, "how alike are these two?" becomes arithmetic.

Two operations do almost all the work in this course:

  • The dot product measures how much two vectors agree.
  • Matrix multiplication runs many dot products at once. Every layer of a neural network, and every attention step in an LLM, is mostly this.

How it works

Dot product. Multiply matching slots and add everything up:

[0.9, 0.1, 0.2] · [0.8, 0.0, 0.4]
= 0.9×0.8 + 0.1×0.0 + 0.2×0.4
= 0.72 + 0 + 0.08 = 0.80

Where both vectors are big (action: 0.9 and 0.8), the product is big. Where one is zero (comedy: you gave 0.0), that slot adds nothing. So a large dot product means "they point the same way".

Length and cosine similarity. The dot product also grows when vectors are just longer. A movie with every number doubled would score twice as high without being any more "your type". To compare only the direction, divide by both lengths:

length(a) = √(a·a)
cosine(a, b) = (a·b) / (length(a) × length(b))      between -1 and 1

Cosine 1 means same direction, 0 means unrelated, -1 means opposite. Search engines and RAG systems (Part 5) rank documents by exactly this number.

Matrix. Stack the movie vectors as rows and you get a matrix with shape (3, 3): 3 rows, 3 columns. Multiplying it by your taste vector gives one dot product per row, which is one score per movie, in a single operation.

The shape rule. You can multiply (m × n) by (n × p) only when the inner numbers match, and the result is (m × p). Most real bugs with matrices are shape bugs, so learn to check shapes before anything else.

Predict first

A batch of 32 tokens, each a vector of 768 numbers, goes into a layer whose weight matrix is (768, 3072). What shape comes out?

Visual

user tastemovies matrix (3 × 3)scores[0.8, 0.0, 0.4][0.9, 0.1, 0.2]Dhaka Attack[0.1, 0.3, 0.9]Monpura[0.6, 0.2, 0.5]Hawa0.800.440.68uuuΣΣΣ0.9×0.8 + 0.1×0.0 + 0.2×0.4 = 0.800.1×0.8 + 0.3×0.0 + 0.9×0.4 = 0.440.6×0.8 + 0.2×0.0 + 0.5×0.4 = 0.68one dot product per row; the biggest score wins
Your taste vector meets each movie row in turn. Each meeting is one dot product: multiply slot by slot, add up, and the result lands in the scores column. Dhaka Attack gets 0.80, the highest, so it is recommended.

Practical example

The same recommendation in code. NumPy's @ operator does the matrix multiply; the Go version writes the loops by hand so you can see there's nothing magic inside.

import numpy as np

# Each movie as 3 numbers: [action, comedy, romance], each 0 to 1.
names = ["Dhaka Attack", "Monpura", "Hawa"]
movies = np.array([
    [0.9, 0.1, 0.2],
    [0.1, 0.3, 0.9],
    [0.6, 0.2, 0.5],
])
# One user's taste, in the same 3 slots.
user = np.array([0.8, 0.0, 0.4])

print("shapes:", movies.shape, "@", user.shape)

# One matrix multiply = one dot product per movie (row).
scores = movies @ user
for name, s in zip(names, scores):
    print(f"{name:<13} score {s:.2f}")
print("recommend:", names[int(np.argmax(scores))])

# Cosine similarity: dot product of the two directions, ignoring length.
def cosine(a, b):
    return float(a @ b / (np.linalg.norm(a) * np.linalg.norm(b)))

print(f"cos(Dhaka Attack, Hawa)    = {cosine(movies[0], movies[2]):.3f}")
print(f"cos(Dhaka Attack, Monpura) = {cosine(movies[0], movies[1]):.3f}")

Output (both programs print the same):

shapes: (3, 3) @ (3,)
Dhaka Attack  score 0.80
Monpura       score 0.44
Hawa          score 0.68
recommend: Dhaka Attack
cos(Dhaka Attack, Hawa)    = 0.883
cos(Dhaka Attack, Monpura) = 0.339

Reading it:

  • shapes: (3, 3) @ (3,) is the shape check: 3 movies with 3 numbers each, times one vector of 3. The inner 3s match.
  • The three scores are the three dot products from the diagram. Dhaka Attack wins with 0.80.
  • Cosine says Dhaka Attack and Hawa are close (0.883, both action-heavy), while Dhaka Attack and Monpura are far apart (0.339, one is action, one is romance). That's "similar movies" without any rules written by hand.

In production

  • Shapes break first. A (768,) vector where the code expects (1, 768) silently broadcasts into the wrong answer in NumPy and PyTorch. Assert shapes at the boundaries of your functions.
  • Memory adds up fast. One million documents, each a 768-number vector in float32 (4 bytes), is 1,000,000 × 768 × 4 ≈ 3 GB. Vector databases (Part 5) exist because of this number.
  • Normalise once. If you divide every vector by its length when you store it, cosine similarity becomes a plain dot product, which is cheaper at query time.
  • Zero vectors. An empty document can produce an all-zero vector. Its length is 0, cosine divides by zero, and you get NaN that spreads through rankings. Guard against it.
  • Speed. A matrix multiply is millions of independent multiply-adds, which is why GPUs (Part 10), with thousands of small cores, run it so much faster than a CPU.

Key takeaway

Things become vectors; the dot product measures how much two vectors agree; a matrix multiply is many dot products at once, and its shapes must line up as (m × n)(n × p) → (m × p).

Practice

Add a fourth movie, "Aynabaji" with [0.7, 0.1, 0.3], and a second user who loves romance, [0.1, 0.1, 0.9]. Put both users in a (2, 3) matrix and compute every user-movie score with one multiply. What shape is the result, and which movie does each user get?

Answer

Users (2 × 3) @ movies transposed (3 × 4) = a (2 × 4) table of scores: one row per user, one column per movie. In NumPy: users @ movies.T. User 1 scores Dhaka Attack 0.80, Monpura 0.44, Hawa 0.68, Aynabaji 0.68, so still Dhaka Attack. User 2 scores 0.28, 0.85, 0.53, 0.35, so Monpura.

Station stamp

Finish the chapter

Done reading? Press Complete to save your progress. XP is Pro-only — on Pro this chapter is worth +10 XP.

Go Pro to earn XP →