Maison  >  Article  >  développement back-end  >  Construire un système de recherche sémantique rapide et efficace à l'aide d'OpenVINO et Postgres

Construire un système de recherche sémantique rapide et efficace à l'aide d'OpenVINO et Postgres

Susan Sarandon
Susan Sarandonoriginal
2024-10-21 16:31:02584parcourir

Building a Fast and Efficient Semantic Search System Using OpenVINO and Postgres

Photo de real-napster sur Pixabay

Dans l'un de mes récents projets, j'ai dû créer un système de recherche sémantique capable d'évoluer avec des performances élevées et de fournir des réponses en temps réel pour les recherches de rapports. Nous avons utilisé PostgreSQL avec pgvector sur AWS RDS, associé à AWS Lambda, pour y parvenir. Le défi était de permettre aux utilisateurs d'effectuer des recherches à l'aide de requêtes en langage naturel au lieu de s'appuyer sur des mots-clés rigides, tout en garantissant que les réponses duraient moins de 1 à 2 secondes, voire moins, et ne pouvaient exploiter que les ressources du processeur.

Dans cet article, je passerai en revue les étapes que j'ai suivies pour créer ce système de recherche, de la récupération au reclassement, ainsi que les optimisations réalisées à l'aide d'OpenVINO et du traitement par lots intelligent pour la tokenisation.

Présentation de la recherche sémantique : récupération et reclassement

Les systèmes de recherche modernes de pointe comprennent généralement deux étapes principales : récupération et reclassement.

1) Récupération : La première étape consiste à récupérer un sous-ensemble de documents pertinents en fonction de la requête de l'utilisateur. Cela peut être fait à l'aide de modèles d'intégration pré-entraînés, tels que les intégrations petites et grandes d'OpenAI, les modèles Embed de Cohere ou les intégrations mxbai de Mixbread. La récupération se concentre sur la réduction du pool de documents en mesurant leur similarité avec la requête.

Voici un exemple simplifié utilisant la bibliothèque de transformation de phrases de Huggingface pour la récupération, qui est l'une de mes bibliothèques préférées pour cela :

from sentence_transformers import SentenceTransformer
import numpy as np

# Load a pre-trained sentence transformer model
model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")

# Sample query and documents (vectorize the query and the documents)
query = "How do I fix a broken landing gear?"
documents = ["Report 1 on landing gear failure", "Report 2 on engine problems"]

# Get embeddings for query and documents
query_embedding = model.encode(query)
document_embeddings = model.encode(documents)

# Calculate cosine similarity between query and documents
similarities = np.dot(document_embeddings, query_embedding)

# Retrieve top-k most relevant documents
top_k = np.argsort(similarities)[-5:]
print("Top 5 documents:", [documents[i] for i in top_k])

2) Reclassement : Une fois les documents les plus pertinents récupérés, nous améliorons encore le classement de ces documents à l'aide d'un modèle de cross-encoder. Cette étape réévalue chaque document par rapport à la requête avec plus de précision, en se concentrant sur une compréhension contextuelle plus approfondie.
Le reclassement est bénéfique car il ajoute une couche de raffinement supplémentaire en notant plus précisément la pertinence de chaque document.

Voici un exemple de code pour le reclassement à l'aide de cross-encoder/ms-marco-TinyBERT-L-2-v2, un encodeur croisé léger :

from sentence_transformers import CrossEncoder

# Load the cross-encoder model
cross_encoder = CrossEncoder("cross-encoder/ms-marco-TinyBERT-L-2-v2")

# Use the cross-encoder to rerank top-k retrieved documents
query_document_pairs = [(query, doc) for doc in documents]
scores = cross_encoder.predict(query_document_pairs)

# Rank documents based on the new scores
top_k_reranked = np.argsort(scores)[-5:]
print("Top 5 reranked documents:", [documents[i] for i in top_k_reranked])

Identifier les goulots d'étranglement : le coût de la tokenisation et de la prédiction

Pendant le développement, j'ai constaté que les étapes de tokenisation et de prédiction prenaient assez de temps lors de la gestion de 1 000 rapports avec des paramètres par défaut pour les transformateurs de phrases. Cela a créé un goulot d'étranglement en termes de performances, d'autant plus que nous visions des réponses en temps réel.

Ci-dessous j'ai profilé mon code à l'aide de SnakeViz pour visualiser les performances :

Building a Fast and Efficient Semantic Search System Using OpenVINO and Postgres

Comme vous pouvez le constater, les étapes de tokenisation et de prédiction sont disproportionnellement lentes, ce qui entraîne des retards importants dans la diffusion des résultats de recherche. Dans l’ensemble, cela a pris en moyenne 4 à 5 secondes. Cela est dû au fait qu’il existe des opérations de blocage entre les étapes de tokenisation et de prédiction. Si nous additionnons également d'autres opérations comme l'appel de base de données, le filtrage, etc., nous nous retrouvons facilement avec 8 à 9 secondes au total.

Optimiser les performances avec OpenVINO

La question à laquelle j'ai été confronté était : Pouvons-nous le rendre plus rapide ? La réponse est oui, en tirant parti de OpenVINO, un backend optimisé pour l'inférence CPU. OpenVINO permet d'accélérer l'inférence de modèles d'apprentissage profond sur le matériel Intel, que nous utilisons sur AWS Lambda.

Exemple de code pour l'optimisation OpenVINO
Voici comment j'ai intégré OpenVINO dans le système de recherche pour accélérer l'inférence :

from sentence_transformers import SentenceTransformer
import numpy as np

# Load a pre-trained sentence transformer model
model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")

# Sample query and documents (vectorize the query and the documents)
query = "How do I fix a broken landing gear?"
documents = ["Report 1 on landing gear failure", "Report 2 on engine problems"]

# Get embeddings for query and documents
query_embedding = model.encode(query)
document_embeddings = model.encode(documents)

# Calculate cosine similarity between query and documents
similarities = np.dot(document_embeddings, query_embedding)

# Retrieve top-k most relevant documents
top_k = np.argsort(similarities)[-5:]
print("Top 5 documents:", [documents[i] for i in top_k])

Avec cette approche, nous pourrions obtenir une accélération de 2 à 3 fois, réduisant les 4 à 5 secondes d'origine à 1 à 2 secondes. Le code de travail complet est sur Github.

Optimisation de la vitesse : taille des lots et tokenisation

Un autre facteur critique dans l'amélioration des performances était l'optimisation du processus de tokenisation et l'ajustement de la taille du lot et de la longueur du jeton. En augmentant la taille du lot (batch_size=16) et en réduisant la longueur du jeton (max_length=512), nous pourrions paralléliser la tokenisation et réduire la surcharge des opérations répétitives. Dans nos expériences, nous avons constaté qu'une valeur batch_size comprise entre 16 et 64 fonctionnait bien, toute valeur supérieure dégradant les performances. De même, nous avons opté pour une max_length de 128, ce qui est viable si la longueur moyenne de vos rapports est relativement courte. Grâce à ces changements, nous avons atteint une accélération globale de 8x, réduisant le temps de reclassement à moins d'une seconde, même sur le processeur.

En pratique, cela signifiait expérimenter différentes tailles de lots et longueurs de jetons pour trouver le bon équilibre entre vitesse et précision pour vos données. Ce faisant, nous avons constaté des améliorations significatives des temps de réponse, rendant le système de recherche évolutif même avec 1 000 rapports.

Conclusion

En utilisant OpenVINO et en optimisant la tokenisation et le traitement par lots, nous avons pu créer un système de recherche sémantique hautes performances qui répond aux exigences en temps réel sur une configuration uniquement CPU. En fait, nous avons connu une accélération globale de 8x. La combinaison de la récupération à l'aide de transformateurs de phrases et du reclassement avec un modèle d'encodeur croisé crée une expérience de recherche puissante et conviviale.

Si vous construisez des systèmes similaires avec des contraintes de temps de réponse et de ressources de calcul, je vous recommande fortement d'explorer OpenVINO et le traitement par lots intelligent pour débloquer de meilleures performances.

J'espère que vous avez apprécié cet article. Si vous avez trouvé cet article utile, donnez-moi un like pour que d'autres puissent le trouver également et partagez-le avec vos amis. Suivez-moi sur Linkedin pour rester au courant de mon travail. Merci d'avoir lu !

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn