04-03-02 — Similaridade: cosine, dot product, euclidean

⏱ 10 minFontes validadas em: 2026-04-29

TL;DR

Para RAG, use cosine similarity por padrão — mede o ângulo entre vetores, ignorando magnitude, o que funciona bem para texto. Use dot product quando os embeddings já estão normalizados (mesma coisa que cosine, mas mais rápido). Evite distância euclidiana para texto de alta dimensão — sofre com a maldição da dimensionalidade. Normalização importa: sempre normalize seus vetores ou use cosine.

O problema: como medir "proximidade" de vetores?

Após transformar texto em vetores de 1536 dimensões, precisamos de uma função matemática que diga "quão parecidos são esses dois vetores". Existem três candidatos principais, cada um com lógica diferente.

Cosine Similarity

Mede o ângulo entre dois vetores. O resultado vai de -1 (opostos) a +1 (idênticos). Score 0 significa ortogonais (sem relação).

Fórmula: cos(θ) = (A · B) / (|A| × |B|)

Intuição geométrica: imagine dois ponteiros saindo da origem. Cosine mede o ângulo entre eles — não importa o comprimento dos ponteiros, só a direção.

Por que funciona bem para texto: Dois documentos sobre o mesmo assunto, mesmo que um seja muito longo e outro curto, apontam na mesma direção no espaço vetorial. A magnitude (tamanho do vetor) reflete comprimento do texto, não relevância semântica.

import numpy as np

def cosine_similarity(a: list[float], b: list[float]) -> float:
    a, b = np.array(a), np.array(b)
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

# Score próximo de 1.0 = muito similar
# Score próximo de 0.0 = sem relação semântica
score = cosine_similarity(embedding_query, embedding_chunk)

Dot Product (Produto Escalar)

Mede o produto escalar direto entre dois vetores. Resultado é qualquer número real — sem limite superior.

Fórmula: A · B = Σ(aᵢ × bᵢ)

Quando usar: Quando os vetores estão normalizados (norma = 1). Nesse caso, dot product == cosine similarity, mas é computacionalmente mais barato (sem divisão pela norma).

💡 Insight

OpenAI e Cohere já retornam embeddings normalizados (norma L2 = 1). Isso significa que para esses modelos, dot product e cosine similarity são equivalentes. Azure AI Search usa dotProduct como padrão quando os vetores são normalizados — é ligeiramente mais rápido.

def dot_product_similarity(a: list[float], b: list[float]) -> float:
    a, b = np.array(a), np.array(b)
    return float(np.dot(a, b))
    # Válido como métrica de similaridade APENAS se ||a|| = ||b|| = 1

def is_normalized(v: list[float], tol: float = 1e-6) -> bool:
    return abs(np.linalg.norm(np.array(v)) - 1.0) < tol

Euclidean Distance (L2)

Mede a distância em linha reta entre dois pontos no espaço vetorial. Ao contrário das outras, menor = mais similar.

Fórmula: d(A,B) = √Σ(aᵢ - bᵢ)²

Problema em alta dimensão: Em espaços de 1000+ dimensões, a distância euclidiana entre pontos tende a convergir para o mesmo valor — fenômeno chamado de "curse of dimensionality". A diferença entre o vizinho mais próximo e o mais distante fica cada vez menor, tornando a métrica pouco discriminativa.

def euclidean_distance(a: list[float], b: list[float]) -> float:
    a, b = np.array(a), np.array(b)
    return float(np.linalg.norm(a - b))
    # MENOR = mais similar (ao contrário de cosine e dot)
⚠️ Evite euclidean para embeddings de texto

Em 1536 dimensões, euclidean distance sofre com a maldição da dimensionalidade. Use cosine. Exceção: se você trabalha com embeddings de imagens ou modelos específicos que recomendam L2 (ex: FAISS com L2).

Comparativo prático

MétricaRangeMais similarNormalização necessária?Quando usar
Cosine[-1, 1]Próximo de 1NãoPadrão para texto — robusto
Dot Product(-∞, ∞)Maior valorSim (ou explícita)Vetores normalizados, performance
Euclidean[0, ∞)Próximo de 0RecomendadaImagens, baixa dimensão

Normalização importa

Se seus embeddings não vêm normalizados (ex: modelos locais como BGE), normalize antes de indexar:

def normalize(embedding: list[float]) -> list[float]:
    v = np.array(embedding)
    norm = np.linalg.norm(v)
    if norm == 0:
        return embedding  # vetor zero — edge case
    return (v / norm).tolist()

# Normalizar antes de indexar E antes de buscar
chunk_vector = normalize(raw_embedding)
query_vector = normalize(raw_query_embedding)
🔗 Conexão com vector databases

Cada vector database tem sua nomenclatura: Qdrant usa Distance.COSINE, pgvector usa o operador <=> para cosine, Azure AI Search usa "metric": "cosine". A matemática é a mesma — o que muda é apenas a configuração do índice.

Como isso se conecta

Fontes

  1. Aggarwal, C. et al. (2001). On the surprising behavior of distance metrics in high dimensional space. ICDT. springer.com
  2. OpenAI. Embeddings — Which distance function should I use?. platform.openai.com
  3. Microsoft. Vector search — similarity metrics in Azure AI Search. learn.microsoft.com