Découpage Sémantique pour une Meilleure Récupération
Divisez les documents intelligemment en fonction du sens, pas seulement de la longueur. Apprenez les techniques de découpage sémantique pour le RAG.
- Auteur
- Équipe de Recherche Ailog
- Date de publication
- Temps de lecture
- 12 min de lecture
- Niveau
- advanced
- Étape du pipeline RAG
- Chunking
Le problème du découpage à taille fixe
Le découpage traditionnel divise le texte tous les N caractères ou tokens : • ❌ Coupe les phrases en pleine pensée • ❌ Sépare le contenu lié • ❌ Aucune conscience du contexte
Le découpage sémantique divise en fonction du sens, pas de la longueur.
Comment fonctionne le découpage sémantique Embedder chaque phrase en utilisant un encodeur de phrases Calculer la similarité entre phrases consécutives Diviser où la similarité chute (changement de sujet)
``python from sentence_transformers import SentenceTransformer import numpy as np
model = SentenceTransformer('all-MiniLM-L6-v2')
def semantic_chunk(text, similarity_threshold=0.5): Split into sentences sentences = text.split('. ')
Embed all sentences embeddings = model.encode(sentences)
Calculate cosine similarity between consecutive sentences chunks = [] current_chunk = [sentences[0]]
for i in range(1, len(sentences)): similarity = np.dot(embeddings[i-1], embeddings[i]) / ( np.linalg.norm(embeddings[i-1]) np.linalg.norm(embeddings[i]) )
if similarity < similarity_threshold: Topic changed - start new chunk chunks.append('. '.join(current_chunk)) current_chunk = [sentences[i]] else: current_chunk.append(sentences[i])
Add final chunk chunks.append('. '.join(current_chunk))
return chunks `
Découpage sémantique LangChain (2025)
LangChain inclut maintenant le découpage sémantique intégré :
`python from langchain.text_splitter import SemanticChunker from langchain_openai.embeddings import OpenAIEmbeddings
text_splitter = SemanticChunker( OpenAIEmbeddings(), breakpoint_threshold_type="percentile", or "standard_deviation" breakpoint_threshold_amount=95 )
chunks = text_splitter.create_documents([long_text]) `
Avancé : Découpage sémantique multi-niveaux
Combinez les divisions sémantiques avec des contraintes de taille :
`python def smart_semantic_chunk(text, max_chunk_size=1000, min_chunk_size=200): First: semantic split semantic_chunks = semantic_chunk(text)
final_chunks = []
for chunk in semantic_chunks: If chunk too large, further split if len(chunk) > max_chunk_size: Split by paragraphs within this semantic section paragraphs = chunk.split('\n\n') sub_chunk = ""
for para in paragraphs: if len(sub_chunk) + len(para) < max_chunk_size: sub_chunk += para + "\n\n" else: final_chunks.append(sub_chunk.strip()) sub_chunk = para + "\n\n"
if sub_chunk: final_chunks.append(sub_chunk.strip())
If chunk too small, merge with next elif len(chunk) < min_chunk_size and final_chunks: final_chunks[-1] += "\n\n" + chunk else: final_chunks.append(chunk)
return final_chunks `
Séparateur sémantique Llamaindex
`python from llama_index.node_parser import SemanticSplitterNodeParser from llama_index.embeddings import OpenAIEmbedding
embed_model = OpenAIEmbedding()
splitter = SemanticSplitterNodeParser( buffer_size=1, Sentences to group breakpoint_percentile_threshold=95, embed_model=embed_model )
nodes = splitter.get_nodes_from_documents(documents) `
Quand utiliser le découpage sémantique
Utilisez le découpage sémantique quand : • Les documents ont des transitions de sujets claires • Vous avez besoin d'une récupération haute précision • Le contenu est narratif ou explicatif • Vous pouvez vous permettre le coût de calcul
Restez sur la taille fixe quand : • La vitesse est critique • Les documents sont très uniformes • Le budget est limité • Le contenu est tabulaire ou structuré
Considérations de performance
Coût d'embedding : • Le découpage sémantique nécessite l'embedding de chaque phrase • Pour un document de 10 000 mots : ~300 phrases à embedder • Envisagez la mise en cache des embeddings
Comparaison de vitesse (novembre 2025) : • Taille fixe : ~1ms par document • Sémantique : ~100-500ms par document (selon le modèle)
Approche hybride : Le meilleur des deux mondes
`python def hybrid_chunk(text, target_size=500): Semantic split first semantic_chunks = semantic_chunk(text, similarity_threshold=0.6) Merge small chunks, split large ones final_chunks = [] buffer = ""
for chunk in semantic_chunks: if len(buffer) + len(chunk) < target_size 1.5: buffer += "\n\n" + chunk if buffer else chunk else: if buffer: final_chunks.append(buffer) buffer = chunk
if buffer: final_chunks.append(buffer)
return final_chunks `
Évaluation
Testez la qualité de récupération avec découpage sémantique vs fixe :
`python Your test queries queries = [ "How does photosynthesis work?", "What are the benefits of exercise?" ]
Compare retrieval accuracy semantic_results = evaluate_chunking(semantic_chunks, queries) fixed_results = evaluate_chunking(fixed_chunks, queries)
print(f"Semantic MRR: {semantic_results['mrr']}") print(f"Fixed MRR: {fixed_results['mrr']}") ``
Le découpage sémantique améliore typiquement la récupération de 15-30% mais coûte 100x plus de calcul. Choisissez en fonction de votre compromis précision/coût.