Para agentes de IA: hay un índice de documentación disponible en https://www.mongodb.com/es/docs/llms.txt — versiones en markdown de todas las páginas están disponibles agregando .md a cualquier ruta URL.
Make the MongoDB docs better! We value your opinion. Share your feedback for a chance to win $100.
MongoDB Branding Shape
Click here >
Docs Menu

Optimizar y medir el rendimiento

Las aplicaciones de IA a menudo pueden empezar siendo pequeñas en términos de computación, datos y costos monetarios. A medida que las aplicaciones de producción se escalan debido al aumento de la interacción de los usuarios, factores clave como el costo asociado con el almacenamiento y la recuperación de grandes volúmenes de datos se convierten en oportunidades críticas de optimización. Se pueden abordar estos desafíos centrándose en:

  • Algoritmos de búsqueda vectorial eficientes

  • Procesos automatizados de cuantización

  • Estrategias de integración optimizadas

Tanto el generación de recuperación aumentada (RAG) como los sistemas basados en agentes dependen de los datos vectoriales, que son representaciones numéricas de objetos de datos como imágenes, vídeos y texto, para realizar búsquedas de similitud semántica. Los sistemas que utilizan RAG o flujos de trabajo dirigidos por agentes deben gestionar de manera eficiente grandes conjuntos de datos de alta dimensión para mantener tiempos de respuesta rápidos, minimizar la latencia de recuperación y controlar los costos de infraestructura.

Este tutorial le dota con las técnicas necesarias para diseñar, implementar y gestionar cargas de trabajo avanzadas de IA a escala, asegurando un rendimiento óptimo y eficiencia de costos.

Específicamente, en este tutorial, aprenderás cómo:

  • Generar incrustaciones usando el voyage-3-large de Voyage IA, un modelo de incrustación multilingüe y de propósito general que también es consciente de la cuantización, e insertarlas en una base de datos de MongoDB.

  • Cuantificar automáticamente las incrustaciones (embeddings) a tipos de datos de menor precisión, optimizando tanto el uso de memoria como la latencia de la query.

  • Ejecuta una query que compare embeddings flotantes32, enteros8 y binarios, ponderando la precisión del tipo de dato frente a la eficiencia y la precisión de recuperación.

  • Mide la recuperación (también conocida como retención) de las incrustaciones cuantificadas, que evalúa cuán eficazmente la búsqueda ANN cuantificada recupera los mismos documentos que una búsqueda ENN de precisión completa.

Nota

  • La cuantificación binaria es óptima para escenarios que exigen reducir el consumo de recursos, aunque puede requerir una pasada de reajuste para corregir cualquier pérdida de precisión.

  • La cuantificación escalar ofrece un término medio práctico, adecuado para la mayoría de los casos de uso que necesitan equilibrar el rendimiento y la precisión.

  • Float32 asegura la máxima fidelidad, pero conlleva la mayor sobrecarga de rendimiento y memoria, lo que lo hace menos ideal para sistemas de gran escala o sensibles a la latencia.

Trabaja con una versión ejecutable de este tutorial como un cuaderno interactivo de Python.

Para completar este tutorial, debes tener lo siguiente:

  • Un clúster de M20 o superior con 2 o más nodos de búsqueda que utilizan High-CPU S20 o un nivel de búsqueda superior.

  • Un entorno para ejecutar cuadernos interactivos de Python como VS Code o Colab.

  • Una clave API de Voyage AI de pago. Una clave de nivel gratuito no admite el volumen de solicitudes requerido para este tutorial.

También recomendamos:

  • Un token de acceso a Hugging Face. Aunque no es obligatorio, usar un token de acceso de Hugging Face aumenta la velocidad de descarga de los conjuntos de datos de Wikipedia que usa este tutorial.
1
  1. Cree un cuaderno interactivo de Python guardando un archivo con la extensión .ipynb.

  2. Instala las librerías.

    Para este tutorial, debes importar las siguientes librerías:

    PyMongo

    driver de Python de MongoDB para conectarte al clúster, crear índices y ejecutar queries.

    voyageai

    Cliente de Voyage IA para Python para generar los embeddings para los datos.

    pandas

    Herramienta de manipulación y análisis de datos para cargar los datos y prepararlos para la búsqueda vectorial.

    conjuntos de datos

    librería de Hugging Face que proporciona acceso a conjuntos de datos listos para usar.

    matplotlib

    Librería de visualización y graficado para visualizar los datos.

    Para instalar las librerías, ejecuta lo siguiente:

    pip install --quiet -U pymongo voyageai pandas datasets matplotlib
  3. Obtenga y establezca de forma segura variables de entorno.

    La siguiente función asistente "set_env_securely" obtiene y establece variables de entorno de forma segura. Copia, pega y ejecuta el siguiente código y, cuando se te indique, establece valores secretos como tu clave de API de Voyage AI y la cadena de conexión de tu clúster. También se solicita un token de acceso opcional de Hugging Face. Proporcionar un token aumenta los límites de velocidad y acelera la descarga de conjuntos de datos. Presione Enter para saltar.

    1import getpass
    2import os
    3import voyageai
    4import huggingface_hub
    5
    6# Function to securely get and set environment variables
    7def set_env_securely(var_name, prompt):
    8 value = getpass.getpass(prompt)
    9 os.environ[var_name] = value
    10
    11# Environment Variables
    12set_env_securely("VOYAGE_API_KEY", "Enter your Voyage API Key: ")
    13set_env_securely("MONGO_URI", "Enter your MongoDB URI: ")
    14MONGO_URI = os.environ.get("MONGO_URI")
    15if not MONGO_URI:
    16 raise ValueError("MONGO_URI not set in environment variables.")
    17
    18# Voyage Client
    19voyage_client = voyageai.Client()
    20# Hugging Face Token (optional)
    21# Providing a token increases rate limits and speeds up dataset downloads.
    22# Press Enter to skip.
    23hf_token = getpass.getpass("Enter your Hugging Face token (optional, press Enter to skip): ")
    24if hf_token:
    25 os.environ["HF_TOKEN"] = hf_token
    26 huggingface_hub.login(hf_token)
    27 print("Hugging Face token set.")
    28else:
    29 print("No Hugging Face token provided. You may experience slower download speeds.")
2

En este paso, debes cargar hasta 250000 documentos de los siguientes conjuntos de datos:

Contiene los datos de Wikipedia con los embeddings de cada documento.

El conjunto de datos wikipedia-22-12-en-voyage-embed contiene fragmentos de artículos de Wikipedia con embarcamientos pre-generados en 1024punto flotante de dimensiones32 a partir del voyage-3-large modelo de Voyage IA. Esta es la colección de documentos principal con metadatos. Este conjunto de datos sirve como un compendio diverso de vectores para probar los efectos de la cuantificación de vectores en la búsqueda semántica. Cada documento de este conjunto de datos contiene los siguientes campos:

_id

El ObjectId ($oid) del documento.

id

El identificador único del documento.

title

El título del documento.

text

El texto del documento.

url

La URL del documento.

wiki_id

El ID de Wikipedia del documento.

views

El número de visualizaciones del documento.

paragraph_id

El ID del párrafo en el documento.

langs

El número de lenguajes en el documento.

embedding

Los vectores de embedding con 1024 dimensiones para el documento.

Contiene los datos de anotación para los datos de Wikipedia.

El conjunto de datos wikipedia-22-12-en-annotation contiene los datos de referencia anotados para la función de medición de recall. Estos datos se utilizan como conjunto de datos de referencia para la validación de exactitud y para evaluar el impacto de la cuantización en la calidad de recuperación. Cada documento en este conjunto de datos contiene los siguientes campos, que son la verdad fundamental utilizada para evaluar el rendimiento de la búsqueda vectorial:

_id

El ObjectId ($oid) del documento.

id

El identificador único del documento.

wiki_id

El ID de Wikipedia del documento.

queries

Las consultas que contienen las frases clave, preguntas, información parcial y oraciones para el documento.

queries.key_phrases

El arreglo de frases clave utilizadas para evaluar el rendimiento de la búsqueda vectorial para el documento.

queries.partial_info

El arreglo de información parcial que se utiliza para evaluar el rendimiento de la búsqueda vectorial del documento.

queries.questions

El arreglo de preguntas que se utilizan para evaluar el rendimiento de la búsqueda vectorial de documentos.

sentences

El arreglo de oraciones que se utilizan para evaluar el rendimiento de la búsqueda vectorial del documento.

  1. Define las funciones para cargar los datos en su clúster.

    Copia, pega y ejecuta el siguiente código en tu cuaderno de apuntes. El código de muestra define las siguientes funciones :

    • generate_bson_vector para convertir las incrustaciones en el conjunto de datos a BSON vectores binarios para el almacenamiento y procesamiento eficiente de sus vectores.

    • get_mongo_client para obtener su cadena de conexión de clúster.

    • insert_dataframe_into_collection para ingerir datos en el clúster.

    1import pandas as pd
    2from datasets import load_dataset
    3from bson.binary import Binary, BinaryVectorDtype
    4import pymongo
    5
    6# Connect to Cluster
    7def get_mongo_client(uri):
    8 """Connect to MongoDB and confirm the connection."""
    9 client = pymongo.MongoClient(uri)
    10 if client.admin.command("ping").get("ok") == 1.0:
    11 print("Connected to MongoDB successfully.")
    12 return client
    13 print("Failed to connect to MongoDB.")
    14 return None
    15
    16# Generate BSON Vector
    17def generate_bson_vector(array, data_type):
    18 """Convert an array to BSON vector format."""
    19 array = [float(val) for val in eval(array)]
    20 return Binary.from_vector(array, BinaryVectorDtype(data_type))
    21
    22# Load Datasets
    23def load_and_prepare_data(dataset_name, amount):
    24 """Load and prepare streaming datasets for DataFrame."""
    25 print(f"Loading {amount} records from {dataset_name}...")
    26 data = load_dataset(dataset_name, streaming=True, split="train").take(amount)
    27 return pd.DataFrame(data)
    28
    29# Insert datasets into MongoDB Collection
    30def insert_dataframe_into_collection(df, collection):
    31 """Insert Dataset records into MongoDB collection."""
    32 collection.insert_many(df.to_dict("records"))
    33 print(f"Inserted {len(df)} records into '{collection.name}' collection.")
  2. Carga los datos en el clúster.

    Copia, pega y ejecuta el siguiente código en tu cuaderno para cargar el conjunto de datos en el clúster. Este código realiza las siguientes acciones:

    • Recupera los conjuntos de datos.

    • Convierte los embeddings a formato BSON.

    • Crea colecciones en tu clúster e inserta los datos.

    1import pandas as pd
    2from bson.binary import Binary, BinaryVectorDtype
    3from pymongo.errors import CollectionInvalid
    4
    5wikipedia_data_df = load_and_prepare_data("MongoDB/wikipedia-22-12-en-voyage-embed", amount=250000)
    6wikipedia_annotation_data_df = load_and_prepare_data("MongoDB/wikipedia-22-12-en-annotation", amount=250000)
    7wikipedia_annotation_data_df.drop(columns=["_id"], inplace=True)
    8
    9# Convert embeddings to BSON format
    10wikipedia_data_df["embedding"] = wikipedia_data_df["embedding"].apply(
    11 lambda x: generate_bson_vector(x, BinaryVectorDtype.FLOAT32)
    12)
    13
    14# MongoDB Setup
    15mongo_client = get_mongo_client(MONGO_URI)
    16DB_NAME = "testing_datasets"
    17db = mongo_client[DB_NAME]
    18
    19collections = {
    20 "wikipedia-22-12-en": wikipedia_data_df,
    21 "wikipedia-22-12-en-annotation": wikipedia_annotation_data_df,
    22}
    23
    24# Create Collections and Insert Data
    25for collection_name, df in collections.items():
    26 if collection_name not in db.list_collection_names():
    27 try:
    28 db.create_collection(collection_name)
    29 print(f"Collection '{collection_name}' created successfully.")
    30 except CollectionInvalid:
    31 print(f"Error creating collection '{collection_name}'.")
    32 else:
    33 print(f"Collection '{collection_name}' already exists.")
    34
    35 # Clear collection and insert fresh data
    36 collection = db[collection_name]
    37 collection.delete_many({})
    38 insert_dataframe_into_collection(df, collection)
    Loading 250000 records from MongoDB/wikipedia-22-12-en-voyage-embed...
    Loading 250000 records from MongoDB/wikipedia-22-12-en-annotation...
    Connected to MongoDB successfully.
    Collection 'wikipedia-22-12-en' created successfully.
    Inserted 250000 records into 'wikipedia-22-12-en' collection.
    Collection 'wikipedia-22-12-en-annotation' created successfully.
    Inserted 87200 records into 'wikipedia-22-12-en-annotation' collection.

    IMPORTANTE: Puede tardar algún tiempo en convertir los embeddings en vectores BSON e incorporar los conjuntos de datos en su clúster.

  3. Verifica que los conjuntos de datos se hayan cargado correctamente iniciando sesión en tu clúster e inspeccionando visualmente las colecciones en Explorador de datos.

3

En este paso, se crean los tres índices siguientes en el campo embedding:

Índice cuantizado escalar

Para utilizar el método de cuantización escalar para cuantizar los embeddings.

Índice cuantizado binario

Utilizar el método de cuantificación binaria para cuantificar las incrustaciones.

Float32 ANN Index

Para utilizar el método float32 ANN para cuantificar las incrustaciones.

  1. Define la función para crear un índice de MongoDB Vector Search.

    Copia, pega y ejecuta lo siguiente en tu notebook:

    1import time
    2from pymongo.operations import SearchIndexModel
    3
    4def setup_vector_search_index(collection, index_definition, index_name="vector_index"):
    5 new_vector_search_index_model = SearchIndexModel(
    6 definition=index_definition, name=index_name, type="vectorSearch"
    7 )
    8
    9 # Create the new index
    10 try:
    11 result = collection.create_search_index(model=new_vector_search_index_model)
    12 print(f"Creating index '{index_name}'...")
    13
    14 # Wait for initial sync to complete
    15 print("Polling to check if the index is ready. This might take a few minutes.")
    16 predicate=None
    17 if predicate is None:
    18 predicate = lambda index: index.get("queryable") is True
    19 while True:
    20 indices = list(collection.list_search_indexes(result))
    21 if len(indices) and predicate(indices[0]):
    22 break
    23 time.sleep(5)
    24 print(f"Index '{index_name}' is ready for querying.")
    25 return result
    26
    27 except Exception as e:
    28 print(f"Error creating new vector search index '{index_name}': {e!s}")
    29 return None
  2. Definir los índices.

    Las siguientes configuraciones de índices implementan una estrategia diferente de cuantificación:

    vector_index_definition_scalar_quantized

    Esta configuración utiliza cuantificación escalar (int8), lo cual:

    • Reduce cada dimensión del vector de un número flotante de 32bits a un número entero de 8bits

    • Mantiene un buen equilibrio entre precisión y eficiencia de memoria

    • Es adecuado para la mayoría de los casos de uso en producción donde se necesita la optimización de memoria

    vector_index_definition_binary_quantized

    Esta configuración utiliza cuantificación binaria (int1), que:

    • Reduce cada dimensión del vector a un solo bit

    • Ofrece la máxima eficiencia de memoria

    • Es ideal para implementaciones a gran escala donde las limitaciones de memoria son críticas

    La cuantización automática se realiza de manera transparente cuando se crean estos índices, y la Búsqueda Vectorial de MongoDB gestiona la conversión de float32 al formato cuantizado especificado durante la creación de índices y las operaciones de búsqueda.

    La configuración del índice vector_index_definition_float32_ann indexa vectores de fidelidad total de 1024 dimensiones utilizando la función de similitud cosine.

    1# Scalar Quantization
    2vector_index_definition_scalar_quantized = {
    3 "fields": [
    4 {
    5 "type": "vector",
    6 "path": "embedding",
    7 "quantization": "scalar",
    8 "numDimensions": 1024,
    9 "similarity": "cosine",
    10 }
    11 ]
    12}
    13# Binary Quantization
    14vector_index_definition_binary_quantized = {
    15 "fields": [
    16 {
    17 "type": "vector",
    18 "path": "embedding",
    19 "quantization": "binary",
    20 "numDimensions": 1024,
    21 "similarity": "cosine",
    22 }
    23 ]
    24}
    25# Float32 Embeddings
    26vector_index_definition_float32_ann = {
    27 "fields": [
    28 {
    29 "type": "vector",
    30 "path": "embedding",
    31 "numDimensions": 1024,
    32 "similarity": "cosine",
    33 }
    34 ]
    35}
  3. Crea los índices escalar, binario y float32 utilizando la función setup_vector_search_index.

    1. Configura los nombres de la colección y los índices.

      wiki_data_collection = db["wikipedia-22-12-en"]
      wiki_annotation_data_collection = db["wikipedia-22-12-en-annotation"]
      vector_search_scalar_quantized_index_name = "vector_index_scalar_quantized"
      vector_search_binary_quantized_index_name = "vector_index_binary_quantized"
      vector_search_float32_ann_index_name = "vector_index_float32_ann"
    2. Crea los índices de MongoDB Vector Search.

      1setup_vector_search_index(
      2 wiki_data_collection,
      3 vector_index_definition_scalar_quantized,
      4 vector_search_scalar_quantized_index_name,
      5)
      6setup_vector_search_index(
      7 wiki_data_collection,
      8 vector_index_definition_binary_quantized,
      9 vector_search_binary_quantized_index_name,
      10)
      11setup_vector_search_index(
      12 wiki_data_collection,
      13 vector_index_definition_float32_ann,
      14 vector_search_float32_ann_index_name,
      15)
      Creating index 'vector_index_scalar_quantized'...
      Polling to check if the index is ready. This might take a few minutes.
      Index 'vector_index_scalar_quantized' is ready for querying.
      Creating index 'vector_index_binary_quantized'...
      Polling to check if the index is ready. This might take a few minutes.
      Index 'vector_index_binary_quantized' is ready for querying.
      Creating index 'vector_index_float32_ann'...
      Polling to check if the index is ready. This might take a few minutes.
      Index 'vector_index_float32_ann' is ready for querying.
      vector_index_float32_ann'

      IMPORTANTE: La operación podría tardar unos minutos en completarse. Los índices deben estar en estado Ready para usarlos en consultas.

    3. Verifica que la creación del índice sea exitosa ingresando a tu clúster e inspeccionando visualmente los índices en la página Search & Vector Search.

4

Este código define las siguientes funciones:

  • La función get_embedding() genera incrustaciones de 1024 dimensiones para el texto proporcionado utilizando el modelo de incrustación voyage-3-large de Voyage IA.

  • La función custom_vector_search toma los siguientes parámetros de entrada y devuelve los resultados de la operación de búsqueda vectorial.

    user_query

    string de texto de query para la que se desea generar incrustaciones.

    collection

    Colección de MongoDB para buscar.

    embedding_path

    Campo de la colección que contiene las incrustaciones.

    vector_search_index_name

    Nombre del índice a utilizar en la query.

    top_k

    Número de documentos principales en los resultados a devolver.

    num_candidates

    Número de candidatos a considerar.

    use_full_precision

    Marcar para realizar ANN, si False, o PEN, si True, búsqueda.

    El valor de use_full_precision se establece en False por defecto para una búsqueda ANN. Establece el valor de use_full_precision a True para realizar una búsqueda ENN.

    En concreto, esta función realiza las siguientes acciones:

    • Genera los embeddings para el texto de la query

    • Construye la etapa $vectorSearch

    • Configura el tipo de búsqueda

    • Especificar los campos de la colección a devolver

    • Ejecuta la pipeline después de reunir estadísticas de rendimiento

    • Devuelve los resultados

1def get_embedding(text, task_prefix="document"):
2 """Fetch embedding for a given text using Voyage AI."""
3 if not text.strip():
4 print("Empty text provided for embedding.")
5 return []
6 result = voyage_client.embed([text], model="voyage-3-large", input_type=task_prefix)
7 return result.embeddings[0]
8
9def custom_vector_search(
10 user_query,
11 collection,
12 embedding_path,
13 vector_search_index_name="vector_index",
14 top_k=5,
15 num_candidates=25,
16 use_full_precision=False,
17):
18 """Perform vector search on a MongoDB collection using specified index."""
19 # Generate embedding for the user query
20 query_embedding = get_embedding(user_query, task_prefix="query")
21
22 if query_embedding is None or not query_embedding:
23 return {
24 "error": "Invalid query or embedding generation failed.",
25 "execution_time_ms": None,
26 "results": [],
27 }
28
29 # Define the vector search stage
30 vector_search_stage = {
31 "$vectorSearch": {
32 "index": vector_search_index_name,
33 "queryVector": query_embedding,
34 "path": embedding_path,
35 "limit": top_k,
36 }
37 }
38
39 # Configure search precision approach
40 if not use_full_precision:
41 # For approximate nearest neighbor (ANN) search
42 vector_search_stage["$vectorSearch"]["numCandidates"] = num_candidates
43 else:
44 # For exact nearest neighbor (ENN) search
45 vector_search_stage["$vectorSearch"]["exact"] = True
46
47 # Project stage to fetch desired fields and vector search score
48 project_stage = {
49 "$project": {
50 "_id": 0,
51 "title": 1,
52 "text": 1,
53 "wiki_id": 1,
54 "url": 1,
55 "score": {"$meta": "vectorSearchScore"},
56 }
57 }
58
59 # Define the aggregate pipeline
60 pipeline = [vector_search_stage, project_stage]
61
62 try:
63 # Execute the explain command to measure latency
64 explain_result = collection.database.command(
65 "explain",
66 {"aggregate": collection.name, "pipeline": pipeline, "cursor": {}},
67 verbosity="executionStats",
68 )
69
70 # Extract the execution time
71 vector_search_explain = explain_result["stages"][0]["$vectorSearch"]
72 execution_time_ms = vector_search_explain["explain"]["query"]["stats"]["context"]["millisElapsed"]
73
74 # Execute the actual aggregate query
75 results = list(collection.aggregate(pipeline))
76
77 return {
78 "results": results,
79 "execution_time_ms": execution_time_ms,
80 }
81 except Exception as e:
82 print(f"Error during vector search: {e}")
83 return {
84 "error": str(e),
85 "execution_time_ms": None,
86 "results": [],
87 }
5

La siguiente query realiza búsquedas vectoriales a través de diferentes estrategias de cuantización, midiendo métricas de rendimiento para vectores cuantizados escalares, binarios y de precisión total (float32), mientras se capturan mediciones de latencia en cada nivel de precisión y se estandariza el formato de resultado para comparación analítica. Utiliza incrustaciones generadas con Voyage IA para el string de la query "¿Cómo puedo aumentar mi productividad para obtener el máximo resultado?".

La query almacena indicadores de rendimiento esenciales clave en la variable results, incluido el nivel de precisión (escalar, binario, flotante32), el tamaño del conjunto de resultados (top_k), la latencia de la query en milisegundos y el contenido del documento recuperado, lo que proporciona métricas completas para evaluar el rendimiento de las búsquedas en diferentes estrategias de cuantificación.

1vector_search_indices = [
2 vector_search_float32_ann_index_name,
3 vector_search_scalar_quantized_index_name,
4 vector_search_binary_quantized_index_name,
5]
6
7# Random query
8user_query = "How do I increase my productivity for maximum output"
9test_top_k = 5
10test_num_candidates = 25
11
12# Result is a list of dictionaries with the following headings: precision, top_k, latency_ms, results
13results = []
14
15for vector_search_index in vector_search_indices:
16 # Conduct a vector search operation using scalar quantized
17 vector_search_results = custom_vector_search(
18 user_query,
19 wiki_data_collection,
20 embedding_path="embedding",
21 vector_search_index_name=vector_search_index,
22 top_k=test_top_k,
23 num_candidates=test_num_candidates,
24 use_full_precision=False,
25 )
26 # Include the precision in the results
27 precision = vector_search_index.split("vector_index")[1]
28 precision = precision.replace("quantized", "").capitalize()
29
30 results.append(
31 {
32 "precision": precision,
33 "top_k": test_top_k,
34 "num_candidates": test_num_candidates,
35 "latency_ms": vector_search_results["execution_time_ms"],
36 "results": vector_search_results["results"][0], # Just taking the first result, modify this to include more results if needed
37 }
38 )
39
40# Conduct a vector search operation using full precision
41precision = "Float32_ENN"
42vector_search_results = custom_vector_search(
43 user_query,
44 wiki_data_collection,
45 embedding_path="embedding",
46 vector_search_index_name="vector_index_scalar_quantized",
47 top_k=test_top_k,
48 num_candidates=test_num_candidates,
49 use_full_precision=True,
50)
51
52results.append(
53 {
54 "precision": precision,
55 "top_k": test_top_k,
56 "num_candidates": test_num_candidates,
57 "latency_ms": vector_search_results["execution_time_ms"],
58 "results": vector_search_results["results"][0], # Just taking the first result, modify this to include more results if needed
59 }
60)
61
62# Convert the results to a pandas DataFrame with the headings: precision, top_k, latency_ms
63results_df = pd.DataFrame(results)
64results_df.columns = ["precision", "top_k", "num_candidates", "latency_ms", "results"]
65
66# To display the results:
67results_df.head()
precision top_k num_candidates latency_ms results
0 _float32_ann 5 25 1659.498601 {'title': 'Henry Ford', 'text': 'Ford had deci...
1 _scalar_ 5 25 951.537687 {'title': 'Gross domestic product', 'text': 'F...
2 _binary_ 5 25 344.585193 {'title': 'Great Depression', 'text': 'The fir...
3 Float32_ENN 5 25 0.231693 {'title': 'Great Depression', 'text': 'The fir...

Las métricas de rendimiento en los resultados muestran diferencias de latencia en los distintos niveles de precisión. Esto demuestra que, si bien la cuantización proporciona mejoras sustanciales en el rendimiento, existe una clara compensación entre la precisión y la velocidad de recuperación, ya que las operaciones de punto flotante de precisión completa32 requieren notablemente más tiempo de cálculo en comparación con sus equivalentes cuantizados.

6

La siguiente query introduce un marco de medición de la latencia sistemático que evalúa el rendimiento de la búsqueda vectorial en diferentes niveles de precisión y escalas de recuperación. El parámetro top-k no solo determina el número de resultados a devolver, sino que también establece el parámetro numCandidates en la búsqueda del grafo HNSW de MongoDB.

El valor numCandidates influye en cuántos nodos en el grafo HNSW explora MongoDB Vector Search durante la búsqueda ANN. Aquí, un valor más alto aumenta la probabilidad de encontrar los verdaderos vecinos más cercanos, pero requiere más tiempo de computación.

  1. Define la función para formatear el latency_ms en un formato legible por humanos.

    1from datetime import timedelta
    2
    3def format_time(ms):
    4 """Convert milliseconds to a human-readable format"""
    5 delta = timedelta(milliseconds=ms)
    6
    7 # Extract minutes, seconds, and milliseconds with more precision
    8 minutes = delta.seconds // 60
    9 seconds = delta.seconds % 60
    10 milliseconds = round(ms % 1000, 3) # Keep 3 decimal places for milliseconds
    11
    12 # Format based on duration
    13 if minutes > 0:
    14 return f"{minutes}m {seconds}.{milliseconds:03.0f}s"
    15 elif seconds > 0:
    16 return f"{seconds}.{milliseconds:03.0f}s"
    17 else:
    18 return f"{milliseconds:.3f}ms"
  2. Define la función para medir la latencia de la query de búsqueda vectorial.

    La siguiente función recibe como entrada un user_query, un collection, un vector_search_index_name, un valor use_full_precision, un valor top_k_values y un valor num_candidates_values, y devuelve los resultados de la búsqueda vectorial. Aquí, toma nota de lo siguiente:

    • La latencia aumenta a medida que aumentan los valores de top_k y num_candidates porque la operación de búsqueda vectorial utiliza un mayor número de documentos, lo que hace que la búsqueda tarde más.

    • La latencia es mayor en la búsqueda de fidelidad completa (use_full_precision=True) que en la búsqueda aproximada (use_full_precision=False), porque la búsqueda de fidelidad completa se demora más que la búsqueda aproximada, ya que busca en todo el conjunto de datos utilizando los vectores de coma flotante de máxima precisión32.

    • La latencia de la búsqueda cuantificada es menor que la de la búsqueda de fidelidad completa porque la búsqueda cuantificada utiliza la búsqueda aproximada y los vectores cuantizados.

    1def measure_latency_with_varying_topk(
    2 user_query,
    3 collection,
    4 vector_search_index_name="vector_index_scalar_quantized",
    5 use_full_precision=False,
    6 top_k_values=[5, 10, 100],
    7 num_candidates_values=[25, 50, 100, 200, 500, 1000, 2000, 5000, 10000],
    8):
    9 results_data = []
    10
    11 # Conduct vector search operation for each (top_k, num_candidates) combination
    12 for top_k in top_k_values:
    13 for num_candidates in num_candidates_values:
    14 # Skip scenarios where num_candidates < top_k
    15 if num_candidates < top_k:
    16 continue
    17
    18 # Construct the precision name
    19 precision_name = vector_search_index_name.split("vector_index")[1]
    20 precision_name = precision_name.replace("quantized", "").capitalize()
    21
    22 # If use_full_precision is true, then the precision name is "_float32_"
    23 if use_full_precision:
    24 precision_name = "_float32_ENN"
    25
    26 # Perform the vector search
    27 vector_search_results = custom_vector_search(
    28 user_query=user_query,
    29 collection=collection,
    30 embedding_path="embedding",
    31 vector_search_index_name=vector_search_index_name,
    32 top_k=top_k,
    33 num_candidates=num_candidates,
    34 use_full_precision=use_full_precision,
    35 )
    36
    37 # Extract the execution time (latency)
    38 latency_ms = vector_search_results["execution_time_ms"]
    39
    40 # Append the results to the list
    41 results_data.append(
    42 {
    43 "precision": precision_name,
    44 "top_k": top_k,
    45 "num_candidates": num_candidates,
    46 "latency_ms": latency_ms,
    47 }
    48 )
    49
    50 print(f"Top-K: {top_k}, NumCandidates: {num_candidates}, "
    51 f"Latency: {format_time(latency_ms)}, Precision: {precision_name}")
    52
    53 return results_data
  3. Ejecuta la consulta de búsqueda vectorial de MongoDB para medir la latencia.

    La operación de evaluación de latencia realiza un análisis integral del rendimiento ejecutando búsquedas a través de todas las estrategias de cuantización, probando múltiples tamaños de conjuntos de resultados, capturando métricas de rendimiento estandarizadas y agregando los resultados para un análisis comparativo, lo que permite una evaluación detallada del comportamiento de búsqueda vectorial bajo diferentes configuraciones y cargas de recuperación.

    1# Run the measurements
    2user_query = "How do I increase my productivity for maximum output"
    3top_k_values = [5, 10, 50, 100]
    4num_candidates_values = [25, 50, 100, 200, 500, 1000, 2000, 5000, 10000]
    5
    6latency_results = []
    7
    8for vector_search_index in vector_search_indices:
    9 latency_results.append(
    10 measure_latency_with_varying_topk(
    11 user_query,
    12 wiki_data_collection,
    13 vector_search_index_name=vector_search_index,
    14 use_full_precision=False,
    15 top_k_values=top_k_values,
    16 num_candidates_values=num_candidates_values,
    17 )
    18 )
    19
    20# Conduct vector search operation using full precision
    21latency_results.append(
    22 measure_latency_with_varying_topk(
    23 user_query,
    24 wiki_data_collection,
    25 vector_search_index_name="vector_index_scalar_quantized",
    26 use_full_precision=True,
    27 top_k_values=top_k_values,
    28 num_candidates_values=num_candidates_values,
    29 )
    30)
    Top-K: 5, NumCandidates: 25, Latency: 1.673s, Precision: _float32_ann
    ...
    Top-K: 100, NumCandidates: 10000, Latency: 184.905ms, Precision: _float32_ann
    Top-K: 5, NumCandidates: 25, Latency: 828.459ms, Precision: _scalar_
    ...
    Top-K: 100, NumCandidates: 10000, Latency: 214.200ms, Precision: _scalar_
    Top-K: 5, NumCandidates: 25, Latency: 400.160ms, Precision: _binary_
    ...
    Top-K: 100, NumCandidates: 10000, Latency: 360.909ms, Precision: _binary_
    Top-K: 5, NumCandidates: 25, Latency: 0.239ms, Precision: _float32_ENN
    ...
    Top-K: 100, NumCandidates: 10000, Latency: 0.179ms, Precision: _float32_ENN

    Las mediciones de latencia revelan una clara jerarquía de rendimiento entre los tipos de precisión, donde la cuantización binaria demuestra los tiempos de recuperación más rápidos, seguida de la cuantización escalar. Las operaciones de ANN de precisión completa float32 muestran latencias significativamente más altas. La diferencia de rendimiento entre las búsquedas cuantificadas y las de precisión total se acentúa a medida que aumentan los valores de Top-K. Las operaciones ENN flotante32 son las más lentas, pero proporcionan los resultados de mayor precisión.

  4. Representa la latencia de búsqueda frente a varios valores superiores k.

    1import matplotlib.pyplot as plt
    2
    3# Map your precision field to the labels and colors you want in the legend
    4precision_label_map = {
    5 "_scalar_": "scalar",
    6 "_binary_": "binary",
    7 "_float32_ann": "float32_ann",
    8 "_float32_ENN": "float32_ENN",
    9}
    10
    11precision_color_map = {
    12 "_scalar_": "orange",
    13 "_binary_": "red",
    14 "_float32_ann": "blue",
    15 "_float32_ENN": "purple",
    16}
    17
    18# Flatten all measurements and find the unique top_k values
    19all_measurements = [m for precision_list in latency_results for m in precision_list]
    20unique_topk = sorted(set(m["top_k"] for m in all_measurements))
    21
    22# For each top_k, create a separate plot
    23for k in unique_topk:
    24 plt.figure(figsize=(10, 6))
    25
    26 # For each precision type, filter out measurements for the current top_k value
    27 for measurements in latency_results:
    28 # Filter measurements with top_k equal to the current k
    29 filtered = [m for m in measurements if m["top_k"] == k]
    30 if not filtered:
    31 continue
    32
    33 # Extract x (num_candidates) and y (latency) values
    34 x = [m["num_candidates"] for m in filtered]
    35 y = [m["latency_ms"] for m in filtered]
    36
    37 # Determine the precision, label, and color from the first measurement in this filtered list
    38 precision = filtered[0]["precision"]
    39 label = precision_label_map.get(precision, precision)
    40 color = precision_color_map.get(precision, "blue")
    41
    42 # Plot the line for this precision type
    43 plt.plot(x, y, marker="o", color=color, label=label)
    44
    45 # Label axes and add title including the top_k value
    46 plt.xlabel("Number of Candidates")
    47 plt.ylabel("Latency (ms)")
    48 plt.title(f"Search Latency vs Num Candidates for Top-K = {k}")
    49
    50 # Add a legend and grid, then show the plot
    51 plt.legend()
    52 plt.grid(True)
    53 plt.show()

    El código devuelve los siguientes gráficos de latencia, que ilustran cómo funciona la recuperación de documentos de búsqueda vectorial con diferentes tipos de precisión de embedding, binario, escalar y float32, a medida que aumenta el top-k (el número de resultados recuperados):

    La latencia de búsqueda para cada tipo de precisión con las mediciones de los 5 mejores resultados.

    Captura de pantalla de la gráfica que muestra la latencia de búsqueda frente al número de candidatos para Top-K = 5
    haga clic para ampliar

    La latencia de búsqueda para cada tipo de precisión con las mediciones de los 10 mejores resultados.

    Captura de pantalla de la gráfica que muestra la latencia de búsqueda frente al número de candidatos para Top-K = 10
    haga clic para ampliar

    La latencia de búsqueda para cada tipo de precisión con las mediciones de los 50 mejores resultados.

    Captura de pantalla de la gráfica que muestra la latencia de búsqueda frente al número de candidatos para Top-K = 50
    haga clic para ampliar

    La latencia de búsqueda para cada tipo de precisión con las mediciones de los 100 mejores resultados.

    Captura de pantalla de la gráfica que muestra la latencia de búsqueda frente al número de candidatos para Top-K = 100
    haga clic para ampliar
7

La siguiente query mide cuán eficazmente MongoDB Vector Search recupera documentos relevantes del conjunto de datos de referencia. Se calcula como la proporción de documentos relevantes encontrados correctamente con respecto al número total de documentos relevantes en la verdad básica (Encontrados/Total). Por ejemplo, si una query tiene 5 documentos relevantes en el ground truth y MongoDB Vector Search encuentra 4 de ellos, el recall sería 0.8 o 80% .

  1. Define una función para medir la capacidad de representación y retención de la operación de búsqueda vectorial. Esta función realiza lo siguiente:

    1. Crea la búsqueda base utilizando los vectores flotantes32 de precisión total y la búsqueda ENN.

    2. Crea la búsqueda cuantificada utilizando los vectores cuantificados y la búsqueda ANN.

    3. Calcula la retención de la búsqueda cuantificada en comparación con la búsqueda de referencia.

    La retención debe mantenerse dentro de un rango razonable para la búsqueda cuantificada. Si la capacidad representacional es baja, significa que la operación de búsqueda vectorial no logra captar el significado semántico de la query y los resultados podrían no ser precisos. Esto indica que la cuantización no es efectiva y que el modelo de embedding inicial utilizado no es efectivo para el proceso de cuantización. Recomendamos utilizar modelos de embedding que sean conscientes de la cuantización, lo que significa que durante el proceso de entrenamiento, el modelo está específicamente optimizado para producir incrustaciones que mantengan sus propiedades semánticas incluso después de la cuantización.

    1def measure_representational_capacity_retention_against_float_enn(
    2 ground_truth_collection,
    3 collection,
    4 quantized_index_name, # This is used for both the quantized search and (with use_full_precision=True) for the baseline.
    5 top_k_values, # List/array of top-k values to test.
    6 num_candidates_values, # List/array of num_candidates values to test.
    7 num_queries_to_test=1,
    8):
    9 retention_results = {"per_query_retention": {}}
    10 overall_retention = {} # overall_retention[top_k][num_candidates] = [list of retention values]
    11
    12 # Initialize overall retention structure
    13 for top_k in top_k_values:
    14 overall_retention[top_k] = {}
    15 for num_candidates in num_candidates_values:
    16 if num_candidates < top_k:
    17 continue
    18 overall_retention[top_k][num_candidates] = []
    19
    20 # Extract and store the precision name from the quantized index name.
    21 precision_name = quantized_index_name.split("vector_index")[1]
    22 precision_name = precision_name.replace("quantized", "").capitalize()
    23 retention_results["precision_name"] = precision_name
    24 retention_results["top_k_values"] = top_k_values
    25 retention_results["num_candidates_values"] = num_candidates_values
    26
    27 # Load ground truth annotations
    28 ground_truth_annotations = list(
    29 ground_truth_collection.find().limit(num_queries_to_test)
    30 )
    31 print(f"Loaded {len(ground_truth_annotations)} ground truth annotations")
    32
    33 # Process each ground truth annotation
    34 for annotation in ground_truth_annotations:
    35 # Use the ground truth wiki_id from the annotation.
    36 ground_truth_wiki_id = annotation["wiki_id"]
    37
    38 # Process only queries that are questions.
    39 for query_type, queries in annotation["queries"].items():
    40 if query_type.lower() not in ["question", "questions"]:
    41 continue
    42
    43 for query in queries:
    44 # Prepare nested dict for this query
    45 if query not in retention_results["per_query_retention"]:
    46 retention_results["per_query_retention"][query] = {}
    47
    48 # For each valid combination of top_k and num_candidates
    49 for top_k in top_k_values:
    50 if top_k not in retention_results["per_query_retention"][query]:
    51 retention_results["per_query_retention"][query][top_k] = {}
    52 for num_candidates in num_candidates_values:
    53 if num_candidates < top_k:
    54 continue
    55
    56 # Baseline search: full precision using ENN (Float32)
    57 baseline_result = custom_vector_search(
    58 user_query=query,
    59 collection=collection,
    60 embedding_path="embedding",
    61 vector_search_index_name=quantized_index_name,
    62 top_k=top_k,
    63 num_candidates=num_candidates,
    64 use_full_precision=True,
    65 )
    66 baseline_ids = {
    67 res["wiki_id"] for res in baseline_result["results"]
    68 }
    69
    70 # Quantized search:
    71 quantized_result = custom_vector_search(
    72 user_query=query,
    73 collection=collection,
    74 embedding_path="embedding",
    75 vector_search_index_name=quantized_index_name,
    76 top_k=top_k,
    77 num_candidates=num_candidates,
    78 use_full_precision=False,
    79 )
    80 quantized_ids = {
    81 res["wiki_id"] for res in quantized_result["results"]
    82 }
    83
    84 # Compute retention for this combination
    85 if baseline_ids:
    86 retention = len(
    87 baseline_ids.intersection(quantized_ids)
    88 ) / len(baseline_ids)
    89 else:
    90 retention = 0
    91
    92 # Store the results per query
    93 retention_results["per_query_retention"][query].setdefault(
    94 top_k, {}
    95 )[num_candidates] = {
    96 "ground_truth_wiki_id": ground_truth_wiki_id,
    97 "baseline_ids": sorted(baseline_ids),
    98 "quantized_ids": sorted(quantized_ids),
    99 "retention": retention,
    100 }
    101 overall_retention[top_k][num_candidates].append(retention)
    102
    103 print(
    104 f"Query: '{query}' | top_k: {top_k}, num_candidates: {num_candidates}"
    105 )
    106 print(f" Ground Truth wiki_id: {ground_truth_wiki_id}")
    107 print(f" Baseline IDs (Float32): {sorted(baseline_ids)}")
    108 print(
    109 f" Quantized IDs: {precision_name}: {sorted(quantized_ids)}"
    110 )
    111 print(f" Retention: {retention:.4f}\n")
    112
    113 # Compute overall average retention per combination
    114 avg_overall_retention = {}
    115 for top_k, cand_dict in overall_retention.items():
    116 avg_overall_retention[top_k] = {}
    117 for num_candidates, retentions in cand_dict.items():
    118 if retentions:
    119 avg = sum(retentions) / len(retentions)
    120 else:
    121 avg = 0
    122 avg_overall_retention[top_k][num_candidates] = avg
    123 print(
    124 f"Overall Average Retention for top_k {top_k}, num_candidates {num_candidates}: {avg:.4f}"
    125 )
    126
    127 retention_results["average_retention"] = avg_overall_retention
    128 return retention_results
  2. Evalúa y compara el rendimiento de tus índices de MongoDB Vector Search.

    1# Access the database
    2DB_NAME = "testing_datasets"
    3db = mongo_client[DB_NAME]
    4
    5# Access collections
    6wiki_data_collection = db["wikipedia-22-12-en"]
    7ground_truth_collection = db["wikipedia-22-12-en-annotation"]
    8
    9overall_recall_results = []
    10top_k_values = [5, 10, 50, 100]
    11num_candidates_values = [25, 50, 100, 200, 500, 1000, 5000]
    12num_queries_to_test = 1
    13
    14for vector_search_index in vector_search_indices:
    15 overall_recall_results.append(
    16 measure_representational_capacity_retention_against_float_enn(
    17 ground_truth_collection=ground_truth_collection,
    18 collection=wiki_data_collection,
    19 quantized_index_name=vector_search_index,
    20 top_k_values=top_k_values,
    21 num_candidates_values=num_candidates_values,
    22 num_queries_to_test=num_queries_to_test,
    23 )
    24 )
    Loaded 1 ground truth annotations
    Query: 'What happened in 2022?' | top_k: 5, num_candidates: 25
    Ground Truth wiki_id: 69407798
    Baseline IDs (Float32): [52251217, 60254944, 64483771, 69094871]
    Quantized IDs: _float32_ann: [60254944, 64483771, 69094871]
    Retention: 0.7500
    ...
    Query: 'What happened in 2022?' | top_k: 5, num_candidates: 5000
    Ground Truth wiki_id: 69407798
    Baseline IDs (Float32): [52251217, 60254944, 64483771, 69094871]
    Quantized IDs: _float32_ann: [52251217, 60254944, 64483771, 69094871]
    Retention: 1.0000
    Query: 'What happened in 2022?' | top_k: 10, num_candidates: 25
    Ground Truth wiki_id: 69407798
    Baseline IDs (Float32): [52251217, 60254944, 64483771, 69094871, 69265870]
    Quantized IDs: _float32_ann: [60254944, 64483771, 65225795, 69094871, 70149799]
    Retention: 1.0000
    ...
    Query: 'What happened in 2022?' | top_k: 10, num_candidates: 5000
    Ground Truth wiki_id: 69407798
    Baseline IDs (Float32): [52251217, 60254944, 64483771, 69094871, 69265870]
    Quantized IDs: _float32_ann: [52251217, 60254944, 64483771, 69094871, 69265870]
    Retention: 1.0000
    Query: 'What happened in 2022?' | top_k: 50, num_candidates: 50
    Ground Truth wiki_id: 69407798
    Baseline IDs (Float32): [25391, 832774, 8351234, 18426568, 29868391, 52241897, 52251217, 60254944, 63422045, 64483771, 65225795, 69094871, 69265859, 69265870, 70149799, 70157964]
    Quantized IDs: _float32_ann: [25391, 8351234, 29868391, 40365067, 52241897, 52251217, 60254944, 64483771, 65225795, 69094871, 69265859, 69265870, 70149799, 70157964]
    Retention: 0.8125
    ...
    Query: 'What happened in 2022?' | top_k: 50, num_candidates: 5000
    Ground Truth wiki_id: 69407798
    Baseline IDs (Float32): [25391, 832774, 8351234, 18426568, 29868391, 52241897, 52251217, 60254944, 63422045, 64483771, 65225795, 69094871, 69265859, 69265870, 70149799, 70157964]
    Quantized IDs: _float32_ann: [25391, 832774, 8351234, 18426568, 29868391, 52241897, 52251217, 60254944, 63422045, 64483771, 65225795, 69094871, 69265859, 69265870, 70149799, 70157964]
    Retention: 1.0000
    Query: 'What happened in 2022?' | top_k: 100, num_candidates: 100
    Ground Truth wiki_id: 69407798
    Baseline IDs (Float32): [16642, 22576, 25391, 547384, 737930, 751099, 832774, 8351234, 17742072, 18426568, 29868391, 40365067, 52241897, 52251217, 52851695, 53992315, 57798792, 60163783, 60254944, 62750956, 63422045, 64483771, 65225795, 65593860, 69094871, 69265859, 69265870, 70149799, 70157964]
    Quantized IDs: _float32_ann: [22576, 25391, 243401, 547384, 751099, 8351234, 17742072, 18426568, 29868391, 40365067, 47747350, 52241897, 52251217, 52851695, 53992315, 57798792, 60254944, 64483771, 65225795, 69094871, 69265859, 69265870, 70149799, 70157964]
    Retention: 0.7586
    ...
    Query: 'What happened in 2022?' | top_k: 100, num_candidates: 5000
    Ground Truth wiki_id: 69407798
    Baseline IDs (Float32): [16642, 22576, 25391, 547384, 737930, 751099, 832774, 8351234, 17742072, 18426568, 29868391, 40365067, 52241897, 52251217, 52851695, 53992315, 57798792, 60163783, 60254944, 62750956, 63422045, 64483771, 65225795, 65593860, 69094871, 69265859, 69265870, 70149799, 70157964]
    Quantized IDs: _float32_ann: [16642, 22576, 25391, 547384, 737930, 751099, 832774, 8351234, 17742072, 18426568, 29868391, 40365067, 52241897, 52251217, 52851695, 53992315, 57798792, 60163783, 60254944, 62750956, 63422045, 64483771, 65225795, 65593860, 69094871, 69265859, 69265870, 70149799, 70157964]
    Retention: 1.0000
    Overall Average Retention for top_k 5, num_candidates 25: 0.7500
    ...

    La salida muestra los resultados de retención para cada query en el conjunto de datos de verdad fundamental. La retención se expresa como un decimal entre 0 y 1 donde 1.0 significa que se retienen los ID de verdad sobre el terreno y 0.25 significa que solo se retiene el 25% de los ID de verdad sobre el terreno.

  3. Plotea la capacidad de retención de los diferentes tipos de precisión.

    1import matplotlib.pyplot as plt
    2
    3# Define colors and labels for each precision type
    4precision_colors = {"_scalar_": "orange", "_binary_": "red", "_float32_": "green"}
    5
    6if overall_recall_results:
    7 # Determine unique top_k values from the first result's average_retention keys
    8 unique_topk = sorted(list(overall_recall_results[0]["average_retention"].keys()))
    9
    10 for k in unique_topk:
    11 plt.figure(figsize=(10, 6))
    12 # For each precision type, plot retention vs. number of candidates at this top_k
    13 for result in overall_recall_results:
    14 precision_name = result.get("precision_name", "unknown")
    15 color = precision_colors.get(precision_name, "blue")
    16 # Get candidate values from the average_retention dictionary for top_k k
    17 candidate_values = sorted(result["average_retention"][k].keys())
    18 retention_values = [
    19 result["average_retention"][k][nc] for nc in candidate_values
    20 ]
    21
    22 plt.plot(
    23 candidate_values,
    24 retention_values,
    25 marker="o",
    26 label=precision_name.strip("_"),
    27 color=color,
    28 )
    29
    30 plt.xlabel("Number of Candidates")
    31 plt.ylabel("Retention Score")
    32 plt.title(f"Retention vs Number of Candidates for Top-K = {k}")
    33 plt.legend()
    34 plt.grid(True)
    35 plt.show()
    36
    37 # Print detailed average retention results
    38 print("\nDetailed Average Retention Results:")
    39 for result in overall_recall_results:
    40 precision_name = result.get("precision_name", "unknown")
    41 print(f"\n{precision_name} Embedding:")
    42 for k in sorted(result["average_retention"].keys()):
    43 print(f"\nTop-K: {k}")
    44 for nc in sorted(result["average_retention"][k].keys()):
    45 ret = result["average_retention"][k][nc]
    46 print(f" NumCandidates: {nc}, Retention: {ret:.4f}")

    El código devuelve las gráficas de retención para lo siguiente:

    La retención para cada tipo de precisión con las mediciones para los 5 mejores resultados.

    Captura de pantalla de la gráfica que muestra Retención vs Núm. de candidatos para Top-K = 5
    haga clic para ampliar

    La retención para cada tipo de precisión con las mediciones para los 10 mejores resultados.

    Captura de pantalla de la gráfica que muestra Retención vs Núm. de candidatos para Top-K = 10
    haga clic para ampliar

    La retención para cada tipo de precisión con las mediciones para los 50 mejores resultados.

    Captura de pantalla de la gráfica que muestra Retención vs Núm. de candidatos para Top-K = 50
    haga clic para ampliar

    La retención para cada tipo de precisión con las mediciones para los 100 mejores resultados.

    Captura de pantalla de la gráfica que muestra Retención vs Núm. de candidatos para Top-K = 100
    haga clic para ampliar

    Para los embeddings float32_ann, scalar y binary, el código también devuelve resultados promedio detallados de retención similares a los siguientes:

    Detailed Average Retention Results:
    _float32_ann Embedding:
    Top-K: 5
    NumCandidates: 25, Retention: 1.0000
    NumCandidates: 50, Retention: 1.0000
    NumCandidates: 100, Retention: 1.0000
    NumCandidates: 200, Retention: 1.0000
    NumCandidates: 500, Retention: 1.0000
    NumCandidates: 1000, Retention: 1.0000
    NumCandidates: 5000, Retention: 1.0000
    Top-K: 10
    NumCandidates: 25, Retention: 1.0000
    NumCandidates: 50, Retention: 1.0000
    NumCandidates: 100, Retention: 1.0000
    NumCandidates: 200, Retention: 1.0000
    NumCandidates: 500, Retention: 1.0000
    NumCandidates: 1000, Retention: 1.0000
    NumCandidates: 5000, Retention: 1.0000
    Top-K: 50
    NumCandidates: 50, Retention: 0.8125
    NumCandidates: 100, Retention: 0.8750
    NumCandidates: 200, Retention: 0.8750
    NumCandidates: 500, Retention: 1.0000
    NumCandidates: 1000, Retention: 1.0000
    NumCandidates: 5000, Retention: 1.0000
    Top-K: 100
    NumCandidates: 100, Retention: 0.7586
    NumCandidates: 200, Retention: 0.7241
    NumCandidates: 500, Retention: 0.9655
    NumCandidates: 1000, Retention: 0.9655
    NumCandidates: 5000, Retention: 1.0000
    _scalar_ Embedding:
    Top-K: 5
    NumCandidates: 25, Retention: 0.2500
    NumCandidates: 50, Retention: 0.5000
    NumCandidates: 100, Retention: 0.7500
    NumCandidates: 200, Retention: 1.0000
    NumCandidates: 500, Retention: 1.0000
    NumCandidates: 1000, Retention: 1.0000
    NumCandidates: 5000, Retention: 1.0000
    Top-K: 10
    NumCandidates: 25, Retention: 0.4000
    NumCandidates: 50, Retention: 0.6000
    NumCandidates: 100, Retention: 0.8000
    NumCandidates: 200, Retention: 1.0000
    NumCandidates: 500, Retention: 1.0000
    NumCandidates: 1000, Retention: 1.0000
    NumCandidates: 5000, Retention: 1.0000
    Top-K: 50
    NumCandidates: 50, Retention: 0.7500
    NumCandidates: 100, Retention: 0.8125
    NumCandidates: 200, Retention: 0.8750
    NumCandidates: 500, Retention: 0.9375
    NumCandidates: 1000, Retention: 0.9375
    NumCandidates: 5000, Retention: 1.0000
    Top-K: 100
    NumCandidates: 100, Retention: 0.8276
    NumCandidates: 200, Retention: 0.8276
    NumCandidates: 500, Retention: 0.8621
    NumCandidates: 1000, Retention: 0.8966
    NumCandidates: 5000, Retention: 0.9310
    _binary_ Embedding:
    Top-K: 5
    NumCandidates: 25, Retention: 0.2500
    NumCandidates: 50, Retention: 0.2500
    NumCandidates: 100, Retention: 0.7500
    NumCandidates: 200, Retention: 0.7500
    NumCandidates: 500, Retention: 1.0000
    NumCandidates: 1000, Retention: 1.0000
    NumCandidates: 5000, Retention: 1.0000
    Top-K: 10
    NumCandidates: 25, Retention: 0.2000
    NumCandidates: 50, Retention: 0.2000
    NumCandidates: 100, Retention: 0.8000
    NumCandidates: 200, Retention: 0.8000
    NumCandidates: 500, Retention: 1.0000
    NumCandidates: 1000, Retention: 1.0000
    NumCandidates: 5000, Retention: 1.0000
    Top-K: 50
    NumCandidates: 50, Retention: 0.2500
    NumCandidates: 100, Retention: 0.5625
    NumCandidates: 200, Retention: 0.6250
    NumCandidates: 500, Retention: 0.7500
    NumCandidates: 1000, Retention: 0.8125
    NumCandidates: 5000, Retention: 1.0000
    Top-K: 100
    NumCandidates: 100, Retention: 0.4483
    NumCandidates: 200, Retention: 0.5517
    NumCandidates: 500, Retention: 0.7586
    NumCandidates: 1000, Retention: 0.8621
    NumCandidates: 5000, Retention: 1.0000

    Los resultados de recall demuestran patrones de rendimiento distintos entre los tres tipos de embeddings.

    La cuantificación escalar muestra una mejora constante, lo que indica una fuerte precisión de recuperación en valores más altos de K. La cuantificación binaria, aunque comienza más bajo, mejora en Top-K 50 y 100, sugiriendo una compensación entre eficiencia computacional y rendimiento de recuperación. Las incrustaciones Float32 demuestran el rendimiento inicial más sólido y alcanzan el mismo recall máximo que la cuantificación escalar en Top-K 50 y 100.

    Esto sugiere que mientras float32 proporciona una mejor memoria en valores Top-K más bajos, la cuantificación escalar puede lograr un rendimiento equivalente en valores Top-K más altos, ofreciendo al mismo tiempo una mayor eficiencia computacional. La cuantización binaria, a pesar de su menor umbral de recuperación, podría seguir siendo valiosa en escenarios donde las limitaciones de memoria y computación sean más importantes que la necesidad de la máxima precisión en la recuperación.