Polydesk-logotype
Polydesk.ai — Header

BM25 (Best Matching 25)

BM25 (Best Matching 25) est un algorithme de ranking probabiliste qui calcule la pertinence d’un document par rapport à une requête en combinant la fréquence des termes, leur rareté dans le corpus et la longueur du document. C’est l’algorithme de recherche par défaut d’Elasticsearch, Apache Lucene et Apache Solr.

Inventé dans les années 1990 dans le cadre du projet Okapi à la City University of London, BM25 est une évolution directe de TF-IDF qui corrige ses principales faiblesses. Trente ans plus tard, il reste la colonne vertébrale de la quasi-totalité des moteurs de recherche textuels en production. Et avec l’essor du hybrid search et du RAG, BM25 connaît une seconde jeunesse comme composant lexical indispensable des pipelines de retrieval modernes.

BM25 en bref
Catégorie
Information Retrieval / Algorithme de ranking
Nom complet
Okapi BM25 (Best Matching 25, 25e itération du modèle probabiliste)
Créateurs
Stephen Robertson et Karen Spärck Jones (City University of London, années 1990)
Utilisé par
Elasticsearch, Apache Lucene, Apache Solr, Weaviate, OpenSearch, Milvus
Paramètres
k1 = 1,2 (saturation TF) et b = 0,75 (normalisation longueur) par défaut
Variantes
BM25F (multi-champs), BM25L, BM25+

Pourquoi BM25 est encore partout en 2026

Dans un paysage dominé par les embeddings et les LLM, la longévité de BM25 peut surprendre. Mais ses avantages sont structurels et complémentaires aux approches neuronales.

Zéro entraînement nécessaire. BM25 fonctionne dès que vous avez un corpus indexé. Pas de GPU, pas de modèle à fine-tuner, pas de pipeline d’embedding. Vous indexez un million de documents et obtenez des résultats pertinents immédiatement.

Rapidité. BM25 repose sur un index inversé, une structure de données qui mappe chaque terme aux documents qui le contiennent. La recherche est en complexité quasi-linéaire par rapport au nombre de termes de la requête, pas au nombre de documents. Sur un corpus de 10 millions de documents, une requête BM25 prend typiquement moins de 10 ms.

Transparence. Vous pouvez expliquer exactement pourquoi un document est classé devant un autre. L’API Explain d’Elasticsearch décompose le score terme par terme. Impossible d’obtenir cette transparence avec une recherche vectorielle.

Précision sur les termes exacts. Noms propres, codes produit, numéros de référence, identifiants techniques : BM25 excelle là où la recherche sémantique échoue. Un embedding ne distingue pas bien « PostgreSQL 15.4 » de « PostgreSQL 14.2 », mais BM25 le fait parfaitement.

Composant clé du hybrid search. Les meilleurs systèmes de retrieval combinent BM25 (précision lexicale) avec la recherche vectorielle (compréhension sémantique) via le hybrid search. BM25 y joue le rôle de filet de sécurité : il attrape les correspondances exactes que les vecteurs ratent.


La formule BM25 expliquée

La formule complète de BM25 peut sembler intimidante, mais elle se décompose en trois composants intuitifs :

Score(D, Q) = Σ IDF(qi) × [ f(qi, D) × (k1 + 1) ] / [ f(qi, D) + k1 × (1 - b + b × |D| / avgdl) ]

Où :
- Q = requête composée des termes q1, q2, ..., qn
- D = document évalué
- f(qi, D) = fréquence du terme qi dans le document D
- |D| = longueur du document D (en nombre de tokens)
- avgdl = longueur moyenne des documents du corpus
- k1 = paramètre de saturation de la fréquence (défaut : 1,2)
- b = paramètre de normalisation de la longueur (défaut : 0,75)
- IDF(qi) = inverse document frequency du terme qi

Décortiquons chaque composant.

IDF : la rareté du terme

L’IDF (Inverse Document Frequency) mesure la rareté d’un terme dans le corpus. Un terme qui apparaît dans seulement 3 documents sur 1 million a un IDF très élevé : il est discriminant. Un terme qui apparaît dans la moitié du corpus a un IDF proche de zéro : il n’aide pas à distinguer les documents.

L’implémentation de Lucene/Elasticsearch utilise cette formule IDF :

IDF(qi) = ln(1 + (N - n(qi) + 0.5) / (n(qi) + 0.5))

Où :
- N = nombre total de documents
- n(qi) = nombre de documents contenant le terme qi

Le +1 à l’intérieur du logarithme est un ajout de Lucene pour garantir que l’IDF ne soit jamais négatif, même pour les termes très fréquents. L’IDF joue un rôle crucial : dans la requête « comment installer Python sur Ubuntu », les mots « comment », « sur » sont quasi-universels (IDF bas), tandis que « Python » et « Ubuntu » sont discriminants (IDF haut). BM25 donne automatiquement plus de poids aux termes rares et pertinents.

TF avec saturation : les rendements décroissants

C’est l’amélioration majeure de BM25 par rapport à TF-IDF. En TF-IDF classique, si un terme apparaît 10 fois dans un document, le score est 10 fois celui d’un document où il apparaît une seule fois. C’est excessif : la dixième occurrence d’un mot n’est pas dix fois plus informative que la première.

BM25 introduit une saturation via le paramètre k1. La courbe de scoring monte rapidement pour les premières occurrences, puis s’aplatit. Concrètement :

Fréquence du terme (TF) Score TF-IDF (linéaire) Score BM25 (k1=1,2)
1 1,00 1,00
2 2,00 1,50
5 5,00 1,94
10 10,00 2,07
50 50,00 2,18
100 100,00 2,19

Avec BM25, un document qui mentionne « Python » 50 fois ne score que légèrement mieux qu’un document qui le mentionne 10 fois. C’est un rempart contre le keyword stuffing et un comportement beaucoup plus proche de l’intuition humaine : au-delà d’un certain point, les répétitions n’ajoutent plus d’information.

Le paramètre k1 contrôle la vitesse de saturation. Un k1 plus élevé (ex. 2,0) laisse le score monter plus longtemps avec la fréquence. Un k1 à 0 annule complètement l’effet de la fréquence (seul l’IDF compte). La valeur par défaut de 1,2 est un bon compromis pour la majorité des corpus.

Normalisation de la longueur du document

Un terme qui apparaît une fois dans un article de 200 mots est plus significatif qu’une occurrence dans un document de 20 000 mots. BM25 normalise le score en fonction de la longueur du document relative à la moyenne du corpus.

Le paramètre b contrôle l’intensité de cette normalisation, sur une échelle de 0 à 1 :

b = 0 : Aucune normalisation. La longueur du document n’affecte pas le score. Utile si vos documents ont tous une taille similaire.

b = 1 : Normalisation maximale. Les documents plus longs que la moyenne sont fortement pénalisés. Les documents courts sont fortement favorisés.

b = 0,75 (défaut) : Normalisation modérée. Un bon compromis qui pénalise les documents longs sans les écraser. C’est la valeur par défaut dans Elasticsearch et Lucene, et elle fonctionne bien pour la plupart des corpus.

Quand ajuster b ? Si votre corpus mélange des documents de longueurs très différentes (tweets de 280 caractères et articles de 5000 mots), le réglage de b peut avoir un impact significatif. Testez des valeurs entre 0,3 et 0,9 en évaluant avec la Rank Eval API d’Elasticsearch. Mais dans la majorité des cas, les défauts (k1=1,2, b=0,75) donnent de bons résultats sans tuning.

BM25 vs TF-IDF : les différences clés

BM25 est souvent décrit comme « TF-IDF amélioré ». C’est une simplification correcte, mais les différences concrètes méritent d’être précisées.

Critère TF-IDF BM25
Fréquence des termes Linéaire ou log (TF brut ou log(1+TF)) Saturation non-linéaire (rendements décroissants)
Normalisation longueur Absente dans la version de base Intégrée via le paramètre b
Paramètres ajustables Aucun k1 (saturation TF) et b (normalisation longueur)
Fondement théorique Heuristique statistique Modèle probabiliste de pertinence
Keyword stuffing Vulnérable (score linéaire avec la fréquence) Résistant (saturation plafonne le gain)
Requêtes longues Performance médiocre Meilleure gestion grâce à la saturation et la normalisation
Défaut dans Elasticsearch Non (remplacé depuis ES 5.0) Oui (défaut depuis ES 5.0 / Lucene 6)

En résumé : si vous utilisez Elasticsearch, Solr ou tout outil basé sur Lucene depuis 2016, vous utilisez déjà BM25 par défaut. TF-IDF reste disponible comme option, mais BM25 le surpasse de façon constante dans les benchmarks académiques et industriels.


Les variantes de BM25

BM25F (Field-weighted BM25)

La variante la plus importante en production. BM25F étend BM25 aux documents structurés avec plusieurs champs (titre, corps, tags, description). Chaque champ reçoit un poids : un match dans le titre compte plus qu’un match dans le corps du document.

Elasticsearch implémente BM25F via la requête combined_fields (disponible depuis ES 7.13). Elle traite les champs pondérés comme un seul champ synthétique pour le calcul du score, ce qui donne des résultats plus cohérents que l’ancienne approche cross_fields. Weaviate utilise aussi BM25F nativement pour son composant de recherche lexicale dans le hybrid search.

// Exemple BM25F dans Elasticsearch (combined_fields)
{
  "query": {
    "combined_fields": {
      "query": "semantic search implementation",
      "fields": ["title^3", "abstract^2", "body"],
      "operator": "or"
    }
  }
}

Ici, un match dans title pèse 3 fois plus qu’un match dans body. C’est simple et très efficace pour améliorer la pertinence sans complexité supplémentaire.

BM25L

Proposé par Lv et Zhai, BM25L corrige un biais de BM25 sur les documents longs. BM25 classique peut pénaliser excessivement les longs documents au point de les classer derrière des documents courts moins pertinents. BM25L ajuste le calcul de la fréquence des termes pour atténuer cet effet. C’est une variante surtout utilisée en recherche académique.

BM25+

BM25+ ajoute un terme additif δ (delta) au score, garantissant que même les documents longs ne peuvent pas avoir un score inférieur à un certain seuil. Cela résout le problème inverse de BM25L : un document très long qui contient un terme de la requête ne peut plus être pénalisé au point de recevoir un score quasi-nul. En pratique, BM25+ donne des scores plus élevés pour les documents courts et maintient un plancher pour les documents longs.


BM25 en pratique : implémentations

Dans Elasticsearch

BM25 est le scoring par défaut depuis Elasticsearch 5.0 (qui a adopté Lucene 6). Vous n’avez rien à configurer pour l’utiliser. Si vous souhaitez ajuster les paramètres :

// Configuration custom de BM25 dans Elasticsearch
PUT /mon_index
{
  "settings": {
    "index": {
      "similarity": {
        "default": {
          "type": "BM25",
          "k1": 1.2,
          "b": 0.75
        }
      }
    }
  }
}

Pour comprendre pourquoi un document reçoit un score donné, utilisez l’API Explain :

GET /mon_index/_explain/doc_123
{
  "query": {
    "match": {
      "content": "semantic search pipeline"
    }
  }
}

La réponse décompose le score en IDF de chaque terme, fréquence dans le document, et facteur de normalisation de longueur. C’est un outil indispensable pour débugger la pertinence.

En Python pur (rank-bm25)

Pour du prototypage ou des petits corpus, la bibliothèque rank-bm25 est la solution la plus simple :

from rank_bm25 import BM25Okapi

# 1. Préparer le corpus (tokenisé)
corpus = [
    "guide pour protéger vos plantes du gel hivernal".split(),
    "les meilleures variétés de tomates pour été".split(),
    "comment installer un système arrosage automatique".split(),
    "protéger le jardin contre le froid et le gel".split(),
]

# 2. Créer l'index BM25
bm25 = BM25Okapi(corpus)

# 3. Rechercher
query = "protéger jardin gel".split()
scores = bm25.get_scores(query)
# array([1.47, 0.00, 0.00, 1.83])

# Récupérer le meilleur résultat
best = bm25.get_top_n(query, corpus, n=2)
# ["protéger le jardin contre le froid et le gel",
#  "guide pour protéger vos plantes du gel hivernal"]

Cette bibliothèque est parfaite pour le prototypage. Pour la production, utilisez Elasticsearch, OpenSearch ou le BM25 intégré de votre base vectorielle (Weaviate, Qdrant, Milvus).

Dans Weaviate

Weaviate intègre BM25F nativement pour son composant de recherche lexicale. Vous pouvez l’utiliser seul ou combiné avec la recherche vectorielle via le hybrid search :

# Recherche BM25 pure dans Weaviate
collection = client.collections.get("Article")
response = collection.query.bm25(
    query="protéger plantes gel",
    limit=10,
    query_properties=["title^2", "content"]  # BM25F : titre pèse 2x
)

BM25 dans les pipelines RAG

Le rôle de BM25 dans les systèmes de RAG modernes est celui du premier retriever lexical dans un pipeline hybrid search. Le pattern standard :

1. BM25 retrieval : Retrouve les chunks contenant les termes exacts de la requête. Excellent pour les noms propres, les termes techniques, les identifiants.

2. Vector retrieval : Retrouve les chunks sémantiquement proches de la requête via des embeddings. Excellent pour les reformulations, les synonymes, le langage naturel.

3. Fusion (RRF) : Combine les deux listes via Reciprocal Rank Fusion.

4. Reranking : Un cross-encoder affine le classement final.

Sans BM25, le pipeline perd la précision sur les correspondances exactes. Un utilisateur qui cherche « erreur CUDA 11.8 PyTorch » a besoin que le système retrouve les documents contenant exactement ces termes. Les embeddings seuls risquent de renvoyer des résultats sur les erreurs GPU en général. BM25 garantit que la correspondance exacte est dans les candidats.

SPLADE : le successeur spirituel de BM25 ? SPLADE (Sparse Lexical and Expansion) est un modèle appris qui produit des vecteurs sparse, similaires à BM25 dans leur structure (index inversé) mais enrichis par l’apprentissage de Transformers. SPLADE peut activer des termes sémantiquement liés en plus des termes présents dans le document. Certains considèrent SPLADE comme le « BM25 neural ». En pratique, SPLADE et BM25 sont souvent complémentaires dans un pipeline à trois voies (dense + sparse + BM25 classique).

Limites de BM25

Aucune compréhension sémantique. BM25 ne comprend pas que « voiture » et « automobile » sont synonymes. Si le document utilise un mot différent de celui de la requête, BM25 ne fait pas le lien. C’est exactement pourquoi il doit être combiné avec la recherche vectorielle dans un hybrid search.

Sensible à la tokenisation. La qualité de BM25 dépend fortement de l’analyse textuelle en amont : stemming, suppression des stop words, gestion des accents, des tirets, des cas composés. Un mauvais tokenizer dégrade les résultats de façon significative.

Pas de personnalisation. BM25 traite toutes les requêtes de la même façon, sans tenir compte du contexte utilisateur, de son historique ou de ses préférences.

Pas de compréhension du contexte. « Java » renvoie aussi bien des résultats sur le langage de programmation que sur l’île indonésienne. BM25 ne désambiguïse pas.

Scores non bornés. Les scores BM25 varient selon le corpus, la requête et la distribution des termes. Il n’y a pas de notion de « score absolu de pertinence ». Cela complique la combinaison avec d’autres signaux de scoring (d’où l’utilité de RRF qui travaille sur les rangs plutôt que les scores).


Verdict

BM25 est l’un de ces rares algorithmes qui traversent les décennies sans être remplacés. Sa force tient à sa simplicité, sa robustesse et sa complémentarité parfaite avec les approches neuronales. En 2026, il n’est pas en compétition avec les embeddings, il en est le partenaire. Tout système de recherche sérieux utilise BM25, que ce soit directement (Elasticsearch, Solr) ou intégré dans une base vectorielle (Weaviate, Qdrant, Milvus).

Si vous construisez un pipeline de recherche ou de RAG, ne négligez pas BM25. Les défauts (k1=1,2, b=0,75) fonctionnent dans la plupart des cas. Activez le hybrid search dans votre base vectorielle, et BM25 fera le travail de précision lexicale pendant que les vecteurs gèrent la compréhension sémantique. C’est un algorithme de 1994 qui résout des problèmes de 2026, et c’est exactement ce qui fait sa beauté.


Questions fréquentes sur BM25

Quelle est la différence entre BM25 et TF-IDF ?

BM25 est une évolution de TF-IDF avec deux améliorations majeures. Premièrement, la saturation de la fréquence des termes : en TF-IDF, le score augmente linéairement avec le nombre d’occurrences d’un mot, ce qui favorise le keyword stuffing. En BM25, les rendements sont décroissants grâce au paramètre k1. Deuxièmement, la normalisation de la longueur du document : BM25 pénalise les documents plus longs que la moyenne via le paramètre b, ce que TF-IDF classique ne fait pas. BM25 a aussi un fondement probabiliste plus rigoureux. Depuis Elasticsearch 5.0 et Lucene 6, BM25 a remplacé TF-IDF comme algorithme par défaut.

Comment régler les paramètres k1 et b de BM25 ?

Les valeurs par défaut (k1=1,2, b=0,75) fonctionnent bien pour la majorité des corpus. Elastic recommande d’optimiser d’abord la structure de vos requêtes, les synonymes, l’analyse textuelle et le boosting avant de toucher à k1 et b. Si vous devez les ajuster, utilisez la Rank Eval API d’Elasticsearch pour évaluer chaque changement sur un jeu de test. Typiquement, b optimal se situe entre 0,3 et 0,9 (testez par incréments de 0,1), et k1 entre 0,5 et 2,0. Un k1 plus haut donne plus de poids aux documents avec de nombreuses occurrences du terme. Un b plus bas réduit la pénalité sur les documents longs.

BM25 est-il encore utile avec la recherche vectorielle ?

Absolument. BM25 et la recherche vectorielle sont complémentaires, pas concurrents. BM25 excelle pour les correspondances exactes (noms propres, codes, identifiants techniques) là où les embeddings échouent. La recherche vectorielle excelle pour les reformulations et les synonymes là où BM25 échoue. Le hybrid search, qui combine les deux via RRF, surpasse systématiquement chaque méthode utilisée seule dans les benchmarks. C’est pourquoi les principales bases vectorielles (Weaviate, Qdrant, Elasticsearch, Milvus) intègrent BM25 nativement. BM25 est le composant de « précision lexicale » indispensable des pipelines de RAG modernes.

Qu’est-ce que BM25F et quand l’utiliser ?

BM25F (Field-weighted BM25) est une extension de BM25 pour les documents structurés avec plusieurs champs. Elle permet d’attribuer des poids différents à chaque champ : par exemple, un match dans le titre peut peser 3 fois plus qu’un match dans le corps du document. Elasticsearch l’implémente via la requête combined_fields (depuis ES 7.13). Utilisez BM25F dès que vos documents ont des champs de pertinence inégale (titre vs corps, description vs tags), ce qui est le cas de la quasi-totalité des applications de recherche réelles.

Quel est le rôle de BM25 dans un pipeline RAG ?

Dans un pipeline RAG, BM25 joue le rôle de premier retriever lexical. Quand un utilisateur pose une question, BM25 retrouve rapidement les chunks de documents contenant les termes exacts de la requête. En parallèle, la recherche vectorielle retrouve les chunks sémantiquement proches. Les deux listes sont fusionnées par RRF, puis un cross-encoder affine le classement. Sans BM25, le pipeline risque de rater les correspondances exactes (erreur spécifique, numéro de version, nom de fonction). C’est le filet de sécurité lexical qui garantit un recall élevé dans les systèmes de recherche hybrides.

Polydesk.ai — Footer