Pular para conteúdo

Capítulo 2.8 — Embedding dimensionality, Matryoshka e custo de retrieval em produção

🎯 Objetivo

Tratar dimensionalidade de embeddings como decisão arquitetural com efeitos em storage, RAM, latência, custo de indexação, qualidade de recall e isolamento multi-tenant. Estabelecer quando é seguro reduzir dimensão, quais métodos preservam ranking e como avaliar.

🧠 Conceito principal

Um embedding é um vetor d-dimensional. d (a "dimensão") é um hiperparâmetro do modelo de embedding (text-embedding-3-small, nomic-embed-text, bge-large etc.). Valores típicos vão de 256 a 4096+.

Cada vetor armazena d × bytes_per_value. Em produção, isso multiplica por:

  • número de chunks indexados (N);
  • número de cópias (réplicas, backups, índices paralelos);
  • estrutura ANN (HNSW costuma adicionar 30–60% de overhead);
  • número de tenants/coleções, se cada um tiver seu índice.

🧮 Aritmética básica

Para um corpus de N chunks com vetores de dimensão d em FP32:

storage_pesos = N × d × 4 bytes
RAM_ANN (HNSW) ≈ storage_pesos × (1.3 a 1.6) + overhead_links

Exemplo: 10 milhões de chunks, d = 1536, FP32 -> 10⁷ × 1536 × 4 = ~61 GB só de vetores; HNSW empurra para ~85–95 GB. Reduzir para d = 512 corta para ~20 GB; quantizar valores para int8 derruba para ~5 GB.

🧠 Caminhos para reduzir dimensão

Método O que faz Custo de aplicar Preserva ranking?
Truncamento simples Mantém primeiros k componentes Quase zero Em geral, não. Embedding clássico não é treinado para isso.
PCA / SVD Projeção linear sobre dataset representativo Médio (precisa amostra + recompute) Razoável para análise; piora ranking em corpus heterogêneo
Random projection Multiplica por matriz aleatória Baixo Aproximado (Johnson-Lindenstrauss); útil em pré-filtragem
Modelo treinado em baixa dimensão Treinar outro modelo diretamente em d menor Alto (treino/seleção de modelo) Bom, mas exige reembedding e reavaliação
Matryoshka Representation Learning (MRL) Modelo é treinado para que prefixos de d (ex.: 64, 128, 256, 512, 1024) também sejam semanticamente válidos Baixo na aplicação (basta truncar) Sim, dentro dos níveis treinados
Quantização de valor (FP32->FP16/INT8/binary) Reduz bits por componente Baixo a médio Bom em INT8; binary perde mais

🧠 Matryoshka Representation Learning

MRL (Kusupati et al., 2022) treina embeddings com uma loss que força cada prefixo do vetor a já ser uma representação útil. Resultado: o mesmo modelo serve embeddings de, por exemplo, 64, 128, 256, 512, 1024 e 1536 dimensões - basta truncar.

Implicações práticas:

  • Indexação multi-resolução. Você pode armazenar embeddings cheios e servir resultados rápidos com prefixos pequenos para uma primeira fase, e refinar com prefixos maiores nos top-k.
  • Custo controlado. Tenants menores ou tarefas menos críticas usam d menor; tarefas críticas usam d total.
  • Sem reembedding. Mudar d em produção não exige reindexar tudo se o modelo é Matryoshka - basta truncar e revalidar.

Implementações conhecidas: text-embedding-3-small/large da OpenAI suportam o parâmetro dimensions (encurtamento); nomic-embed-text v1.5 e posteriores e modelos de Sentence Transformers expõem variantes Matryoshka.

⚠️ Maturidade. MRL é técnica recente e amplamente adotada por provedores (OpenAI, Nomic, Cohere) e por Sentence Transformers. Não é universal - modelos antigos não são Matryoshka e truncar sai pior. Sempre verifique a documentação do modelo.

🏗️ Como isso aparece em produção

  • Storage e RAM. Em RAG com dezenas de milhões de chunks, dimensão é diretamente custo mensal. Reduzir de 1536 para 768 pode economizar digito significativo de infraestrutura.
  • Latência de retrieval. ANN com vetores menores é mais rápido (memória, cache da CPU, SIMD). Em HNSW, o tempo de busca cresce aproximadamente com O(d × log N × ef).
  • Custo de indexação inicial. Reindexar 100 M de chunks é caro; ter Matryoshka evita reembedding ao mudar dimensão.
  • Multi-tenant RAG. Tenants pequenos podem usar dimensão menor e índices compartilhados com filtro por tenant_id; tenants grandes podem ter índices dedicados em dimensão alta. Isolamento por tenant continua sendo controle obrigatório, não propriedade da dimensão.
  • Vector DB e ANN (HNSW/IVF). HNSW favorece recall com M e efSearch mais altos; IVF favorece escala com nprobe. Em ambos, dimensão maior custa mais, e o ganho de qualidade não é linear.
  • Hybrid search e reranker. Quando há reranker forte (cross-encoder), a dimensão dos embeddings pode ser menor sem grande perda - o reranker recupera precisão. Quando não há reranker, dimensão alta tem mais importância para top-k.
  • Metadata filtering. Filtros (tenant, idioma, jurisdição) são obrigatórios e independem de dimensão. Filtros ruins destroem qualquer ganho de embedding.

⚖️ Trade-offs

Eixo Subir d Reduzir d
Recall@k em corpus difícil Melhor (até saturação) Pior em modelos não-Matryoshka
Storage e RAM Pior linearmente Melhor linearmente
Latência ANN Pior Melhor
Custo de reembedding Alto Alto se trocar modelo
Robustez a chunk ruim Pouco efeito Pouco efeito
Robustez a query ambígua Pequeno efeito; reranker importa mais Pequeno efeito

🚨 Modos de falha

  • Truncar embeddings de modelo não treinado em Matryoshka e considerar recall similar.
  • Reduzir dimensão e medir só MRR ou NDCG em corpus pequeno; em produção, perda aparece em consultas raras e em multi-tenant.
  • Trocar modelo de embedding mantendo o índice antigo. Vetores antigos e novos coexistem e produzem ranking caótico.
  • Esquecer filtros por tenant: cross-tenant leakage independe de dimensão.
  • Avaliar dimensão sem reranker quando produção tem reranker, ou vice-versa.

🛡️ Controles e mitigações

  • Eval set de retrieval com golden queries por tenant, idioma e tipo de pergunta. Métricas: recall@k, MRR, NDCG, groundedness downstream.
  • Curva dimensão × qualidade. Avalie 256, 512, 768, 1024 e dimensão cheia no mesmo eval set. Procure a dimensão "joelho da curva".
  • Reindexação versionada. Embedding tem model_id + version + dimensions. Índice antigo é mantido até o novo passar nos gates.
  • Quantização de valor combinada com Matryoshka. Ex.: int8 × d=512 pode bater fp32 × d=1024 em qualidade-por-byte.
  • Filtros obrigatórios por tenant no nível do storage, não no prompt.

📈 Métricas

  • storage_per_million_chunks (GB).
  • ram_for_index (GB) por configuração ANN.
  • p95_retrieval_latency_ms por dimensão.
  • recall@k, mrr, ndcg por tenant/idioma.
  • groundedness e citation_coverage da resposta final (downstream).
  • cost_per_thousand_embeddings (provider ou self-host).
  • reembedding_cost projetado caso troque o modelo.

🧪 Como avaliar dimensão ideal

  1. Construa golden set representativo (centenas a milhares de queries).
  2. Embede o corpus na maior dimensão suportada.
  3. Avalie recall@k, MRR e NDCG truncando para 256, 512, 768, 1024 e dimensão cheia, com e sem reranker.
  4. Avalie groundedness na resposta final.
  5. Escolha a menor dimensão cuja qualidade não cai além de um delta aceito (ex.: ≤ 1 ponto absoluto de NDCG).
  6. Documente a decisão em ADR e fixe dimensions no contrato do índice.

📌 Checklist para escolher dimensão de embedding em produção

  • [ ] Modelo é Matryoshka? Em caso negativo, considerar que reduzir não é grátis.
  • [ ] Existe eval set por tenant/idioma com pelo menos centenas de queries?
  • [ ] Há curva dimensão × qualidade documentada?
  • [ ] Storage e RAM foram estimados para o crescimento previsto do corpus?
  • [ ] Reranker está no pipeline? A dimensão considerou esse efeito?
  • [ ] model_id + version + dimensions + index_version aparecem no tracing?
  • [ ] Filtros obrigatórios por tenant estão no storage, não no prompt?
  • [ ] Existe procedimento de reembedding com índice paralelo + dual-read?

🧰 Exemplos práticos relacionados (planejados)

  • EX-LLM-09 - comparação de dimensões (256/512/1024) com Matryoshka e reranker; curva qualidade × custo.
  • EX-LLM-10 - reembedding seguro com índice paralelo (dual-write + dual-read).

📚 Referências