Module 1 · Talking to a model
Lesson 1.5
Cosine similarity
Cosine similarity is a score from -1 to 1 that shows how closely two lists point the same way.
The problem
Why it matters
Without it
Two embeddings are two long lists of numbers. Comparing them value by value gives no single clear answer.
With it
One score sums up the comparison. A high score means the texts are close in meaning, and a low score means they are not.
How it works
Both lists need the same length
Cosine similarity compares two embeddings with the same number of values. The refund list is [0.9, 0.1, 0] and the money-back list is [0.8, 0.2, 0.1]. Both have three values, so each value pairs with the value in the same position.
const refund = [0.9, 0.1, 0];const moneyBack = [0.8, 0.2, 0.1];console.log("Same length ->", refund.length === moneyBack.length);Output
Same length -> true
Multiply the pairs and add them
Multiply the two values in each position, then add the three products. The products are 0.72, 0.02, and 0, so the dot product is 0.74. The dot product grows with the size of both lists, so the next block corrects for it.
Position Refund Money back Product 1 0.9 0.8 0.72 2 0.1 0.2 0.02 3 0 0.1 0 Sum 0.74 Each position, its product, and the dot product at the bottom. const refund = [0.9, 0.1, 0];const moneyBack = [0.8, 0.2, 0.1];// Multiply each pair, then add the products.const dot = refund.reduce((s, v, i) => s + v * moneyBack[i], 0);console.log("Dot product ->", dot.toFixed(2));Output
Dot product -> 0.74
Divide by the two lengths
The length of a list is the square root of the sum of its squared values. The refund list has length 0.91 and the money-back list has length 0.83. Dividing the dot product by both lengths gives 0.74 divided by 0.91 times 0.83, which is 0.984.
- Dot product 0.74
- Divide by 0.91 times 0.83
- Score 0.984
The three numbers that make the score. const refund = [0.9, 0.1, 0];const moneyBack = [0.8, 0.2, 0.1];// Math.hypot gives the length of each list.const a = Math.hypot(...refund);const b = Math.hypot(...moneyBack);const dot = refund.reduce((s, v, i) => s + v * moneyBack[i], 0);const score = dot / (a * b);console.log(a.toFixed(2), b.toFixed(2), score.toFixed(3));Output
0.91 0.83 0.984
Reading the score
A score of 1 means the two lists point the same way. A score of 0 means they meet at a right angle, with no shared direction. A score of -1 means they point in opposite directions. The refund and password lists score 0.108, close to 0, so they share little direction.
Scores of the refund list against two others. The money-back list is close to 1. const refund = [0.9, 0.1, 0];const password = [0, 0.9, 0.2];const dot = refund.reduce((s, v, i) => s + v * password[i], 0);const a = Math.hypot(...refund);const b = Math.hypot(...password);console.log("Refund vs password ->", (dot / (a * b)).toFixed(3));Output
Refund vs password -> 0.108
Only the direction counts
Scaling a list does not change the score. The list [2, 4] is twice the list [1, 2], so both point the same way and score 1. Search systems sort texts by this score, so it is only as good as the embeddings behind it.
The lists [1, 2] and [2, 4] lie on one line through zero, so they point the same way. const cosine = (a: number[], b: number[]) => { const dot = a.reduce((s, v, i) => s + v * b[i], 0); return dot / (Math.hypot(...a) * Math.hypot(...b));};const result = cosine([1, 2], [2, 4]);console.log("[1, 2] vs [2, 4] ->", result.toFixed(3));Output
[1, 2] vs [2, 4] -> 1.000
Common mistakes
What goes wrong
- Comparing lists of different lengths. The formula needs the same number of values in both lists.
- Reading the score as a percent. A score of 0.8 is not 80 percent sure. It only measures closeness in direction.
- Choosing a cutoff without testing it. The right cutoff depends on your data, so test it on real examples.
Use it when and skip it when
Is it the right tool?
Use it when
- You rank texts by how close they are in meaning.
- You compare two embeddings that came from the same model.
Skip it when
- The lists have different lengths or come from different models. The score then means nothing.
- You need the size of the gap between two values, not the direction. A distance measure fits better.
Words to know
Terms in this lesson
- Cosine similarity
- A score from -1 to 1 for how closely two lists point the same way.
- Dot product
- Multiply each pair of values, then add the products.
- Length
- The square root of the sum of the squared values. It is also called the norm.
- Ranking
- Sorting results from the best match to the weakest.