Embedding similarity
An embedding is a vector that stands for the meaning of a piece of text. Texts with similar meaning get vectors that point in a similar direction. The dot product alone is a poor judge of that, because it also grows with the length of the vectors. Cosine similarity fixes this: it divides the dot product by the lengths of both vectors, so only the direction counts. The result is 1 for the same direction, 0 for unrelated directions, and -1 for opposite ones.
Learn it
Words to know
- embedding
- A vector that a trained model produces for a piece of text. Texts with similar meaning get vectors that point in similar directions.
- vector length
- How long a vector is as an arrow, not how many numbers it holds: the square root of the sum of its squared numbers. Also called its norm.
- cosine similarity
- The dot product divided by the two vector lengths multiplied together. It is 1 for the same direction, 0 for unrelated directions and -1 for opposite ones.
How it works
- Compute the dot product of
aandb. - Compute the length of each vector: square every number, add the squares, and take the square root. For
[3, 4]that issqrt(9 + 16) = 5. - Divide the dot product by the two lengths multiplied together.
- If either length is 0, the vector has no direction. Return 0 instead of dividing by zero.
Worked example
Input a = [3, 4] b = [4, 3] Output 0.96
| Quantity | Working | Value |
|---|---|---|
| Dot product | 3 × 4 + 4 × 3 | 24 |
| Length of a | sqrt(3 × 3 + 4 × 4) = sqrt(25) | 5 |
| Length of b | sqrt(4 × 4 + 3 × 3) = sqrt(25) | 5 |
| Cosine | 24 / (5 × 5) | 0.96 |
- a = [3, 4]
- b = [4, 3]
0.96 is close to 1, so the two vectors point in nearly the same direction.
Why it matters
Vector search ranks documents by this number: turn the question and every piece of every document into an embedding, and return the pieces with the highest cosine. Chapter 2 builds the same ranking with a simpler score.
Your turn
Return the cosine similarity of two vectors with the same number of positions.
a- a list of numbers, possibly empty
b- a list with as many numbers as
a - Returns
- A number from -1 to 1, give or take a rounding error in the last digit: the dot product divided by both lengths. If either vector is all zeros or empty, 0.
What JavaScript gives you
Math.sqrt(x)- the square root of x
x * x- x squared
What Python gives you
math.sqrt(x)- the square root of x; it needs
import math, which the starter has x * x- x squared;
x ** 2also works
Example 1
Input a = [3, 4] b = [4, 3] Output 0.96
The dot product is 24 and both lengths are 5, so the answer is 24 / 25 = 0.96.
Example 2
Input a = [1, 0] b = [5, 0] Output 1
Both point the same way: each has all of its size in the first position. One is five times longer and the answer is still 1: length does not count.
Example 3
Input a = [1, 0] b = [0, 1] Output 0
Nothing overlaps: every product is 0, so the dot product and the cosine are 0.
Example 4
Input a = [0, 0] b = [1, 2] Output 0
A vector of zeros has length 0 and no direction. The rule for this case is to return 0.
The checks also try
- opposite directions
- an angle of 45 degrees
- the same direction, ten times longer
- zeros in the second vector
- three positions
- numbers that add up to zero
- a zero in the first position only
- two empty vectors
Hints
Each hint gives away a little more. Open one only when you need it.
Hint 1: Where to start
Three numbers are needed: the dot product, the length of
a, and the length ofb. The dot product is the previous lesson.Hint 2: The key step
A length is the square root of the sum of the squares. A small helper for it saves writing the same loop twice.
Hint 3: The outline
Compute the dot product as the sum of
a[i] * b[i]. Compute each length as the square root of the sum of its squared numbers. If either length is 0, return 0. Otherwise return the dot product divided by the two lengths multiplied together.
Solution
From here on the page gives the method away. If you are still solving, go back to the hints.
Step by step
- The length of a vector: square, add, take the root. This is the distance from the corner to the tip of the arrow. A helper keeps the same loop from being written twice.
- The dot product, as in the previous lesson. It measures how much the two vectors agree, but it still grows with their lengths.
- A vector of zeros has no direction. Its length is 0, and dividing by 0 does not give a usable number. Returning 0 treats it as unrelated to everything.
- Divide by both lengths so that only direction is left. Dividing by one length is not enough: the result would still grow with the size of the other vector.
The same numbered steps are the comments in the solution.
JavaScript
function solve(a, b) {
// 1. The length of a vector: square, add, take the root
const length = (vector) => {
let squares = 0;
for (const x of vector) squares += x * x;
return Math.sqrt(squares);
};
// 2. The dot product, as in the previous lesson
let dot = 0;
for (let i = 0; i < a.length; i++) dot += a[i] * b[i];
// 3. A vector of zeros has no direction
const lengths = length(a) * length(b);
if (lengths === 0) return 0;
// 4. Divide by both lengths so that only direction is left
return dot / lengths;
}Python
import math
def solve(a, b):
# 1. The length of a vector: square, add, take the root
def length(vector):
squares = 0
for x in vector:
squares += x * x
return math.sqrt(squares)
# 2. The dot product, as in the previous lesson
dot = 0
for i in range(len(a)):
dot += a[i] * b[i]
# 3. A vector of zeros has no direction
lengths = length(a) * length(b)
if lengths == 0:
return 0
# 4. Divide by both lengths so that only direction is left
return dot / lengths
Watch out
- Do not forget the square root. Without it you divide by the squared lengths, and the answer for
[3, 4]and[4, 3]becomes 0.0384 instead of 0.96. - A high cosine is a hint, not proof. It says two texts are about similar things; it does not say the document answers the question.
- In JavaScript:
0 / 0does not raise an error in JavaScript. It givesNaN, which is not equal to anything, so the check fails without any message about a division. - In Python:
0 / 0raisesZeroDivisionErrorin Python, so a vector of zeros stops the run unless you check for it first.
Check yourself
Why divide the dot product by the two lengths?
To remove the effect of length. What is left depends only on the angle between the vectors: 1 for the same direction, 0 for unrelated, -1 for opposite.
What does the function return for a vector of all zeros, and why?
0. Its length is 0, so the division is not possible, and a vector with no direction is treated as unrelated.