Similaridade Cosseno
Visão geral
Similaridade do cosseno (cosine similarity) é uma das formas mais comuns de medir o quão semelhantes dois vetores são. Em IA/AM (AI/ML) ela é especialmente popular para comparar vetores de incorporação (embeddings) — representações vetoriais de palavras, frases, documentos, imagens, usuários etc. — porque foca na direção em vez da magnitude.
Intuitivamente, a similaridade do cosseno pergunta:
“Esses vetores apontam em uma direção semelhante?”
Isso a torna um ajuste natural para representações de alta dimensão em que o comprimento do vetor pode variar por motivos não relacionados à similaridade semântica (tamanho do documento, deriva da norma do embedding, confiança do modelo e assim por diante).
Para contexto sobre operações vetoriais como produto interno e normas, veja Vetores, Matrizes, Tensores.
Definição (produto interno normalizado)
Dados dois vetores (x, y \in \mathbb{R}^d), a similaridade do cosseno é definida como o produto interno normalizado pelas normas L2 dos vetores:
[ \mathrm{cos_sim}(x, y) = \frac{x \cdot y}{|x|_2 , |y|_2} ]
Onde:
- (x \cdot y = \sum_{i=1}^d x_i y_i) é o produto interno (dot product)
- (|x|2 = \sqrt{\sum{i=1}^d x_i^2}) é a norma euclidiana (L2)
Intervalo e interpretação
- Se ambos os vetores são não nulos, a similaridade do cosseno está em ([-1, 1]).
- (1): mesma direção (maximamente semelhante)
- (0): ortogonais (nenhuma similaridade direcional)
- (-1): direções opostas
Em muitas aplicações de AM (por exemplo, vetores TF‑IDF, embeddings baseados em ReLU ou outros atributos não negativos), os valores frequentemente ficam em ([0, 1]) porque os vetores raramente apontam em direções opostas.
Interpretação geométrica: similaridade pelo ângulo
Uma identidade-chave da álgebra linear relaciona o produto interno ao ângulo (\theta) entre vetores:
[ x \cdot y = |x|_2 , |y|_2 \cos(\theta) ]
Reorganizando, obtemos:
[ \cos(\theta) = \frac{x \cdot y}{|x|_2 , |y|_2} ]
Assim, a similaridade do cosseno é literalmente o cosseno do ângulo entre os vetores.
Por que similaridade baseada em ângulo é útil
A similaridade do cosseno é invariante a escala:
[ \mathrm{cos_sim}(\alpha x, y) = \mathrm{cos_sim}(x, y) \quad \text{para qualquer } \alpha > 0 ]
Isso importa porque, em muitas representações, a direção captura significado enquanto o comprimento pode variar por fatores não relacionados:
- Um documento mais longo tende a produzir um vetor de contagens brutas maior.
- Alguns modelos de embedding produzem normas correlacionadas com frequência ou confiança.
- A dinâmica de treinamento pode alterar normas mesmo que a semântica relativa permaneça.
A similaridade do cosseno “elimina” esse efeito de magnitude ao normalizar.
Similaridade do cosseno e normalização
Um passo prático comum é a normalização L2 (L2 normalization):
[ \hat{x} = \frac{x}{|x|_2}, \quad \hat{y} = \frac{y}{|y|_2} ]
Então a similaridade do cosseno se torna apenas um produto interno:
[ \mathrm{cos_sim}(x, y) = \hat{x} \cdot \hat{y} ]
Isso é extremamente útil em sistemas de produção (recuperação, ranqueamento, busca de vizinhos mais próximos), porque reduz “similaridade do cosseno” a um primitivo rápido e bem otimizado: multiplicação de matrizes (matrix multiplication) / produtos internos.
Relação com a distância euclidiana (na esfera unitária)
Quando vetores são normalizados em L2 ((|\hat{x}|_2 = |\hat{y}|_2 = 1)), a similaridade do cosseno e a distância euclidiana (Euclidean distance) estão fortemente conectadas:
[ |\hat{x} - \hat{y}|_2^2 = 2(1 - \hat{x}\cdot\hat{y}) = 2(1 - \mathrm{cos_sim}(x,y)) ]
Consequências:
- Para embeddings normalizados na unidade, maximizar a similaridade do cosseno é equivalente a minimizar a distância euclidiana.
- Muitos sistemas de vizinhos mais próximos aproximados (approximate nearest neighbor, ANN) podem usar qualquer uma das métricas depois que os vetores são normalizados.
Essa “geometria da esfera unitária” aparece em técnicas como clusterização esférica (spherical clustering) e aprendizado contrastivo de embeddings.
Distância do cosseno: relacionada, mas nem sempre uma métrica
As pessoas frequentemente transformam a similaridade do cosseno em uma “distância” de uma destas formas:
1) Distância do cosseno (heurística comum)
[ \mathrm{cos_dist}(x, y) = 1 - \mathrm{cos_sim}(x, y) ]
Isso é amplamente usado na prática, mas observe:
- Não é garantido que satisfaça a desigualdade triangular em geral, então nem sempre é uma métrica verdadeira.
- Ainda assim, se comporta bem em muitos cenários com embeddings, especialmente quando os vetores são normalizados e em geral ficam em uma região restrita.
2) Distância angular (mais fiel geometricamente)
[ \mathrm{ang_dist}(x, y) = \arccos(\mathrm{cos_sim}(x, y)) ]
Isso mede diretamente o ângulo em radianos. Às vezes é normalizada para ([0,1]) dividindo por (\pi). A distância angular costuma combinar melhor com “distância ao longo da esfera”, mas é mais cara por causa do arccos.
Por que a similaridade do cosseno é amplamente usada para embeddings
Embeddings (de métodos clássicos a aprendizado profundo) frequentemente são treinados de modo que similaridade semântica corresponda a ângulos pequenos:
- Vetores de palavras/documentos: TF‑IDF, embeddings no estilo word2vec, embeddings de sentenças
- Embeddings neurais: saídas de Redes Neurais (Neural Networks), especialmente codificadores como a Arquitetura Transformer (Transformer Architecture)
- Embeddings de recomendação: usuários/itens em um espaço latente
- Embeddings de visão: vetores de atributos de imagens (por exemplo, de modelos contrastivos)
Razões práticas pelas quais funciona bem
Robusta a diferenças de magnitude
- Comprimento do documento e contagem de tokens inflacionam normas.
- Normas de embeddings podem se correlacionar com frequência ou confiança.
- A similaridade do cosseno foca na direção (padrão relativo de atributos).
Funciona bem em alta dimensão
- Em espaços de alta dimensão, distâncias euclidianas brutas podem se tornar menos informativas (“concentração de distância”), dependendo da distribuição dos dados.
- Medidas baseadas em ângulo podem permanecer significativas quando vetores são comparáveis até um fator de escala.
Conveniência computacional
- Com vetores normalizados em L2, similaridade do cosseno = produto interno.
- Isso facilita computar similaridades em lote com multiplicação de matrizes em GPU/TPU.
Alinhamento com objetivos de treinamento
- Muitas perdas contrastivas e de estilo recuperação (por exemplo, InfoNCE) efetivamente otimizam produtos internos entre embeddings normalizados, o que é similaridade do cosseno.
Exemplos trabalhados
Exemplo 1: intuição simples em 2D
Seja:
[ x = (1, 0), \quad y = (0, 1) ]
Então:
- (x \cdot y = 0)
- (|x| = 1), (|y| = 1)
- (\mathrm{cos_sim}(x,y) = 0)
Esses vetores são ortogonais (separados por 90°): “direções não relacionadas”.
Se, em vez disso:
[ y = (2, 0) ]
Então:
- (x \cdot y = 2)
- (|x| = 1), (|y| = 2)
- (\mathrm{cos_sim}(x,y) = \frac{2}{1\cdot 2} = 1)
Mesmo que (y) seja “mais longo”, a similaridade do cosseno é 1 porque a direção coincide exatamente.
Exemplo 2: similaridade do cosseno em Python (NumPy)
import numpy as np
def cosine_similarity(x, y, eps=1e-12):
x = np.asarray(x, dtype=np.float64)
y = np.asarray(y, dtype=np.float64)
nx = np.linalg.norm(x)
ny = np.linalg.norm(y)
return float(np.dot(x, y) / (max(nx, eps) * max(ny, eps)))
a = np.array([1, 2, 3])
b = np.array([2, 4, 6]) # same direction as a
c = np.array([-1, 0, 1]) # different direction
print(cosine_similarity(a, b)) # close to 1.0
print(cosine_similarity(a, c)) # could be near 0 or negative depending on alignment
Detalhe-chave: o eps evita divisão por zero quando uma norma é extremamente pequena (mais sobre isso em “casos-limite de vetor zero”).
Exemplo 3: similaridade de documentos com TF‑IDF (scikit-learn)
Vetores TF‑IDF frequentemente são comparados com similaridade do cosseno porque contagens brutas escalam com o tamanho do documento.
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
docs = [
"transformers are neural networks for sequence modeling",
"sequence modeling with transformers and attention",
"I like cooking pasta"
]
X = TfidfVectorizer().fit_transform(docs)
S = cosine_similarity(X) # pairwise cosine similarities
print(S.round(3))
Em geral, você verá que os dois primeiros documentos têm similaridade maior entre si do que qualquer um deles com o documento sobre cozinhar.
Exemplo 4: recuperação sobre embeddings (normalizar e então produto interno)
Um padrão comum em busca semântica:
- Calcular embeddings para todos os itens (documentos).
- Normalizá-los em L2 uma vez e armazenar.
- Para uma consulta, gerar embedding + normalizar.
- Escores de similaridade = produtos internos.
import numpy as np
def l2_normalize(M, eps=1e-12):
norms = np.linalg.norm(M, axis=-1, keepdims=True)
return M / np.maximum(norms, eps)
# Suppose we have N document embeddings of dimension d
docs = np.random.randn(5, 4)
query = np.random.randn(4)
docs_n = l2_normalize(docs)
query_n = l2_normalize(query)
scores = docs_n @ query_n # cosine similarity for each doc
topk = np.argsort(-scores)[:3]
print(scores)
print("top docs:", topk)
Este é o mesmo truque que muitos bancos de dados vetoriais e bibliotecas de vizinhos mais próximos aproximados usam: a busca por similaridade do cosseno vira busca por produto interno após a normalização.
Notas práticas e armadilhas
Normalização L2: quando e onde aplicá-la
- Se você está comparando embeddings usando similaridade do cosseno repetidamente, geralmente é melhor armazenar embeddings normalizados.
- Se embeddings são produzidos dinamicamente dentro de um modelo, normalize-os no forward pass quando o objetivo de treinamento assume similaridade do cosseno / produto interno na esfera.
Em frameworks de aprendizado profundo (por exemplo, PyTorch), a normalização L2 é uma operação padrão (torch.nn.functional.normalize).
Casos-limite de vetor zero (importante)
A similaridade do cosseno é indefinida se qualquer um dos vetores for o vetor zero porque (|x|_2 = 0) causa divisão por zero.
Isso acontece na prática quando:
- Um documento não tem tokens no vocabulário (após filtragem/remoção de stopwords).
- Um vetor de atributos está inteiramente ausente.
- Algum pré-processamento produz um vetor todo zero (por exemplo, esparsificação agressiva).
Estratégias comuns:
- Retornar 0 de similaridade se qualquer norma for zero (tratar como “sem sinal de similaridade”).
- Adicionar um pequeno epsilon às normas (como no código acima) para evitar NaNs, mas tenha em mente que isso pode tornar resultados arbitrários para vetores quase zero.
- Filtrar vetores zero e tratá-los separadamente (frequentemente o melhor em pipelines de recuperação).
Estabilidade numérica e precisão
A similaridade do cosseno geralmente é estável, mas em sistemas de grande escala você pode encontrar:
- Problemas de precisão em Float16/BF16 ao computar normas/produtos internos para embeddings de alta dimensão.
- Erro de acumulação em produtos internos.
Mitigações:
- Computar a similaridade em float32 mesmo que embeddings estejam armazenados em float16.
- Usar normalização estável e considerar bibliotecas que tratam desses detalhes.
- Para mais sobre essas armadilhas, veja Álgebra Linear Numérica.
Vetores esparsos vs vetores densos
- Vetores esparsos (por exemplo, bag-of-words / TF‑IDF) se beneficiam de produtos internos esparsos e computações esparsas de norma.
- Embeddings densos se beneficiam de BLAS/multiplicação de matrizes em GPU.
A fórmula da similaridade do cosseno é idêntica; a implementação difere.
Similaridade do cosseno vs correlação (não confunda)
A similaridade do cosseno compara direções com respeito à origem.
A correlação de Pearson (Pearson correlation) entre dois vetores é a similaridade do cosseno após centralização pela média (mean-centering) (e escalonamento pelos desvios padrão). Se seus atributos têm deslocamentos/vieses, a correlação pode refletir melhor a covariação do que a orientação bruta.
Similaridades negativas são significativas
Uma similaridade do cosseno negativa significa que os vetores apontam, em grande medida, em direções opostas. Se isso é significativo depende da representação:
- Alguns embeddings (com componentes com sinal) conseguem expressar oposições de forma significativa.
- Muitos pipelines tratam valores negativos como “dissimilares” e podem truncar para 0, mas isso perde informação.
Quando a similaridade do cosseno pode ser uma má escolha
A similaridade do cosseno não é universalmente a melhor. Ela pode ser subótima quando:
- As magnitudes dos vetores carregam significado que você quer preservar (por exemplo, norma codifica certeza, popularidade, intensidade).
- Você precisa de uma métrica verdadeira com desigualdade triangular para certos algoritmos (embora muitos sistemas de ANN funcionem bem com similaridades não métricas).
- Sua representação não é naturalmente “direcional” (alguns espaços de atributos brutos se comportam melhor com distância euclidiana ou outras distâncias).
Aplicações em IA/AM
A similaridade do cosseno é amplamente usada em fluxos de trabalho modernos de AM:
- Busca semântica / recuperação: correspondência consulta-documento usando embeddings
- Clusterização: k-means esférico e outros métodos de clusterização baseados em ângulo
- Sistemas de recomendação: similaridade usuário–item em espaços de fatores latentes
- Detecção de duplicatas: documentos ou imagens quase duplicados
- Aprendizado contrastivo: produtos internos de embeddings normalizados como sinal de treinamento
- Classificação por vizinho mais próximo em espaço de embeddings (k-vizinhos mais próximos)
Em muitos desses casos, a similaridade do cosseno é combinada com técnicas como redução de dimensionalidade (por exemplo, via Autovalores e SVD) para acelerar a recuperação ou reduzir ruído em embeddings.
Resumo
- A similaridade do cosseno entre vetores não nulos (x) e (y) é: [ \frac{x \cdot y}{|x|_2|y|_2} ]
- Ela mede similaridade baseada em ângulo, em grande parte ignorando a magnitude do vetor.
- Com vetores normalizados em L2, a similaridade do cosseno é igual ao produto interno, permitindo recuperação eficiente em grande escala.
- A distância do cosseno frequentemente é definida como (1 - \mathrm{cos_sim}), enquanto a distância angular usa (\arccos).
- Atenção a vetores zero, escolhas de normalização e questões de precisão numérica em sistemas grandes/de alta dimensão.
Para as operações vetoriais fundamentais que sustentam a similaridade do cosseno, veja Vetores, Matrizes, Tensores, e para armadilhas numéricas em escala veja Álgebra Linear Numérica.