Chapter 4 · Part 2

Rank by cosine similarity

We have eight documents as points in space, and any query becomes a point too. Search is now a geometry question: which document point is closest to the query point? The standard way to measure that for embeddings is cosine similarity — how closely two vectors point in the same direction.

Cosine similarity runs from −1 to 1: 1 means the two vectors point the exact same way (same meaning), 0 means unrelated, negative means opposite. Direction is what matters, not length — which is exactly why we normalized every vector in Chapter 3.

Score the query against every document

Because the corpus is one 8 × 384 matrix and the query is one 384 vector, a single matrix–vector product scores the query against all eight documents at once. Add this to search.py:

search.py (continued)
import numpy as np

query = "I can't log into my account"
query_embedding = model.encode(query, normalize_embeddings=True)

# One dot product per document = cosine similarity for all of them at once.
scores = corpus_embeddings @ query_embedding   # shape (8,)

for doc, score in zip(corpus, scores):
  print(f"{score:.3f}  {doc}")

The @ is matrix multiplication: each row of corpus_embeddings (a document) is dotted with query_embedding, giving one score per document. Run it:

terminal
python search.py
0.531  How do I reset my password?
0.198  Where can I see my past orders?
0.161  What is your refund and return policy?
0.242  How do I update my shipping address?
0.129  Do you ship internationally?
0.334  How can I contact customer support?
0.286  How do I cancel my subscription?
0.147  What payment methods do you accept?

The query — "I can't log into my account" — contains none of the words reset, password, forgot. Yet the password-reset document scores highest by a clear margin. The engine matched on meaning. Now we just need to pick the winners and wrap it in a tidy function.