🚀 Prueba Zilliz Cloud, el Milvus completamente gestionado, gratis—¡experimenta un rendimiento 10 veces más rápido! Prueba Ahora>>

milvus-logo
LFAI
  • Home
  • Blog
  • Optimizar las bases de datos vectoriales y mejorar la IA generativa basada en RAG

Optimizar las bases de datos vectoriales y mejorar la IA generativa basada en RAG

  • Engineering
May 13, 2024
Cathy Zhang, Dr. Malini Bhandaru

Este post se publicó originalmente en el canal Medium de Intel y se vuelve a publicar aquí con permiso.


Dos métodos para optimizar tu base de datos vectorial cuando usas RAG

Foto de Ilya Pavlov en Unsplash

Por Cathy Zhang y la Dra. Malini Bhandaru Colaboradores: Lin Yang y Changyan Liu

Los modelos de IA generativa (GenAI), que se están adoptando de forma exponencial en nuestra vida cotidiana, se están mejorando mediante la generación aumentada por recuperación (RAG), una técnica utilizada para mejorar la precisión y la fiabilidad de las respuestas mediante la obtención de datos de fuentes externas. La RAG ayuda a un gran modelo lingüístico (LLM) normal a comprender el contexto y reducir las alucinaciones aprovechando una gigantesca base de datos no estructurados almacenados como vectores, una presentación matemática que ayuda a captar el contexto y las relaciones entre los datos.

Las RAG ayudan a recuperar más información contextual y, por tanto, a generar mejores respuestas, pero las bases de datos vectoriales en las que se basan son cada vez más grandes para ofrecer contenidos ricos a los que recurrir. Así como los LLM de billones de parámetros están en el horizonte, las bases de datos vectoriales de miles de millones de vectores no se quedan atrás. Como ingenieros de optimización, teníamos curiosidad por ver si podíamos aumentar el rendimiento de las bases de datos vectoriales, cargar los datos más rápidamente y crear índices más rápidamente para garantizar la velocidad de recuperación incluso cuando se añaden nuevos datos. Esto no sólo reduciría el tiempo de espera de los usuarios, sino que también haría que las soluciones de IA basadas en RAG fueran un poco más sostenibles.

En este artículo, aprenderás más sobre las bases de datos vectoriales y sus marcos de evaluación comparativa, conjuntos de datos para abordar diferentes aspectos y las herramientas utilizadas para el análisis del rendimiento: todo lo que necesitas para empezar a optimizar las bases de datos vectoriales. También compartiremos nuestros logros de optimización en dos soluciones populares de bases de datos vectoriales para inspirarle en su viaje de optimización del rendimiento y el impacto de la sostenibilidad.

Bases de datos vectoriales

A diferencia de las bases de datos relacionales o no relacionales tradicionales, en las que los datos se almacenan de forma estructurada, una base de datos vectorial contiene una representación matemática de elementos de datos individuales, denominada vector, construida mediante una función de incrustación o transformación. El vector suele representar características o significados semánticos y puede ser corto o largo. Las bases de datos vectoriales realizan la recuperación de vectores mediante la búsqueda de similitudes utilizando una métrica de distancia (donde más cerca significa que los resultados son más similares) como la similitud euclidiana, del producto punto o del coseno.

Para acelerar el proceso de recuperación, los datos vectoriales se organizan mediante un mecanismo de indexación. Algunos ejemplos de estos métodos de organización son las estructuras planas, el archivo invertido (IVF), los mundos pequeños navegables jerárquicos (HNSW) y el hashing sensible a la localidad (LSH), entre otros. Cada uno de estos métodos contribuye a la eficiencia y eficacia de la recuperación de vectores similares cuando es necesario.

Examinemos cómo se utilizaría una base de datos de vectores en un sistema GenAI. La Figura 1 ilustra tanto la carga de datos en una base de datos de vectores como su utilización en el contexto de una aplicación GenAI. Cuando se introduce un indicador, éste se somete a un proceso de transformación idéntico al utilizado para generar vectores en la base de datos. Este vector transformado se utiliza entonces para recuperar vectores similares de la base de datos de vectores. Estos elementos recuperados sirven esencialmente como memoria conversacional, proporcionando un historial contextual para las instrucciones, de forma similar a como funcionan los LLM. Esta función resulta especialmente ventajosa en el procesamiento del lenguaje natural, la visión por ordenador, los sistemas de recomendación y otros ámbitos que requieren comprensión semántica y correspondencia de datos. Su pregunta inicial se "fusiona" posteriormente con los elementos recuperados, proporcionando contexto y ayudando al LLM a formular respuestas basadas en el contexto proporcionado, en lugar de depender únicamente de sus datos de entrenamiento originales.

Figura 1. Arquitectura de la aplicación RAG. Arquitectura de una aplicación GAR.

Los vectores se almacenan e indexan para su rápida recuperación. Las bases de datos vectoriales son de dos tipos: bases de datos tradicionales que se han ampliado para almacenar vectores y bases de datos vectoriales específicas. Algunos ejemplos de bases de datos tradicionales que proporcionan soporte vectorial son Redis, pgvector, Elasticsearch y OpenSearch. Ejemplos de bases de datos vectoriales específicas son las soluciones propietarias Zilliz y Pinecone, y los proyectos de código abierto Milvus, Weaviate, Qdrant, Faiss y Chroma. Puedes obtener más información sobre bases de datos vectoriales en GitHub a través de LangChain y OpenAI Cookbook.

Examinaremos más detenidamente una de cada categoría, Milvus y Redis.

Mejorar el rendimiento

Antes de sumergirnos en las optimizaciones, repasemos cómo se evalúan las bases de datos vectoriales, algunos marcos de evaluación y las herramientas de análisis de rendimiento disponibles.

Métricas de rendimiento

Echemos un vistazo a las métricas clave que pueden ayudarle a medir el rendimiento de las bases de datos vectoriales.

  • La latencia de carga mide el tiempo necesario para cargar datos en la memoria de la base de datos vectorial y crear un índice. Un índice es una estructura de datos utilizada para organizar y recuperar datos vectoriales de forma eficiente basándose en su similitud o distancia. Los tipos de índices en memoria incluyen el índice plano, IVF_FLAT, IVF_PQ, HNSW, vecinos más cercanos escalables (ScaNN)y DiskANN.
  • Larecuperación es la proporción de coincidencias verdaderas, o elementos relevantes, encontrados en los K primeros resultados recuperados por el algoritmo de búsqueda. Los valores más altos indican una mejor recuperación de los elementos relevantes.
  • Consultas por segundo (QPS) es la velocidad a la que la base de datos vectorial puede procesar las consultas entrantes. Los valores más altos de QPS implican una mayor capacidad de procesamiento de consultas y un mayor rendimiento del sistema.

Marcos de evaluación comparativa

Figura 2. Marco de evaluación comparativa de bases de datos vectoriales Marco de evaluación comparativa de bases de datos vectoriales.

La evaluación comparativa de una base de datos vectorial requiere un servidor de bases de datos vectoriales y clientes. En nuestras pruebas de rendimiento, utilizamos dos conocidas herramientas de código abierto.

  • VectorDBBench: Desarrollada y de código abierto por Zilliz, VectorDBBench ayuda a probar diferentes bases de datos vectoriales con diferentes tipos de índices y proporciona una cómoda interfaz web.
  • vector-db-benchmark: Desarrollado y de código abierto por Qdrant, vector-db-benchmark ayuda a probar varias bases de datos vectoriales típicas para el tipo de índice HNSW. Ejecuta pruebas a través de la línea de comandos y proporciona un archivo Docker Compose __file para simplificar el inicio de los componentes del servidor.

Figura 3. Ejemplo de vector-db-benchmark Un ejemplo de comando vector-db-benchmark utilizado para ejecutar la prueba de referencia.

Pero el marco de referencia es sólo una parte de la ecuación. Necesitamos datos que ejerciten diferentes aspectos de la propia solución de base de datos vectorial, como su capacidad para manejar grandes volúmenes de datos, varios tamaños de vectores y la velocidad de recuperación.

Conjuntos de datos abiertos para ejercitar bases de datos vectoriales

Los grandes conjuntos de datos son buenos candidatos para probar la latencia de carga y la asignación de recursos. Algunos conjuntos de datos tienen datos de gran dimensión y son buenos para probar la velocidad de cálculo de la similitud.

Los conjuntos de datos van desde una dimensión de 25 a una dimensión de 2048. El conjunto de datos LAION, una colección de imágenes abierta, se ha utilizado para entrenar modelos neuronales profundos visuales y lingüísticos muy grandes, como los modelos generativos de difusión estable. El conjunto de datos de OpenAI de 5 millones de vectores, cada uno con una dimensión de 1536, fue creado por VectorDBBench ejecutando OpenAI en datos sin procesar. Dado que cada elemento del vector es de tipo FLOAT, sólo para guardar los vectores se necesitan aproximadamente 29 GB (5M * 1536 * 4) de memoria, además de una cantidad adicional similar para guardar índices y otros metadatos, lo que supone un total de 58 GB de memoria para las pruebas. Cuando utilice la herramienta vector-db-benchmark, asegúrese de que dispone de suficiente espacio en disco para guardar los resultados.

Para probar la latencia de carga, necesitábamos una gran colección de vectores, que deep-image-96-angular ofrece. Para probar el rendimiento de la generación de índices y el cálculo de similitudes, los vectores de alta dimensión proporcionan más tensión. Para ello, elegimos el conjunto de datos 500K de vectores de 1536 dimensiones.

Herramientas de rendimiento

Ya hemos visto cómo estresar el sistema para identificar métricas de interés, pero examinemos lo que ocurre a un nivel inferior: ¿cuán ocupada está la unidad de cálculo, el consumo de memoria, las esperas en los bloqueos, etc.? Esto nos da pistas sobre el comportamiento de la base de datos, especialmente útiles para identificar áreas problemáticas.

La utilidad top de Linux proporciona información sobre el rendimiento del sistema. Sin embargo, la herramienta perf de Linux proporciona un conjunto más profundo de información. Para obtener más información, también recomendamos leer los ejemplos de perf de Linux y el método de análisis de microarquitectura top-down de Intel. Otra herramienta más es Intel® vTune™ Profiler, que resulta útil a la hora de optimizar no solo las aplicaciones, sino también el rendimiento y la configuración del sistema para una variedad de cargas de trabajo que abarcan HPC, nube, IoT, medios, almacenamiento, etc.

Optimizaciones de bases de datos vectoriales Milvus

Veamos algunos ejemplos de cómo intentamos mejorar el rendimiento de la base de datos vectorial Milvus.

Reducción de la sobrecarga del movimiento de memoria en la escritura del búfer del datanode

Los proxies de la ruta de escritura de Milvus escriben datos en un corredor de registro a través de MsgStream. A continuación, los nodos de datos consumen los datos, convirtiéndolos y almacenándolos en segmentos. Los segmentos fusionarán los datos recién insertados. La lógica de fusión asigna un nuevo búfer para contener/mover tanto los datos antiguos como los nuevos datos que se van a insertar y, a continuación, devuelve el nuevo búfer como datos antiguos para la siguiente fusión de datos. El resultado es que los datos antiguos se hacen cada vez más grandes, lo que a su vez ralentiza el movimiento de los datos. Los perfiles de rendimiento muestran una elevada sobrecarga de esta lógica.

Figura 4. Fusión y movimiento de datos en el Fusionar y mover datos en la base de datos vectorial genera una sobrecarga de alto rendimiento.

Cambiamos la lógica del búfer de fusión para añadir directamente los nuevos datos a insertar en los datos antiguos, evitando asignar un nuevo búfer y mover los datos antiguos de gran tamaño. Los perfiles de rendimiento confirman que no hay sobrecarga en esta lógica. Las métricas de microcódigo metric_CPU operating frequency y metric_CPU utilization indican una mejora que es coherente con el hecho de que el sistema ya no tiene que esperar el movimiento largo de la memoria. La latencia de carga mejoró en más de un 60 por ciento. La mejora se recoge en GitHub.

Figura 5. Con menos copias vemos una mejora del rendimiento de más del 50 por ciento en la latencia de carga.

Creación de índices invertidos con menor sobrecarga de asignación de memoria

El motor de búsqueda de Milvus, Knowhere, emplea el algoritmo k-means de Elkan para entrenar datos de clúster para crear índices de archivos invertidos (IVF). Cada ronda de entrenamiento de datos define un recuento de iteraciones. Cuanto mayor sea el recuento, mejores serán los resultados del entrenamiento. Sin embargo, también implica que el algoritmo Elkan se llamará con más frecuencia.

El algoritmo Elkan se encarga de asignar y desasignar memoria cada vez que se ejecuta. En concreto, asigna memoria para almacenar la mitad del tamaño de los datos de la matriz simétrica, excluyendo los elementos diagonales. En Knowhere, la dimensión de la matriz simétrica utilizada por el algoritmo Elkan se establece en 1024, lo que resulta en un tamaño de memoria de aproximadamente 2 MB. Esto significa que para cada ronda de entrenamiento Elkan asigna y desasigna repetidamente 2 MB de memoria.

Los datos de perfiles de rendimiento indicaron una actividad frecuente de asignación de memoria de gran tamaño. De hecho, se activó la asignación del área de memoria virtual (VMA), la asignación de páginas físicas, la configuración del mapa de páginas y la actualización de las estadísticas del cgroup de memoria en el núcleo. Este patrón de gran actividad de asignación/desasignación de memoria puede, en algunas situaciones, agravar también la fragmentación de la memoria. Se trata de un impuesto importante.

La estructura IndexFlatElkan está específicamente diseñada y construida para soportar el algoritmo Elkan. En cada proceso de entrenamiento de datos se inicializará una instancia de IndexFlatElkan. Para mitigar el impacto en el rendimiento derivado de la frecuente asignación y desasignación de memoria en el algoritmo Elkan, hemos refactorizado la lógica del código, trasladando la gestión de memoria fuera de la función del algoritmo Elkan al proceso de construcción de IndexFlatElkan. Esto permite que la asignación de memoria se produzca sólo una vez durante la fase de inicialización, mientras que sirve a todas las llamadas posteriores a la función del algoritmo Elkan desde el proceso de formación de datos actual y ayuda a mejorar la latencia de carga en alrededor de un 3 por ciento. Encuentre el parche Knowhere aquí.

Aceleración de la búsqueda vectorial de Redis mediante Software Prefetch

Redis, un popular almacén tradicional de datos clave-valor en memoria, ha comenzado recientemente a admitir la búsqueda vectorial. Para ir más allá de un almacén típico de clave-valor, ofrece módulos de extensibilidad; el módulo RediSearch facilita el almacenamiento y la búsqueda de vectores directamente dentro de Redis.

Para la búsqueda de similitud vectorial, Redis admite dos algoritmos: fuerza bruta y HNSW. El algoritmo HNSW está diseñado específicamente para localizar de forma eficiente vecinos más cercanos aproximados en espacios de alta dimensión. Utiliza una cola prioritaria denominada candidate_set para gestionar todos los vectores candidatos para el cálculo de distancias.

Cada vector candidato contiene metadatos sustanciales además de los datos del vector. Como resultado, cuando se carga un candidato desde la memoria, se pueden perder datos de la caché, lo que provoca retrasos en el procesamiento. Nuestra optimización introduce la precarga por software para cargar proactivamente el siguiente candidato mientras se procesa el actual. Esta mejora se ha traducido en una mejora del rendimiento de entre el 2 y el 3 por ciento para búsquedas de similitud vectorial en una configuración de Redis de instancia única. El parche está en proceso de actualización.

Cambio en el comportamiento predeterminado de GCC para evitar penalizaciones por código ensamblador mixto

Para obtener el máximo rendimiento, las secciones de código de uso frecuente suelen escribirse a mano en ensamblador. Sin embargo, cuando diferentes segmentos de código son escritos por diferentes personas o en diferentes momentos, las instrucciones utilizadas pueden proceder de conjuntos de instrucciones en ensamblador incompatibles, como Intel® Advanced Vector Extensions 512 (Intel® AVX-512) y Streaming SIMD Extensions (SSE). Si no se compila adecuadamente, el código mezclado produce una penalización en el desempeño. Obtenga más información sobre la mezcla de instrucciones Intel AVX y SSE aquí.

Puede determinar fácilmente si está utilizando código ensamblador de modo mixto y no ha compilado el código con VZEROUPPER, incurriendo en la penalización de rendimiento. Se puede observar a través de un comando perf como sudo perf stat -e 'assists.sse_avx_mix/event/event=0xc1,umask=0x10/' <workload>. Si su sistema operativo no tiene soporte para el evento, utilice cpu/event=0xc1,umask=0x10,name=assists_sse_avx_mix/.

El compilador Clang por defecto inserta VZEROUPPER, evitando cualquier penalización de modo mixto. Sin embargo, el compilador GCC sólo inserta VZEROUPPER cuando se especifican los indicadores de compilador -O2 u -O3. Nos pusimos en contacto con el equipo de GCC y les explicamos el problema, y ahora, por defecto, manejan correctamente el código ensamblador de modo mixto.

Optimice sus bases de datos vectoriales

Las bases de datos vectoriales están desempeñando un papel integral en GenAI, y están creciendo cada vez más para generar respuestas de mayor calidad. Con respecto a la optimización, las aplicaciones de IA no difieren de otras aplicaciones de software en que revelan sus secretos cuando se emplean herramientas de análisis de rendimiento estándar junto con marcos de referencia y entradas de estrés.

Con estas herramientas, descubrimos trampas de rendimiento relacionadas con la asignación innecesaria de memoria, la no precarga de instrucciones y el uso de opciones incorrectas del compilador. Basándonos en nuestros hallazgos, hemos introducido mejoras en Milvus, Knowhere, Redis y el compilador GCC para ayudar a que la IA sea un poco más eficiente y sostenible. Las bases de datos vectoriales son una clase importante de aplicaciones que merecen tus esfuerzos de optimización. Esperamos que este artículo te ayude a empezar.

Like the article? Spread the word

Sigue Leyendo