Esta página explica as principais estratégias de otimização de desempenho para o MongoDB Vector Search e como as usamos para criar nosso benchmark. Para saber como interpretar esse guia, consulte Como usar esse benchmark.
Para nosso benchmark, usamos o conjunto de dados Amazon Reviews 2023, um enorme conjunto de dados de e-commerce que contém 571,54mi de análises em 33 categorias de produtos, representando interações de maio 1996 de a setembro de 2023. Com cerca de 48,2 milhões de itens exclusivos cobertos por essas revisões, ele fornece dados multimodais ricos, incluindo avaliações de usuários (classificações, texto, votos de utilidade), metadados de itens (descrições, preço, imagens) e gráficos de interação usuário-item. Analisamos subconjuntos do conjunto de dados de itens (5,5mi e 15,3mi) que contêm títulos e descrições, e usaram o modelo voyage-3-large da Voyage AI para incorporá-los usando a seguinte lógica:
source = "Item: Title: " + record["title"] + " Description: " + record["description"] source_embedding = vo.embed(source, model="voyage-3-large", input_type="document", output_dimension=2048)
A qualidade dos resultados para os filtros é determinada pelo cálculo da similaridade de Jaccard (intersection / expected number of results) usando os resultados de uma query ANN e os resultados float ENN exatos correspondentes para a mesma entrada de texto e número de vetores solicitados. Recall é calculado encontrando a interseção média em 50 queries de amostra que podem ser feitas em um conjunto de dados de e-commerce.
Observação
Para ver o código-fonte usado para benchmarking, bem como o código usado para incorporar o conjunto de dados de origem, consulte Repositório de testes de desempenho.
Fatores que afetam o desempenho
Esta seção descreve vários fatores que impacto o desempenho da Vector Search do MongoDB e como configuramos nosso benchmark para testá-los.
Quantização
A quantização reduz a precisão das incorporações vetoriais para diminuir o uso de memória e melhorar a velocidade da pesquisa, com concessões em termos de precisão da pesquisa.
Quantização escalar
A quantização escalar converte vetores de ponto flutuante de 32 bits em inteiros de 8 bits, alcançando uma redução de 4x no uso de memória. As comparações de vetores inteiros exigem menos tempo de computação em comparação com vetores de ponto flutuante e requerem menos recursos, mas podem resultar em uma penalidade na precisão da pesquisa.
Quantização binária
A quantização binária converte vetores em representações de 1 bit, alcançando uma redução de 32x no uso de memória. As comparações de vetores binários envolvem o cálculo da distância de Hamming e exigem ainda menos tempo de computação em comparação com vetores inteiros e menos recursos. No entanto, a penalidade na precisão da pesquisa ao passar de vetores float para vetores binários é tão significativa que, para compensar isso, adicionamos uma etapa de reavaliação, o que aumenta a latência. No momento da consulta, os principais numCandidates acumulados durante a pesquisa são reordenados por seus vetores de fidelidade completa no disco antes de gerar os principais resultados limit.
Dimensionalidade vetorial
Utilizamos o modelo voyage-3-large da Voyage AI para incorporar os tamanhos médio (5.5mi) e grande (15.3mi) de conjuntos de dados vetoriais. Escolhemos esse modelo de incorporação devido ao seu desempenho superior em muitos benchmarks de IR e porque ele é treinado com o Aprendizado de Representação Matryoshka e a quantização em mente. Portanto, ele apresenta um bom desempenho em dimensões menores com a quantização ativada, mesmo em volumes maiores de vetores.
Utilizamos a indexação em visualizações para criar campos adicionais que dividem as primeiras N dimensões do vetor com 2048 dimensões de origem, gerando vetores com 1024, 512 e 256 dimensões, e os indexamos como faríamos com o campo de origem.
Observação
Você deve usar a versão 8.1 do MongoDB ou posterior para criar um índice de pesquisa vetorial em uma exibição.
db.createView( "all_dims_amazon_dataset", "2048d_amazon_dataset", [ { $addFields: { "1024_embedding": { $slice: ["$embedding", 1024] }, "512_embedding": { $slice: ["$embedding", 512] }, "256_embedding": { $slice: ["$embedding", 256] } } } ] )
db.all_dims_amazon_dataset.createSearchIndex( "all_dims_vector_index", "vectorSearch", { "fields": [ { "numDimensions": 2048, "path": "embedding", // original 2048d embedding produced by voyage-3-large "quantization": "scalar", // adjust to binary when needed "similarity": "dotProduct", "type": "vector" }, { "numDimensions": 1024, "path": "1024_embedding", "quantization": "scalar", "similarity": "cosine", // sliced embeddings aren't normalized, so must use cosine "type": "vector" }, { "numDimensions": 512, "path": "512_embedding", "quantization": "scalar", "similarity": "cosine", "type": "vector" }, { "numDimensions": 256, "path": "256_embedding", "quantization": "scalar", "similarity": "cosine", "type": "vector" } ] } )
Semelhante a diferentes representações em cada posição, as várias dimensionalidades impactam a capacidade representativa de cada vetor. Consequentemente, você pode alcançar maior precisão com vetores de 2048d em comparação com vetores de 256d, especialmente quando mede em relação a uma linha de base de ponto flutuante de 2048d ENN.
Além de exigir mais armazenamento e memória, os vetores de dimensionamento mais alto são um pouco mais lentos para executar query em comparação com os vetores de dimensionamento inferior, mas isso é mitigado significativamente à medida que o MongoDB Vector Search aproveita as instruções SIMD ao realizar comparações vetoriais.
Filtragem
Também criamos uma definição de índice separada na coleção contendo todos os 15,3mi itens, que inclui filtros em dois campos para habilitar queries pré-filtradas nesse conjunto de dados.
db.large_amazon_dataset.createSearchIndex( "vectorSearch", "large_vector_index", { "fields": [ { "numDimensions": 2048, "path": "embedding", "quantization": "scalar", // adjust to binary when needed "similarity": "dotProduct", "type": "vector" }, { "path": "category", "type": "filter" }, { "path": "price", "type": "filter" } ] } )
Executamos queries de pesquisa vetoriais, tanto quanto com quanto sem filtro, no grande conjunto de dados indexados:
# unfiltered query query = [ { "$vectorSearch": { "index": "large_vector_index", "path": "embedding", "queryVector": embedding.tolist(), "limit": k, "numCandidates": candidates, } }, { "$project": {"embedding": 0} } ]
# filtered query query = [ { "$vectorSearch": { "index": "large_vector_index", "path": "embedding", "queryVector": embedding.tolist(), "limit": k, "numCandidates": candidates, "filter": {"$and": [{'price': {'$lte': 1000}}, {'category': {'$eq': "Pet Supplies"}}]} } }, { "$project": {"embedding": 0} } ]
Observação
Ambos os padrões de query excluem os campos incorporados na saída usando a etapa $project. Isso é sempre recomendado para reduzir a latência, a menos que você precise de incorporações nos seus resultados.
Configuração de nó de pesquisa
O desempenho do MongoDB Vector Search é dimensionado com nós de pesquisa dedicados, que lidam com computação vetorial separadamente do volume de trabalho do banco de dados principal e fazem uso eficiente de instâncias de hardware dedicadas. Todos os testes foram realizados usando um cluster de base M20, mas, dependendo do tipo de teste, reconfiguramos os nós de pesquisa usados para melhor se adequar ao nosso caso de teste. Todos os testes foram executados utilizando Nós de Pesquisa no Amazon Web Services us-east-1, com uma instância do EC2 também em us-east-1 fazendo solicitações. Há três tipos de nós de pesquisa que você pode provisionar na Amazon Web Services, que variam em termos de disco, RAM e vCPUs disponíveis:
Tipo de nó | Perfil de Recursos | Uso recomendado |
|---|---|---|
CPU baixa | Baixa proporção de disco para memória (~6:1), baixa vCPU | Bom ponto de partida para muitas cargas de trabalho vetoriais que não usam quantização. |
CPU alta | Alta proporção de disco para memória (~25:1), alta vCPU | Escolha de alto desempenho para cargas de trabalho com alta QPS ou cargas de trabalho que aproveitam a quantização |
Otimizado para armazenamento | Alta proporção de disco para memória (~25:1), baixa vCPU | Opção custo-efetiva para cargas de trabalho que utilizam quantização |
Dimensionamento do conjunto de dados da Amazon
Um vetor flutuante de 768dimensão ocupa ~3kb de espaço no disco. Esse requisito de recurso é dimensionado linearmente com o número de vetores e o número de dimensões de cada vetor: 1M 768d vetores ocupa ~3GB; 1M 1536d ocupa ~6GB.
Utilizando quantização, geramos vetores de representação que são mantidos na memória a partir dos vetores de alta fidelidade armazenados no disco. Isso reduz a quantidade de memória necessária em 3,75x para quantização escalar e 24x para quantização binária, mas aumenta a quantidade de disco necessária para armazenar os vetores não quantizados e quantizados.
1 vetor quantizado escalar 768d requer 0,8 kb de memória (3/3.75) e ~3,8 kb de disco (3 + 3/3.75). Considerando essas opções de hardware e os requisitos de recursos para quantização, selecionamos os seguintes níveis de nós de pesquisa para os diferentes casos de teste:
Caso de Teste | Recursos necessários (RAM, armazenamento) | Search Node Tier RAM, disk, vCPUs | Preço para nós 2x |
|---|---|---|---|
Conjunto de dados médio (5.5M vetores, todas as dimensões), quantização escalar | 22, 104.5 GB | S50-otimizado para armazenamento 32 GB, 843 GB, 4 vCPUs | $1.04/hr |
Conjunto de dados médio (5,5mi vetores, todas as dimensões), quantização binária | 3.43, 104.5 GB | S30-high-cpu 8 GB 213 GB 4 vCPUs | $0.24/hr |
Grande conjunto de dados (15,3mi vetores, 2048d), quantização escalar | 32.64, 155.04 GB | S50-otimizado para armazenamento 32 GB, 843 GB, 4 vCPUs | $1.04/hr |
Grande conjunto de dados (15,3mi vetores, 2048d), quantização binária | 5.1, 155.04 GB | S30-high-cpu 8 GB 213 GB 4 vCPUs | $0.24/hr |
binData Compactação de vetor
Para o grande conjunto de dados, utilizamos um recurso adicional chamado compactação vetorial, que reduz a pegada de cada vetor na coleção de origem em cerca de 60%. Isso acelera a etapa dentro de uma query quando os IDs são hidratados na coleção de origem, e essa é uma etapa recomendada para todas as grandes cargas de trabalho.
Concurrency
Avaliamos não apenas a latência da query serial, mas também a taxa de transferência/QPS quando 10 e 100 solicitações são emitidas simultaneamente.
Observação
O mecanismo recomendado para lidar com uma maior taxa de transferência é o dimensionamento horizontal do número de nós de pesquisa, o que não medimos nestes testes.
Fragmentação
Avaliamos o impacto da fragmentação do nosso cluster e da nossa coleção no campo _id sobre a taxa de transferência do sistema, com foco na simultaneidade de solicitações de 10 e 100 para o grande índice quantizado binário.