Vetores e embeddings: entendendo a base da busca semântica
1. O que são vetores e embeddings?
Vetores são representações numéricas de dados não estruturados como texto, imagem e áudio. Um embedding é um vetor denso de números reais que captura o significado semântico de um objeto. Diferente da codificação one-hot — que cria vetores esparsos do tamanho do vocabulário —, embeddings densos comprimem a informação em poucas centenas de dimensões.
A propriedade fundamental dos embeddings é geométrica: palavras ou documentos com significados similares ficam próximos no espaço vetorial. Por exemplo, os vetores de "gato" e "felino" terão alta similaridade por cosseno, enquanto "gato" e "carro" estarão distantes.
# Exemplo conceitual de similaridade entre vetores
vetor_gato = [0.2, 0.8, 0.1, 0.5]
vetor_felino = [0.3, 0.7, 0.2, 0.4]
vetor_carro = [0.9, 0.1, 0.8, 0.2]
# Similaridade por cosseno entre gato e felino: ~0.95
# Similaridade por cosseno entre gato e carro: ~0.30
2. Como os embeddings são gerados?
Modelos clássicos como Word2Vec (2013), GloVe (2014) e FastText (2016) geram embeddings de palavras isoladas, sem contexto. Já modelos modernos baseados em transformers — como BERT (2018), Sentence-BERT (2019) e os embeddings da OpenAI — produzem representações contextuais, onde a mesma palavra pode ter vetores diferentes dependendo da frase.
O pipeline de geração segue três etapas:
1. Tokenização: o texto é dividido em tokens (palavras ou subpalavras)
2. Codificação: o modelo transformer processa os tokens e gera representações ocultas
3. Pooling: as representações dos tokens são combinadas (média, CLS token) para gerar um vetor único para a frase
# Exemplo de geração de embedding com Sentence-BERT
from sentence_transformers import SentenceTransformer
modelo = SentenceTransformer('all-MiniLM-L6-v2')
frase = "Inteligência artificial está transformando o mundo"
embedding = modelo.encode(frase)
print(f"Dimensão do embedding: {len(embedding)}")
# Saída: Dimensão do embedding: 384
3. A matemática por trás da busca semântica
A similaridade entre dois vetores é calculada principalmente por cosseno ou distância euclidiana. A similaridade por cosseno mede o ângulo entre vetores, ignorando a magnitude:
similaridade_cosseno(A, B) = (A · B) / (||A|| * ||B||)
A distância euclidiana mede a distância geométrica direta:
distancia_euclidiana(A, B) = sqrt(soma((A_i - B_i)^2))
Para embeddings normalizados (com norma igual a 1), o produto escalar equivale à similaridade por cosseno, simplificando os cálculos. A escolha da métrica depende do modelo: Sentence-BERT funciona melhor com cosseno, enquanto alguns modelos de recomendação preferem distância euclidiana.
# Cálculo prático de similaridade por cosseno
import numpy as np
def cosine_similarity(v1, v2):
produto_escalar = np.dot(v1, v2)
norma_v1 = np.linalg.norm(v1)
norma_v2 = np.linalg.norm(v2)
return produto_escalar / (norma_v1 * norma_v2)
v1 = np.array([0.1, 0.5, 0.3])
v2 = np.array([0.2, 0.4, 0.3])
print(f"Similaridade: {cosine_similarity(v1, v2):.4f}")
# Saída: Similaridade: 0.9759
4. Como funciona a busca semântica na prática
A busca semântica converte a consulta do usuário em embedding e encontra os vetores mais próximos no banco. Para grandes volumes, a busca exata (k-NN) é inviável — usa-se Aproximação de Vizinhos Mais Próximos (ANN).
Algoritmos populares de ANN incluem:
- HNSW (Hierarchical Navigable Small World): baseado em grafos, excelente precisão
- IVF (Inverted File Index): divide o espaço em clusters, rápido para milhões de vetores
- PQ (Product Quantization): comprime vetores para reduzir memória
Comparado à busca por palavra-chave (TF-IDF ou BM25), a busca semântica captura sinônimos e contexto. Enquanto BM25 exige a palavra exata "carro", a busca semântica encontra "automóvel" e "veículo".
# Exemplo de busca semântica com FAISS
import faiss
import numpy as np
# Criando índice HNSW
dimensao = 384
indice = faiss.IndexHNSWFlat(dimensao, 32) # 32 conexões por nó
# Adicionando embeddings (simulação)
documentos = np.random.random((1000, dimensao)).astype('float32')
indice.add(documentos)
# Consulta
consulta = np.random.random((1, dimensao)).astype('float32')
distancias, indices = indice.search(consulta, k=5)
print(f"Documentos mais similares: {indices[0]}")
5. Bancos de dados vetoriais e integração
pgvector é uma extensão do PostgreSQL que permite armazenar e buscar vetores diretamente no banco relacional. Ideal para quem já usa PostgreSQL e precisa de busca semântica sem infraestrutura extra.
-- Criando tabela com pgvector
CREATE EXTENSION vector;
CREATE TABLE documentos (
id SERIAL PRIMARY KEY,
conteudo TEXT,
embedding vector(384)
);
-- Busca por similaridade
SELECT conteudo, 1 - (embedding <=> '[0.1, 0.5, ...]') AS similaridade
FROM documentos
ORDER BY embedding <=> '[0.1, 0.5, ...]'
LIMIT 5;
Soluções especializadas como Pinecone, Weaviate, Qdrant e Milvus oferecem performance superior para milhões de vetores, com escalabilidade horizontal e filtros híbridos (metadados + vetores). O trade-off: bancos relacionais com pgvector são mais simples de manter, enquanto bancos vetoriais dedicados oferecem latência menor em grande escala.
6. Desafios reais em produção
Dimensionalidade do embedding: modelos como text-embedding-3-large da OpenAI geram vetores de 3072 dimensões. Armazenar 10 milhões desses vetores consome ~120 GB. Técnicas de redução de dimensionalidade (PCA, autoencoders) podem ajudar, mas perdem precisão.
Atualização de modelos: quando um modelo de embedding é atualizado, todos os vetores existentes precisam ser regenerados — processo caro e demorado. Estratégias comuns incluem versionamento de embeddings e re-embedding incremental.
Qualidade dos embeddings: modelos genéricos podem falhar em domínios específicos (medicina, direito). Fine-tuning com dados do domínio melhora significativamente a qualidade da busca.
# Exemplo de redução de dimensionalidade com PCA
from sklearn.decomposition import PCA
embeddings_originais = np.random.random((10000, 3072))
pca = PCA(n_components=256)
embeddings_reduzidos = pca.fit_transform(embeddings_originais)
print(f"Dimensão original: {embeddings_originais.shape[1]}")
print(f"Dimensão reduzida: {embeddings_reduzidos.shape[1]}")
7. Casos de uso além da busca textual
Recomendação de conteúdo: embeddings de filmes permitem recomendar títulos similares baseados em enredo, não apenas em gênero.
Detecção de duplicatas: artigos de notícias com mesmo conteúdo, mas escritos de forma diferente, geram embeddings próximos.
Embeddings multimodais: modelos como CLIP (OpenAI) e ImageBind (Meta) mapeiam texto, imagem e áudio no mesmo espaço vetorial. Uma foto de cachorro e a frase "cão feliz" podem ter alta similaridade.
# Exemplo conceitual: busca multimodal com CLIP
from PIL import Image
import clip
modelo, preprocess = clip.load("ViT-B/32")
# Codificando texto e imagem
texto = clip.tokenize(["um gato preto"]).cuda()
imagem = preprocess(Image.open("gato.jpg")).unsqueeze(0).cuda()
with torch.no_grad():
embedding_texto = modelo.encode_text(texto)
embedding_imagem = modelo.encode_image(imagem)
similaridade = torch.cosine_similarity(embedding_texto, embedding_imagem)
print(f"Similaridade texto-imagem: {similaridade.item():.4f}")
Referências
- Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks — Artigo seminal que explica a geração de embeddings de frases com transformers.
- FAISS: A Library for Efficient Similarity Search — Repositório oficial da biblioteca de busca vetorial aproximada desenvolvida pelo Facebook AI Research.
- pgvector: Open-source vector similarity search for PostgreSQL — Documentação oficial da extensão pgvector para armazenamento e busca de embeddings no PostgreSQL.
- OpenAI Embeddings Guide — Guia oficial da OpenAI sobre como usar seus modelos de embedding para busca semântica e classificação.
- HNSW Algorithm Explained — Artigo original que descreve o algoritmo Hierarchical Navigable Small World para busca aproximada de vizinhos mais próximos.
- CLIP: Learning Transferable Visual Models From Natural Language Supervision — Paper da OpenAI que introduz embeddings multimodais conectando texto e imagem no mesmo espaço vetorial.
- Milvus: Vector Database for Scalable Similarity Search — Documentação oficial do banco vetorial Milvus, com tutoriais sobre indexação e busca semântica.