BRAGA

02 Writing02-02 Notes02-02-169

Cosine Similarity

Published 16 Apr 20267 min read
Contents
  1. 01Deep Breakdown
  2. 02Flowchart
  3. 03Real-world use
  4. 04The formula, piece by piece
  5. 05The formula in words
  6. 06Critical Concepts
  7. 07Step-by-step calculation (tiny example)
  8. 08Code examples
SC(x,y)=x⋅y∥x∥∥y∥=∑xiyi∑xi2 ∑yi2S_C(x,y) = \frac{x \cdot y}{\|x\| \|y\|} = \frac{\sum x_i y_i}{\sqrt{\sum x_i^2}\,\sqrt{\sum y_i^2}}
(1)

Deep Breakdown

What it tells me (Situation Awareness): Cosine similarity measures the cosine of the angle between two vectors, ranging from −1-1 (opposite directions) to +1+1 (same direction), with 00 meaning orthogonal (no shared orientation). It is scale-invariant: SC(x,2x)=1S_C(x, 2x) = 1 because scaling does not change direction. In DA/DS: cosine similarity is the default similarity metric for text, embeddings, and sparse feature vectors, because documents of different lengths should be comparable by their topic profile (direction), not their word count (magnitude). It drives document search engines, recommendation systems, semantic similarity scoring, and NLP embedding space analysis.

Flowchart

Vectors x, y(e.g., TF-IDFvectors,embeddings)Compute dotproduct: x . y =sum_i xi * yiCompute norms:||x|| = sqrt(sumxi^2), ||y|| =sqrt(sum yi^2)Divide: S_C = (x. y) / (||x|| * ||y||)S_C in [-1, +1]S_C = 1: samedirection(identicalorientation)S_C = 0:orthogonal (noshared features)S_C = -1:oppositedirections

Real-world use

Where it is used

Text similarity (TF-IDF, BM25, bag-of-words vectors), sentence/document embedding comparison (BERT, SBERT, OpenAI embeddings), recommendation engines (user-item vectors), image retrieval (CNN feature vector similarity), and nearest-neighbor search in high-dimensional embedding spaces.

Problem it solves

Different documents or users may have very different total activity levels (word counts, interaction counts), making raw vector magnitude irrelevant. A short focused essay and a long essay on the same topic should be similarly close to a query. Cosine similarity ignores magnitude and compares only direction (topic profile, preference profile).

How it solves the problem

Dividing the dot product by the product of the norms normalizes both vectors to unit length: SC(x,y)=x^⋅y^S_C(x,y) = \hat{x} \cdot \hat{y} where x^=x/∥x∥\hat{x} = x/\|x\|. The dot product of unit vectors is exactly the cosine of the angle between them (from the definition x⋅y=∥x∥∥y∥cos⁡θx \cdot y = \|x\|\|y\|\cos\theta). Result: only the angle (direction) matters, not magnitude.

Example in a business

A DS builds a content recommendation engine. Each article is represented as a TF-IDF vector. For a user who just read article qq, the system computes cosine similarity between qq's TF-IDF vector and all other articles, then returns the top-5 most similar ones. Long articles and short articles on the same topic score nearly identically — which is the correct behavior for semantic similarity.

The formula, piece by piece

  • x⋅y=∑ixiyix \cdot y = \sum_i x_i y_i: dot product; measures shared "energy" across all dimensions
  • ∥x∥=∑ixi2\|x\| = \sqrt{\sum_i x_i^2}: L2L_2 norm (magnitude) of xx
  • ∥y∥=∑iyi2\|y\| = \sqrt{\sum_i y_i^2}: L2L_2 norm of yy
  • x⋅y∥x∥∥y∥\frac{x \cdot y}{\|x\|\|y\|}: dividing normalizes both vectors to unit length; the result is x^⋅y^\hat{x} \cdot \hat{y}
  • Range: SC∈[−1,+1]S_C \in [-1, +1] for real-valued vectors; SC∈[0,1]S_C \in [0, 1] for non-negative vectors (e.g., TF-IDF, counts)
  • SC=1S_C = 1: xx and yy are parallel (same direction, possibly different magnitudes)
  • SC=0S_C = 0: x⊥yx \perp y (no shared features in the same direction)
  • SC=−1S_C = -1: xx and yy are antiparallel (opposite directions)
  • Cauchy-Schwarz inequality ensures ∣x⋅y∣≤∥x∥∥y∥|x \cdot y| \leq \|x\|\|y\|, so SC∈[−1,1]S_C \in [-1,1]

The formula in words

"Normalize each vector to unit length (direction only), then take their dot product. The result is the cosine of the angle between them. Values near 1 mean they point in nearly the same direction (high similarity); near 0 means they share nothing; near -1 means they are opposite." Tiny example: x=(1,1,0)x = (1, 1, 0), y=(1,0,0)y = (1, 0, 0). x⋅y=1x \cdot y = 1. ∥x∥=2\|x\| = \sqrt{2}. ∥y∥=1\|y\| = 1. SC=1/2≈0.707S_C = 1/\sqrt{2} \approx 0.707. Angle =45°= 45°. ✓\checkmark

Critical Concepts

  • Scale invariance: SC(x,cx)=1S_C(x, cx) = 1 for any scalar c>0c > 0. Doubling word counts does not change topic similarity.
  • Not a proper distance metric: cosine similarity is a similarity, not a distance. Cosine distance =1−SC= 1 - S_C turns it into a dissimilarity (see Cosine Distance note).
  • Efficient computation via normalization: pre-normalizing all vectors to unit length reduces similarity computation to a dot product. For a query qq: similarities = X_normalized @ q_normalized in one matrix-vector multiply — this is how most ANN (approximate nearest neighbor) search works.
  • Degenerate case: if ∥x∥=0\|x\| = 0 or ∥y∥=0\|y\| = 0 (the zero vector), cosine similarity is undefined. Handle with a guard clause.
  • Connection to inner product geometry: SC=cos⁡θS_C = \cos\theta where θ\theta is the angle between xx and yy — directly from x⋅y=∥x∥∥y∥cos⁡θx \cdot y = \|x\|\|y\|\cos\theta.
  • Asymmetry of magnitudes: cosine similarity ignores how "confident" or "strong" either vector is.

In recommendation, a user who rated 1 movie and a user who rated 1000 movies may have identical cosine similarity to a query — but very different reliability. This is a known limitation.

Step-by-step calculation (tiny example)

Step 1: Compute the dot product x=(2,3,1)x = (2, 3, 1), y=(1,2,4)y = (1, 2, 4). x⋅y=2⋅1+3⋅2+1⋅4=2+6+4=12x \cdot y = 2\cdot1 + 3\cdot2 + 1\cdot4 = 2 + 6 + 4 = 12.

Step 2: Compute the norms ∥x∥=4+9+1=14≈3.742\|x\| = \sqrt{4+9+1} = \sqrt{14} \approx 3.742. ∥y∥=1+4+16=21≈4.583\|y\| = \sqrt{1+4+16} = \sqrt{21} \approx 4.583.

Step 3: Divide SC=12/(3.742×4.583)=12/17.146≈0.6995S_C = 12 / (3.742 \times 4.583) = 12 / 17.146 \approx 0.6995. Angle =arccos⁡(0.6995)≈45.6°= \arccos(0.6995) \approx 45.6°. Moderately similar. ✓\checkmark

Code examples

Beginner example (Python)

python
import numpy as np

def cosine_similarity(x: np.ndarray, y: np.ndarray) -> float:
    """Compute cosine similarity between non-zero vectors x and y."""
    norm_x = np.linalg.norm(x)
    norm_y = np.linalg.norm(y)
    if norm_x == 0.0 or norm_y == 0.0:
        raise ValueError("Cosine similarity undefined for zero vectors.")
    return float(np.dot(x, y) / (norm_x * norm_y))
x = np.array([2.0, 3.0, 1.0])
y = np.array([1.0, 2.0, 4.0])
z = np.array([4.0, 6.0, 2.0])   # z = 2x (same direction)
print(f"S_C(x, y) = {cosine_similarity(x, y):.4f}")
print(f"S_C(x, z) = {cosine_similarity(x, z):.4f} (z = 2x, should be 1.0)")
print(f"Angle(x, y) = {np.degrees(np.arccos(cosine_similarity(x, y))):.2f} degrees")   # Using sklearn
from sklearn.metrics.pairwise import cosine_similarity as sk_cosine
print(f"\nsklearn cosine_similarity: {sk_cosine(x.reshape(1,-1), y.reshape(1,-1))[0,0]:.4f}")

Beginner example (R)

r
# -- Cosine similarity ---
cosine_sim <- function(x, y) { norm_x <- sqrt(sum(x^2))
  norm_y <- sqrt(sum(y^2))
  if (norm_x == 0 || norm_y == 0) stop("Zero vector has undefined cosine similarity") sum(x * y) / (norm_x * norm_y)
}
x <- c(2, 3, 1)
y <- c(1, 2, 4)
z <- 2 * x  # same direction as x
cat(sprintf("S_C(x, y) = %.4f\n", cosine_sim(x, y)))
cat(sprintf("S_C(x, z) = %.4f (z = 2x, should be 1)\n", cosine_sim(x, z)))
cat(sprintf("Angle(x,y) = %.2f degrees\n", acos(cosine_sim(x, y)) * 180 / pi))

Professional example (Python)

python
import numpy as np
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
np.random.seed(42)   # -- Business scenario: TF-IDF cosine similarity document retrieval ---
DOCUMENTS = [ "machine learning model training data features", "deep learning neural network gradient descent", "data pipeline ETL transformation warehouse SQL", "customer churn prediction logistic regression model", "python pandas numpy data analysis visualization", "marketing campaign ROI conversion funnel analytics", "A/B testing hypothesis statistical significance p-value", "recommendation system collaborative filtering user item",
]
QUERY = "machine learning prediction model"
vectorizer = TfidfVectorizer()
X_tfidf = vectorizer.fit_transform(DOCUMENTS)
q_tfidf = vectorizer.transform([QUERY])   # Cosine similarities via dot product (vectors already normalized internally by sklearn)
# Manually: normalize then dot product
X_norms = np.linalg.norm(X_tfidf.toarray(), axis=1, keepdims=True)
X_unit = X_tfidf.toarray() / np.where(X_norms == 0, 1, X_norms)
q_arr = q_tfidf.toarray().flatten()
q_norm = np.linalg.norm(q_arr)
q_unit = q_arr / q_norm if q_norm > 0 else q_arr
similarities = X_unit @ q_unit   # all cosine similarities at once
results = pd.DataFrame({ "doc_id": range(len(DOCUMENTS)), "snippet": [d[:40] for d in DOCUMENTS], "cosine_sim": similarities.round(4),
}).sort_values("cosine_sim", ascending=False)
print(f"Query: '{QUERY}'\n")
print("Top matches by cosine similarity:")
print(results.to_string(index=False))

Source: domain knowledge

#linear-algebra#spatial-distance-norms-and-analytic-geometry#advanced-distance-and-similarity-metrics

Keyboard shortcuts

Single keys, ignored while typing in a field or with Cmd, Ctrl or Alt held.

0
Home
1 2 3 4
Work, Writing, Tools, About
j / k
Next / previous section on this page
n / p
Next / previous project or essay
u
Up one level
l
Deutsch
t
Light or dark theme
?
This list
Esc
Close