Dense Retrieval: Semantische Suche mit Embeddings
Beherrschen Sie Dense Retrieval für eine leistungsfähige semantische Suche. Embeddings, Modelle, vektorielle Indexierung und fortgeschrittene Optimierungen.
Dense Retrieval : Semantische Suche mit embeddings
Le dense retrieval transforme la recherche d'information en capturant le sens profond des requêtes et documents. Contrairement à la recherche lexicale qui compare des mots, le dense retrieval compare des concepts. Ce guide approfondit les mécanismes, modèles et techniques pour implémenter une recherche sémantique performante dans vos systèmes RAG.
Qu'est-ce que le dense retrieval ?
Le dense retrieval représente chaque texte par un vecteur dense de nombres réels, typiquement entre 384 et 4096 dimensions. Ces vecteurs, appelés embeddings, capturent la sémantique du texte dans un espace mathématique où des concepts similaires sont proches.
Différence avec le sparse retrieval
| Caractéristique | Dense Retrieval | Sparse Retrieval |
|---|---|---|
| Représentation | Vecteurs denses (384-4096 dim) | Vecteurs sparse (vocabulaire) |
| Correspondance | Sémantique | Lexicale |
| "Voiture" = "Automobile" | Oui | Non |
| Termes rares | Moins performant | Excellent |
| Typos et variantes | Robuste | Sensible |
Le dense retrieval excelle quand l'utilisateur formule sa requête différemment du contenu source. "Comment annuler ma commande" trouvera "Procédure de résiliation d'achat" même sans mots communs.
Architecture d'un système dense retrieval
┌──────────────────────────────────────────────────────────────┐
│ Pipeline Dense Retrieval │
├──────────────────────────────────────────────────────────────┤
│ │
│ Documents Modèle Base Vectorielle │
│ ┌─────────┐ Embedding ┌──────────────┐ │
│ │ Doc 1 │──┐ ┌───────┐ ┌───│ Qdrant │ │
│ │ Doc 2 │──┼───▶│ E5 │────┼───│ Pinecone │ │
│ │ Doc 3 │──┘ │ BGE │ │ │ Weaviate │ │
│ └─────────┘ └───────┘ │ └──────────────┘ │
│ │ │ │
│ Query │ │ ANN Search │
│ ┌─────────┐ │ ▼ │
│ │"Comment │────────────────────┘ ┌──────────────┐ │
│ │annuler" │ │ Top-K docs │ │
│ └─────────┘ └──────────────┘ │
│ │
└──────────────────────────────────────────────────────────────┘
Modèles d'embedding : le comparatif 2024
Le choix du modèle d'embedding impacte directement la qualité de votre retrieval. Voici les options actuelles classées par performance sur les benchmarks MTEB.
Modèles open source
DEVELOPERpythonfrom sentence_transformers import SentenceTransformer # BGE-M3 : Meilleur rapport qualité/performance multilingue model_bge = SentenceTransformer('BAAI/bge-m3') # E5-Large : Excellent pour le français model_e5 = SentenceTransformer('intfloat/multilingual-e5-large') # GTE-Large : Alternative performante model_gte = SentenceTransformer('thenlper/gte-large')
| Modèle | Dimensions | MTEB FR | Vitesse | Usage |
|---|---|---|---|---|
| BGE-M3 | 1024 | 62.3 | Moyen | Production multilingue |
| E5-Large-v2 | 1024 | 61.8 | Moyen | Français prioritaire |
| GTE-Large | 1024 | 60.2 | Rapide | Volume élevé |
| All-MiniLM | 384 | 52.1 | Très rapide | Prototypage |
Modèles propriétaires
DEVELOPERpythonimport openai # OpenAI text-embedding-3 client = openai.OpenAI() response = client.embeddings.create( model="text-embedding-3-large", input="Comment annuler ma commande ?", dimensions=1536 # Configurable jusqu'à 3072 ) embedding = response.data[0].embedding # Cohere Embed v3 import cohere co = cohere.Client() response = co.embed( texts=["Comment annuler ma commande ?"], model="embed-multilingual-v3.0", input_type="search_query" )
| Modèle | Dimensions | Performance | Coût/1M tokens |
|---|---|---|---|
| text-embedding-3-large | 3072 | Excellent | $0.13 |
| text-embedding-3-small | 1536 | Très bon | $0.02 |
| Cohere embed-v3 | 1024 | Excellent | $0.10 |
| Voyage-2 | 1024 | État de l'art | $0.12 |
Optimiser la qualité des embeddings
Préfixes query/passage
Certains modèles nécessitent des préfixes pour distinguer les requêtes des documents :
DEVELOPERpythonfrom sentence_transformers import SentenceTransformer model = SentenceTransformer('intfloat/multilingual-e5-large') # IMPORTANT : Préfixer correctement query = "query: Comment fonctionne la garantie ?" passages = [ "passage: La garantie couvre les défauts pendant 2 ans.", "passage: Contactez le SAV pour toute réclamation." ] query_embedding = model.encode(query) passage_embeddings = model.encode(passages)
Sans ces préfixes, la performance peut chuter de 5 à 15 points sur les benchmarks.
Normalisation des embeddings
Pour utiliser le produit scalaire au lieu de la similarité cosinus (plus rapide) :
DEVELOPERpythonimport numpy as np def normalize(embeddings): norms = np.linalg.norm(embeddings, axis=1, keepdims=True) return embeddings / norms # Embeddings normalisés embeddings_norm = normalize(embeddings) # Maintenant dot product = cosine similarity similarity = np.dot(query_norm, doc_norm.T)
Matryoshka embeddings
Les modèles récents supportent les "Matryoshka embeddings" : des embeddings où les premières dimensions sont les plus informatives.
DEVELOPERpython# Avec text-embedding-3, vous pouvez réduire les dimensions response = client.embeddings.create( model="text-embedding-3-large", input=texts, dimensions=512 # Au lieu de 3072, avec perte minime ) # Recherche rapide avec dimensions réduites # puis reranking avec dimensions complètes
Cela permet un compromis vitesse/précision configurable.
Indexation vectorielle avancée
Algorithmes ANN (Approximate Nearest Neighbor)
La recherche exacte (brute force) est O(n). Les algorithmes ANN réduisent à O(log n) avec une légère perte de précision.
HNSW (Hierarchical Navigable Small Worlds)
Le plus utilisé. Excellent compromis vitesse/précision.
DEVELOPERpythonfrom qdrant_client import QdrantClient from qdrant_client.models import VectorParams, HnswConfigDiff client = QdrantClient("localhost", port=6333) # Configuration HNSW optimisée client.create_collection( collection_name="documents", vectors_config=VectorParams( size=1024, distance="Cosine" ), hnsw_config=HnswConfigDiff( m=16, # Connexions par nœud (16-64) ef_construct=100, # Qualité de construction ) ) # À la recherche, ajuster ef pour précision/vitesse results = client.search( collection_name="documents", query_vector=query_embedding, limit=10, search_params={"ef": 128} # Plus haut = plus précis, plus lent )
IVF (Inverted File Index)
Divise l'espace en clusters. Plus rapide mais moins précis.
DEVELOPERpython# Avec FAISS import faiss # Créer l'index IVF dimension = 1024 nlist = 100 # Nombre de clusters quantizer = faiss.IndexFlatL2(dimension) index = faiss.IndexIVFFlat(quantizer, dimension, nlist) # Entraîner sur les données index.train(embeddings) index.add(embeddings) # Recherche avec nprobe clusters index.nprobe = 10 # Plus haut = plus précis distances, indices = index.search(query_embedding, k=10)
Quantification pour réduire la mémoire
Réduire la taille des vecteurs de 75% avec une perte minimale :
DEVELOPERpython# Quantification scalaire (int8) from qdrant_client.models import QuantizationConfig, ScalarQuantization client.create_collection( collection_name="documents_quantized", vectors_config=VectorParams(size=1024, distance="Cosine"), quantization_config=QuantizationConfig( scalar=ScalarQuantization( type="int8", quantile=0.99, always_ram=True ) ) ) # 1M vecteurs 1024d : 4GB → 1GB # Perte de recall : ~1-2%
Évaluation du dense retrieval
Métriques essentielles
DEVELOPERpythondef evaluate_retrieval(queries, ground_truth, retriever, k_values=[1, 5, 10]): metrics = {} for k in k_values: recalls = [] precisions = [] for query, relevant_docs in zip(queries, ground_truth): retrieved = retriever.search(query, top_k=k) retrieved_ids = [doc.id for doc in retrieved] # Recall@k hits = len(set(retrieved_ids) & set(relevant_docs)) recall = hits / len(relevant_docs) recalls.append(recall) # Precision@k precision = hits / k precisions.append(precision) metrics[f"recall@{k}"] = np.mean(recalls) metrics[f"precision@{k}"] = np.mean(precisions) # MRR mrr_scores = [] for query, relevant_docs in zip(queries, ground_truth): retrieved = retriever.search(query, top_k=100) for i, doc in enumerate(retrieved): if doc.id in relevant_docs: mrr_scores.append(1 / (i + 1)) break else: mrr_scores.append(0) metrics["mrr"] = np.mean(mrr_scores) return metrics
Benchmarks recommandés
- BEIR : Benchmark multidomaine pour le retrieval
- MTEB : Massive Text Embedding Benchmark
- MS MARCO : Questions-réponses web
Pièges courants et solutions
1. Domain shift
Les modèles génériques performent mal sur du vocabulaire spécialisé.
Solution : Fine-tuning contrastif
DEVELOPERpythonfrom sentence_transformers import SentenceTransformer, InputExample, losses model = SentenceTransformer('BAAI/bge-m3') # Données d'entraînement : paires (query, positive_doc) train_examples = [ InputExample(texts=["Symptômes COVID", "Fièvre, toux sèche, fatigue"]), InputExample(texts=["Traitement grippe", "Repos, hydratation, paracétamol"]), ] # Loss contrastive train_loss = losses.MultipleNegativesRankingLoss(model) # Fine-tuning model.fit( train_objectives=[(train_dataloader, train_loss)], epochs=3, warmup_steps=100 )
2. Requêtes courtes
Les requêtes de 1-2 mots ont des embeddings peu discriminants.
Solution : Query expansion
DEVELOPERpythondef expand_query(query: str, llm) -> str: if len(query.split()) <= 3: expansion = llm.complete( f"Reformule cette recherche en une phrase complète: {query}" ) return f"{query} {expansion}" return query
3. Documents longs
Les embeddings de documents trop longs perdent en précision.
Solution : Late interaction ou chunking avec agrégation
DEVELOPERpythondef embed_long_document(doc: str, model, chunk_size=512): chunks = split_into_chunks(doc, chunk_size) chunk_embeddings = model.encode(chunks) # Agrégation : moyenne pondérée par position weights = np.exp(-np.arange(len(chunks)) * 0.1) weights /= weights.sum() return np.average(chunk_embeddings, axis=0, weights=weights)
Intégration dans un pipeline RAG
DEVELOPERpythonclass DenseRetriever: def __init__(self, model_name: str, collection: str): self.model = SentenceTransformer(model_name) self.client = QdrantClient("localhost", port=6333) self.collection = collection def index(self, documents: list[dict]): embeddings = self.model.encode( [doc["content"] for doc in documents], show_progress_bar=True ) points = [ PointStruct( id=doc["id"], vector=emb.tolist(), payload={"content": doc["content"], **doc.get("metadata", {})} ) for doc, emb in zip(documents, embeddings) ] self.client.upsert(collection_name=self.collection, points=points) def search(self, query: str, top_k: int = 5, filters: dict = None): query_embedding = self.model.encode(f"query: {query}") filter_conditions = self._build_filters(filters) if filters else None results = self.client.search( collection_name=self.collection, query_vector=query_embedding.tolist(), query_filter=filter_conditions, limit=top_k ) return [ {"content": hit.payload["content"], "score": hit.score} for hit in results ]
Prochaines étapes
Le dense retrieval est puissant mais pas universel. Pour certains cas, la recherche lexicale reste supérieure. Découvrez comment dans nos guides connexes :
- Sparse Retrieval et BM25 - Quand la recherche lexicale surpasse
- Fusion hybride - Combiner dense et sparse
- Fondamentaux du Retrieval - Vue d'ensemble
FAQ
Passez au dense retrieval avec Ailog
Implémenter un système dense retrieval performant demande expertise et infrastructure. Avec Ailog, bénéficiez :
- Modèles d'embedding optimisés pour le français (BGE-M3, E5-Large)
- Indexation HNSW automatique avec Qdrant
- Quantification intelligente pour optimiser coûts et performances
- Fine-tuning assisté sur votre vocabulaire métier
Testez gratuitement et déployez votre recherche sémantique en minutes.
Tags
Verwandte Artikel
Query Routing: Anfragen an die richtige Quelle weiterleiten
Implementieren Sie Query Routing, um jede Anfrage zur optimalen Datenquelle zu leiten. Klassifizierung, LLM-Routing und fortgeschrittene Strategien.
Hybride Fusion: Dense- und Sparse-Retrieval kombinieren
Meistern Sie die hybride Fusion zur Kombination von semantischer und lexikalischer Suche. RRF, weighted fusion und optimale Kombinationsstrategien.
Sparse Retrieval und BM25: Wenn die lexikalische Suche überlegen ist
Entdecken Sie Sparse Retrieval und BM25 für eine präzise lexikalische Suche. Anwendungsfälle, Implementierung und Vergleich mit dem dense retrieval.