
Je suis Jules Ginhac, Co-Founder & ingénieur IA chez Apogée Consult à Lyon. Je conçois et déploie des architectures IA génératives (RAG, agents, LLMOps) pour des PME, startups et organisations publiques.
Embeddings multilingues pour le français : notre choix après bench de 6 modèles
- ia-produit
Construire un RAG sur un corpus français demande de choisir un modèle d'embedding adapté. On a benché 6 modèles sur des données réelles, voici ce qu'on a mesuré et ce qu'on utilise.
Embeddings multilingues pour le français : notre choix après bench de 6 modèles
La plupart des tutoriels RAG utilisent text-embedding-3-small d'OpenAI ou ada-002. Ces modèles fonctionnent raisonnablement bien en anglais. Sur du français natif, textes juridiques, documentation technique, corpus métier, la situation est moins simple.
On a eu besoin de trancher sur un projet avec un corpus de 800 000 documents français (rapports d'analyse, notes internes, documentation réglementaire). On a benché 6 modèles. Voici nos résultats.
Le protocole de benchmark
On a construit un dataset d'évaluation à partir de 300 paires question/document extraites manuellement du corpus. Pour chaque paire, un annotateur humain a confirmé que le document était une réponse pertinente à la question.
Le metric principal : Recall@10, pour une requête donnée, est-ce que le document pertinent apparaît dans les 10 premiers résultats ?
Le metric secondaire : MRR@10 (Mean Reciprocal Rank), à quelle position apparaît en moyenne le premier document pertinent ?
On a aussi mesuré le coût d'embedding pour 1 million de tokens et la latence d'inférence en mode batch.
Les 6 modèles testés :
- text-embedding-3-small (OpenAI), 1536 dim
- text-embedding-3-large (OpenAI), 3072 dim
- mistral-embed (Mistral AI), 1024 dim
- multilingual-e5-large (Microsoft), 1024 dim
- BGE-M3 (BAAI), 1024 dim
- paraphrase-multilingual-mpnet-base-v2 (sentence-transformers), 768 dim
Résultats
| Modèle | Recall@10 | MRR@10 | Coût / 1M tokens | Self-hosted |
|---|---|---|---|---|
| text-embedding-3-small | 0.81 | 0.62 | $0.02 | Non |
| text-embedding-3-large | 0.84 | 0.67 | $0.13 | Non |
| mistral-embed | 0.83 | 0.65 | $0.10 | Non |
| multilingual-e5-large | 0.82 | 0.64 | , | Oui |
| BGE-M3 | 0.88 | 0.71 | , | Oui |
| paraphrase-multilingual-mpnet | 0.74 | 0.55 | , | Oui |
BGE-M3 sort clairement en tête sur notre corpus, avec 4 à 7 points de Recall@10 d'avance sur les modèles OpenAI et Mistral. Mistral Embed est au niveau de text-embedding-3-large pour un coût légèrement inférieur, un choix raisonnable si on veut rester sur une API.
Le modèle sentence-transformers paraphrase-multilingual-mpnet-base-v2 décroche clairement. C'est un modèle plus ancien (2020) qui n'a pas été conçu spécifiquement pour la recherche sémantique.
Pourquoi BGE-M3 se démarque sur le français
BGE-M3 a été entraîné par le Beijing Academy of AI sur un corpus multilingue massif qui inclut une proportion significative de données françaises, et avec un objectif explicite de retrieval dense. Il supporte trois modes de retrieval : dense, sparse (comme BM25), et multi-vector (ColBERT-style).
Pour le mode dense standard utilisé dans la plupart des RAG, c'est déjà le meilleur sur notre benchmark. Mais la vraie force de BGE-M3 est le mode hybride dense+sparse :
from FlagEmbedding import BGEM3FlagModel
model = BGEM3FlagModel('BAAI/bge-m3', use_fp16=True)
# Génère à la fois des embeddings denses et sparse
embeddings = model.encode(
sentences,
batch_size=12,
max_length=8192,
return_dense=True,
return_sparse=True,
return_colbert_vecs=False
)
dense_vecs = embeddings['dense_vecs'] # numpy array (n, 1024)
sparse_vecs = embeddings['lexical_weights'] # dict token -> poidsLe mode hybride dense+sparse a amélioré notre Recall@10 de 0.88 à 0.92 sur notre dataset. C'est la combinaison qui a le meilleur rappel sur les requêtes qui mélangent des termes techniques précis (sparse) et du langage naturel (dense).
Les contraintes de BGE-M3
BGE-M3 nécessite d'auto-héberger le modèle. Il n'existe pas d'API publique officielle maintenue par BAAI à ce jour. En pratique, ça signifie :
- Un GPU pour l'inférence à l'échelle (on tourne sur une A10G pour le batch d'indexation, une T4 pour l'inférence temps réel)
- Une gestion de l'infrastructure d'embedding séparée de la base vectorielle
- Un pipeline de batch pour ré-indexer quand le corpus change
Le coût d'inférence en self-hosted sur A10G est d'environ 30-40 secondes pour embarquer 1 000 documents de 500 tokens en batch. Pour 800 000 documents, l'indexation initiale a pris environ 8 heures.
Mistral Embed : le compromis API
Si l'infrastructure d'auto-hébergement est un blocant, Mistral Embed est le meilleur choix sur API pour le français. On a mesuré un MRR@10 de 0.65 sur notre corpus, inférieur à BGE-M3 mais supérieur à text-embedding-3-large malgré un coût inférieur ($0.10 vs $0.13 pour 1M tokens).
Mistral Embed a l'avantage d'avoir été entraîné avec un focus explicite sur le français. C'est particulièrement visible sur les requêtes avec des formes verbales complexes ou des structures de phrases typiquement françaises où OpenAI sous-performe légèrement.
Ce qu'on utilise en production
Sur ce projet, on a opté pour BGE-M3 en mode hybride dense+sparse, self-hosted sur une T4 pour l'inférence temps réel et sur une A10G pour les batch d'indexation.
Le pipeline :
from FlagEmbedding import BGEM3FlagModel
from qdrant_client import QdrantClient
from qdrant_client.models import SparseVector, NamedVector
model = BGEM3FlagModel('BAAI/bge-m3', use_fp16=True)
def index_document(doc_id: str, text: str):
embedding = model.encode(
[text],
return_dense=True,
return_sparse=True,
)
sparse_indices = list(embedding['lexical_weights'][0].keys())
sparse_values = list(embedding['lexical_weights'][0].values())
client.upsert(
collection_name="documents",
points=[{
"id": doc_id,
"vector": {
"dense": embedding['dense_vecs'][0].tolist(),
"sparse": SparseVector(
indices=sparse_indices,
values=sparse_values
)
}
}]
)Le choix de BGE-M3 self-hosted vs Mistral Embed sur API reste un arbitrage entre qualité et complexité opérationnelle. Pour une équipe sans infrastructure GPU disponible, Mistral Embed est une alternative valide qui évite cette complexité avec une perte de recall acceptable.
La question qu'on ne sait pas encore répondre : comment ces modèles vont-ils évoluer face à des modèles d'embedding spécialisés francophones qui pourraient émerger dans les prochains mois ? BGE-M3 était déjà la meilleure option multilingue disponible au moment de notre benchmark, mais l'espace bouge vite.