Inférence d’embeddings à grande échelle pour les applications RAG avec Ray Data et Milvus
La génération augmentée par récupération (RAG) est l’un des cas d’utilisation les plus populaires de l’IA générative en entreprise. La plupart des tutoriels RAG montrent comment utiliser l’API OpenAI à la fois pour le modèle d’embedding et l’inférence du grand modèle de langage (LLM). Pourquoi devriez-vous payer pour accéder à vos propres données, surtout pendant le processus de développement ? Vous pouvez accéder à vos propres données et itérer aussi rapidement que vous le souhaitez avec l’open source.
L’une des découvertes les plus intrigantes a été l’amélioration remarquable des performances obtenue avec Ray Data pendant l’étape d’embedding, où les données sont transformées en vecteurs.
L’exécution d’embeddings open source à l’aide de requêtes d’inférence par lots mutualisées avec des outils tels que Ray Data a permis d’économiser des ressources et du temps par rapport à Pandas. En utilisant seulement quatre workers sur un ordinateur portable Mac M2 avec 16 Go de RAM, Ray Data a été 60 fois plus rapide, plus de détails plus loin dans ce blog.
Notre stack RAG open source :
Nouveau modèle d’embedding BGM-M3 (générant 3 types de vecteurs en une seule passe : sparse, dense et multi-vector)
Ray Data pour une inférence d’embedding rapide et distribuée
AWS S3 pour stocker temporairement le résultat de l’inférence
Base de données vectorielle Milvus ou Zilliz Cloud
Données d’exemple téléchargées depuis Kaggle IMDB poster
Notre stack RAG open source
Modèle d’embedding BGM-M3
Embeddings puissants sparse, dense et multi-vector. Le modèle d’embedding BGE-M3 doit son surnom à ses capacités « multi » : multilinguisme, multifonctionnalité et multigranularité. Il peut fonctionner avec plus de 100 langues, calculer simultanément des embeddings pour les trois méthodes courantes de récupération : embeddings denses, sparse et multi-vector. Il fonctionne également avec différentes longueurs de texte, des phrases courtes aux documents longs (jusqu’à 8 192 tokens). Vous pouvez en apprendre davantage dans cet article, ou sur cette page HuggingFace à propos du modèle.
Depuis la version 2.4, Milvus dispose d’une prise en charge intégrée de BGE M3.
Ray Data
Des tâches de transformation de données de longue durée ?
Le traitement de données scalable de Ray Data rend plus facile et plus rapide le traitement en parallèle de très grands volumes de données sur plusieurs machines (CPU, GPU, etc.). Ray Data est particulièrement utile lorsque les données peuvent être réparties en processus parallèles, comme de nombreuses transformations simultanées de chunking et d’embedding ! Sous le capot, Ray Data dispose d’un puissant moteur d’exécution en streaming, afin de maximiser l’utilisation des GPU dans le cluster. Par rapport à l’exécution d’embeddings avec un service en ligne (comme l’API d’embedding OpenAI), l’exécution d’un job d’embedding hors ligne avec Ray Data peut permettre d’économiser la majorité des coûts.
Anyscale est une plateforme managée pour Ray. Vous pouvez facilement faire passer à l’échelle les jobs d’embedding sur Anyscale pour tirer parti de centaines de machines GPU.
Milvus et Zilliz
La recette secrète derrière une application RAG ultra-rapide est une base de données vectorielle puissante !_ Milvus est conçu pour gérer d’énormes quantités de données pour une utilisation à grande échelle par les entreprises. Contrairement à certaines bases de données vectorielles, Milvus peut évoluer de manière flexible à mesure que vos besoins en données augmentent – ses couches architecturales pour le stockage, l’indexation et les requêtes sont conçues pour monter en capacité indépendamment et/ou horizontalement. Cela rend votre application RAG rapide, car Milvus effectue intelligemment des calculs hors ligne avant et pendant que les requêtes arrivent en temps réel. De plus, Milvus est livré avec d’autres fonctionnalités importantes pour les entreprises, comme le maintien de la sécurité et de l’organisation de vos données (multi-tenancy et contrôle d’accès basé sur les rôles) et la garantie qu’elles soient toujours disponibles (haute disponibilité).
Zilliz est un produit cloud managé et utilise Milvus open source.
Configurer vos outils RAG
Nous utiliserons le SDK Python pour Milvus, Ray Data, Amazon S3 et Zilliz.
Pour Amazon S3, vous devrez créer un compte AWS.
Dans votre navigateur, accédez à console.aws.amazon.com > IAM > My security credentials > Create access key. Copiez et enregistrez localement, en lieu sûr, votre clé et votre clé secrète.
Installez les bibliothèques et exécutez aws config. Cela placera les variables AWS dans un fichier d’identifiants.
pip install boto3
pip install awscli –force-reinstall –upgrade
aws config #fill in your key and secret key
more ~/.aws/credentials #make sure this looks correct
Installez Ray Data :
pip install -U "ray[data]"
Installez Pymilvus :
pip install -U pymilvus "pymilvus[model]" langchain
Le modèle d’embedding BGE-M3 est déjà fourni avec Pymilvus depuis la v2.4.
import ray, os, pprint, time, boto3
from langchain.text_splitter import RecursiveCharacterTextSplitter
import numpy as np
import pymilvus
print(pymilvus.__version__) # must be >= 2.4.0
from pymilvus.model.hybrid import BGEM3EmbeddingFunction
Pour utiliser le niveau gratuit de Zilliz (jusqu’à 2 collections, 1 million de vecteurs chacune), inscrivez-vous pour créer un compte et créez un cluster Starter.
Préparer les données
Le code de ce blog utilise les célèbres données publiques Kaggle de posters IMDB. Elles contiennent environ 48 000 films, avis, liens vers les posters et davantage de métadonnées.
J’ai copié tous les champs textuels (nom du film, description, texte de l’avis) dans une nouvelle colonne appelée « text » et je l’ai enregistrée au format Parquet, car il est plus efficace que le CSV.
Générer des embeddings
Les étapes pour créer des embeddings sont les suivantes :
- Découper les données : diviser le texte d’entrée en morceaux, afin de conserver ensemble les parties de texte sémantiquement liées.
- Appeler un modèle d’embedding en mode inférence pour générer des représentations vectorielles des morceaux.
Ray Data est capable de paralléliser ces opérations sur les données en utilisant :
- flat_map() pour découper les données, puisque la sortie comportera plus de lignes que l’entrée.
- map_batches() pour appeler le modèle d’embedding depuis une méthode Class appelable.
chunk_size = 512
chunk_overlap = np.round(chunk_size * 0.10, 0)
# Define a LangChain text splitter.
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=chunk_overlap,
length_function=len) #len is a built-in Python function
# 1. Define a regular python function for chunking.
def chunk_row(row, splitter=text_splitter):
# Copy the row columns into metadata.
metadata = row.copy()
del metadata['text'] # Remove text from metadata
# Split the text into chunks.
chunks = splitter.create_documents(
texts=[row["text"]],
metadatas=[metadata])
chunk_list = [{
"text": chunk.page_content,
**chunk.metadata} for chunk in chunks]
return chunk_list
# 2. Define a class with a callable method to compute embeddings.
class ComputeEmbeddings:
def __init__(self):
# Initialize a Milvus built-in sparse-dense-late-interaction-reranking encoder.
# https://huggingface.co/BAAI/bge-m3
self.model = BGEM3EmbeddingFunction(use_fp16=False, device="cpu")
print(f"dense_dim: {self.model.dim['dense']}")
print(f"sparse_dim: {self.model.dim['sparse']}")
def __call__(self, batch):
# Ray data batch is a dictionary where values are array values.
# BGEM3EmbeddingFunction input is docs as a list of strings.
docs = list(batch['text'])
# Encode the documents. bge-m3 dense embeddings are already normalized.
embeddings = self.model(docs)
batch['vector_dense'] = embeddings['dense']
return batch
if __name__ == "__main__":
FILE_PATH = "s3://zilliz/kaggle_imdb.parquet"
# Load and transform data.
ds = ray.data.read_parquet(FILE_PATH)
# Découper le texte d’entrée en segments
chunked_ds = ds.flat_map(chunk_row)
# Calculer les embeddings avec une classe qui appelle le modèle d’embeddings.
embeddings_ds = chunked_ds.map_batches(ComputeEmbeddings, concurrency=4)
# Enregistrer les embeddings sur S3 dans un dossier de fichiers part parquet.
embeddings_ds.write_parquet('s3://zilliz/kaggle_imdb_embeddings')
Pour l’exécuter, vous le soumettrez en tant que tâche Ray :
Enregistrez le code dans un fichier de script Python. Je l’ai appelé ray_data_demo.py
Pour l’exécuter localement depuis votre ordinateur portable, créez un répertoire propre, avec uniquement le fichier de script .py et le fichier de données .parquet. Ne mettez que le strict minimum dans ce répertoire propre. J’ai appelé le mien « ray_cluster ».
Exécutez le script Python. Cela démarrera un cluster Ray et soumettra automatiquement une tâche.
**Accédez à
http://127.0.0.1:8265. Consultez les temps de Cluster et de Jobs.
Latence des embeddings - 60x plus rapide sur un ordinateur portable
| Approche | Taille des données d’entrée | Temps total | Capture d’écran |
| Pandas | 100 lignes | 23 s |
|
| Ray Data | 100 lignes | 50 s |
|
| Pandas | 45K lignes | >4 heures |
|
| Ray Data | 45K lignes | 4 min |
|
Tableau : Temps pour générer des embeddings de données sur un ordinateur portable M2 16 Go. Le traitement par lots Ray Data s’exécutait sur un cluster Ray à nœud unique, concurrency= 4 workers. Pandas était lent parce qu’il ne disposait que d’un seul processeur, tandis que Ray Data en avait 4. Les deux s’exécuteraient plus rapidement sur un cluster plus grand.
Insérer en masse les données avec embeddings depuis S3 directement dans Milvus ou Zilliz
Milvus et Zilliz proposent tous deux l’insertion en masse pour importer des données déjà intégrées directement depuis AWS, GCP ou Azure. En plus de la console web (illustrée ci-dessous), Zilliz propose également une API restful et un SDK.
Pour un grand corpus d’embeddings générés par lots, l’utilisation de l’import en masse peut permettre d’économiser considérablement les ressources machine et de réduire le temps d’insertion par rapport à l’insertion incrémentielle. Plus important encore, l’index de recherche vectorielle construit par import en masse est beaucoup plus efficace que celui issu de l’insertion incrémentielle (pensez à une optimisation globale vs. une optimisation locale).
Voyons comment effectuer facilement un import en masse en quelques clics simples sur la console web Zilliz Cloud. À partir du cluster dans lequel vous souhaitez créer la nouvelle collection, créez une nouvelle collection avec AutoID, uniquement la colonne « vector » avec le bon EMBEDDING_DIMENSION, utilisez l’option pratique « Dynamic Field », puis cliquez sur « Create Collection ».
Ensuite, cliquez sur « Importer des données » et suivez les instructions à l’écran pour copier le chemin vers les fichiers parquet écrits par la tâche Ray Data. (Notez que vous devez également spécifier la clé d’accès et la clé secrète si votre bucket S3 est privé, afin que Zilliz Cloud puisse lire les données qu’il contient). Les sources cloud Amazon S3, Google Cloud Storage ou Azure Blob Storage sont prises en charge. Cliquez sur « Importer » pour commencer à importer toutes les données dans la collection de la base de données vectorielle.
Une fois l’importation terminée, vous pouvez éventuellement cliquer sur la création d’index sur la collection pour rendre la recherche vectorielle plus efficace à l’étape Interroger vos données.
Image : capture d’écran des écrans d’insertion en masse de Zilliz.
Interroger vos données
Pour tester la collection nouvellement importée, posons une question et récupérons des réponses à partir de nos données de films.
def mc_run_search(question, output_fields, top_k=2, filter_expression=""):
# Embed the question using the same encoder.
embeddings = model_bgem3([question])
query_embeddings = embeddings['dense']
# Run semantic vector search using your query and the vector database.
results = mc.search(
COLLECTION_NAME,
data=query_embeddings,
search_params=SEARCH_PARAMS,
output_fields=output_fields,
# Milvus can utilize metadata in boolean expressions to filter search.
filter=filter_expression,
limit=top_k,
consistency_level="Eventually"
)
# Assemble retrieved context and context metadata.
# The search result is in the variable `results[0]`, which is type
# 'pymilvus.orm.search.SearchResult'.
METADATA_FIELDS = [f for f in output_fields if f != 'chunk']
formatted_results, context, context_metadata = _utils.client_assemble_retrieved_context(
results, metadata_fields=METADATA_FIELDS, num_shot_answers=top_k)
return formatted_results, context, context_metadata
SAMPLE_QUESTION = "muybridge horse movie"
# Return top k unique results with HNSW index.
TOP_K = 2
# Define output fields to return.
OUTPUT_FIELDS = ["movie_id", "chunk", "PosterLink"]
formatted_results, context, context_metadata = \
mc_run_search(SAMPLE_QUESTION, OUTPUT_FIELDS, TOP_K)
En parcourant les 2 principaux résultats uniques, nous pouvons voir le contenu suivant, étroitement renvoyé par la requête de recherche ci-dessus :
Le script Ray Data complet est disponible sur GitHub.
Conclusion
Ce blog a montré comment utiliser les fonctionnalités Ray Data et Milvus Bulk Import pour accélérer considérablement la génération de vecteurs et les charger efficacement par lots dans une base de données vectorielle. Par exemple, l’intégration de 102 000 lignes de données avec Ray Data a pris 4 minutes, contre 4 heures avec une approche Pandas naïve ! ****De plus, l’utilisation de Bulk Import dans Milvus permet de créer un index vectoriel très efficace et d’économiser des ressources et du temps par rapport à l’insertion incrémentielle classique. Consultez Ray Data et les fonctionnalités Bulk Import dans Milvus et Zilliz Cloud pour plus de détails !
Continuer à lire

How to Build RAG with Milvus, QwQ-32B and Ollama
Hands-on tutorial on how to create a streamlined, powerful RAG pipeline that balances efficiency, accuracy, and scalability using the QwQ-32B and Milvus.

Building RAG Pipelines for Real-Time Data with Cloudera and Milvus
explore how Cloudera can be integrated with Milvus to effectively implement some of the key functionalities of RAG pipelines.

DeepRAG: Thinking to Retrieval Step by Step for Large Language Models
Discover DeepRAG, an advanced retrieval-augmented generation (RAG) model that improves LLM accuracy by retrieving only essential data through step-by-step reasoning.




