Cosine similarity between all embeddings

Easy10 min10 hidden tests

A help center embeds each of its articles as a vector, and the docs team wants to find near-duplicate articles. Cosine similarity compares two vectors by their angle: 1 when they point the same way, 0 at right angles, -1 when opposite. Write cosine_similarity_matrix(E) that returns the similarity between every pair of rows of E. An article whose embedding is all zeros has no direction, so every similarity with it is 0.

Drag the two vectors

cos = a · b / (|a| |b|)
ab

Cosine 0.74 = 5.52 / (2.79 × 2.68). Stretch either vector along its own line and the cosine stays the same.

Angle 42 degrees, cosine 0.74.

Examples

Rows 0 and 2 are at right angles, so their similarity is 0

cosine_similarity_matrix([[1, 0], [1, 1], [0, 2]])

returns [[1, 0.707107, 0], [0.707107, 1, 0.707107], [0, 0.707107, 1]]

Row 1 is all zeros, and rows 0 and 2 point in opposite directions

cosine_similarity_matrix([[3, 4], [0, 0], [-6, -8]])

returns [[1, 0, -1], [0, 0, 0], [-1, 0, 1]]

Requirements

  • E is a 2-D list or array of shape (n, d), one embedding per row.
  • Return a float array of shape (n, n). Entry [i, j] is the dot product of rows i and j divided by the product of their lengths.
  • Any entry that involves an all-zero row is 0.0, including that row's own diagonal entry.
  • Use array operations instead of a Python loop over pairs.
The idea behind it

An embedding model turns text, images or products into vectors so that similar things land close together. The usual way to compare two embeddings is cosine similarity:

cos(a, b) = a . b / (|a| |b|)

It measures the angle between the vectors and ignores their length. A long document and a short one about the same topic can have very different lengths but point the same way. The result runs from -1 (opposite) through 0 (unrelated, at right angles) to 1 (same direction).

To compare every pair, scale each row to length 1 first. Then the dot product of two unit rows is their cosine, and one matrix product, U @ U.T, gives the whole table. Vector databases do the same thing, and many store vectors already normalized so a dot product is all a query needs. An all-zero vector has no direction, so cosine is undefined for it; returning 0 keeps it from matching anything.

pythonCtrl+Enter runs
import numpy as np

def cosine_similarity_matrix(E):
"""Cosine similarity between every pair of rows.

E: 2-D list or array of shape (n, d), one embedding per row
Returns a float array of shape (n, n) where entry [i, j] is
E[i] . E[j] / (|E[i]| * |E[j]|), and 0.0 when either row is all zeros.
"""
raise NotImplementedError

Stuck? Reveal one rung at a time

Worked solution

Give it a real try first. Then read the solution and why each line is there.