Cosine Similarity vs. Inner Product

黎 浩然/ 9 10 月, 2026/ 大语言模型/LARGELANGUAGEMODEL/LLM, 机器学习/MACHINELEARNING, 研究生/POSTGRADUATE, 计算机/COMPUTER/ 0 comments

Changing the search metric can change the top result for the same vectors. That need not indicate a broken index. Inner product depends on direction and magnitude; cosine similarity divides out magnitude. Equivalence requires explicit normalization assumptions.

Contents
  1. A two-dimensional counterexample
  2. What normalization changes
  3. L2 equivalence has its own condition
  4. Reproduce the calculation without a model
  5. Three checks before changing the metric
  6. References

This is a useful configuration check before RAG hybrid retrieval. 中文版

A two-dimensional counterexample

Let the query be q=(1,0), candidate A=(1,0), and B=(80,60). These invented teaching vectors do not represent actual text. A points in exactly the query direction. B has length 100 and a different direction.

The inner products are q·A=1 and q·B=80, so B ranks first. Cosine similarity is cos(q,d)=(q·d)/(‖q‖‖d‖). A scores 1 and B scores 0.8, placing A first. This demonstrates different objectives; it does not establish which is better for real semantic search.

Raw inner product: B wins
A: 1
B: 80
Cosine: A wins
A: 1
B: 0.8
Original score diagram for invented vectors. Panels use different scales; compare bars within each panel.

What normalization changes

For a nonzero vector, define x̂=x/‖x‖. Once both vectors have unit length, x̂·ŷ equals the original cosine similarity. The official Faiss guide maps cosine search to maximum inner product search by normalizing database and query vectors.

For a fixed nonzero query, changing only its magnitude leaves raw inner-product ordering unchanged: every score receives the same positive multiplier. Scores and thresholds still change. Candidate magnitudes vary, so this argument does not remove their influence. Normalize both sides consistently if returned values must represent cosine similarity.

L2 equivalence has its own condition

Expanding squared distance gives ‖x−y‖²=‖x‖²+‖y‖²−2x·y. Only when both vectors have unit length does it become 2−2x·y. Exact maximum-inner-product and minimum-squared-L2 ordering therefore agree on unit vectors; do not apply that conclusion to unnormalized vectors.

Quantity Preferred direction Unit-vector relation
Inner product Larger Equals cosine
Cosine similarity Larger Range −1 to 1
Squared L2 Smaller 2−2×inner product

Faiss returns squared Euclidean distance for L2. Taking a square root preserves ordering, but thresholds and displayed distances need to use the intended quantity.

Reproduce the calculation without a model

The following standard-library Python example was executed. It checks the ranking change, the unit-vector identity and zero-vector handling. It performs neither embedding inference nor a Faiss index search and is not a retrieval benchmark.

from math import sqrt, isclose

def dot(a, b):
    if len(a) != len(b):
        raise ValueError('dimension mismatch')
    return sum(x*y for x,y in zip(a,b))

def unit(v):
    norm = sqrt(dot(v,v))
    if norm == 0:
        raise ValueError('zero vector has no cosine direction')
    return [x/norm for x in v]

q = [1., 0.]
docs = {'A': [1., 0.], 'B': [80., 60.]}
raw = {k: dot(q,v) for k,v in docs.items()}
cos = {k: dot(unit(q),unit(v)) for k,v in docs.items()}
assert raw == {'A': 1., 'B': 80.}
assert isclose(cos['A'], 1.) and isclose(cos['B'], .8)
for v in docs.values():
    x,y = unit(q),unit(v)
    squared_l2 = sum((a-b)**2 for a,b in zip(x,y))
    assert isclose(squared_l2, 2-2*dot(x,y), abs_tol=1e-12)
try:
    unit([0.,0.])
except ValueError:
    pass
else:
    raise AssertionError('zero vector must be rejected')
print('inner product:', raw)
print('cosine:', cos)
print('normalized squared L2:', {k:2-2*s for k,s in cos.items()})

Inner product produces A=1 and B=80; cosine produces A=1 and B=0.8. Normalized squared L2 is approximately 0 and 0.4. The printed 0.3999999999999999 is floating-point representation error, so the identity uses a tolerance. A zero vector has no defined cosine direction; this code rejects it explicitly. Production handling also needs an explicit policy.

Three checks before changing the metric

First, check the embedding model’s intended scoring method and whether its outputs are already normalized. Magnitude may carry information by design; normalization is not automatically a harmless transformation for every model.

Second, check that indexing and querying share model version, dimensions and preprocessing. Normalizing new queries while keeping non-unit document vectors does not generally produce cosine scores. A change needs to account for existing indices and live thresholds.

Third, build a small exact baseline before comparing approximate search. Mathematical ordering equivalence does not guarantee identical lists under different approximate settings, quantization errors or tie-breaking rules. Separate score semantics from search error to distinguish configuration mistakes from approximate recall loss.

References

Faiss: MetricType and distances(2024-12-03)

Publication note: published as a catch-up on October 9, 2026 (Beijing time), retaining the originally scheduled 09:00 article date. This original mathematical example makes no model-performance claim.

Support

If this article helped you, you can support this site.

WeChat support QR code; click to enlarge
WeChat
Alipay support QR code; click to enlarge
Alipay
Buy Me a Coffee; support this site
Buy Me a Coffee

Click a QR code to enlarge. More options: support page。

Share this Post

Leave a Comment

您的邮箱地址不会被公开。 必填项已用 * 标注

*
*