TF-IDF (Term Frequency-Inverse Document Frequency)
TF-IDF est une méthode statistique qui évalue l’importance d’un mot dans un document par rapport à un corpus (ensemble de documents). Elle combine la fréquence d’un terme dans un document (TF) avec sa rareté à travers le corpus (IDF) pour attribuer un poids qui distingue les mots véritablement informatifs des mots banals.
Concrètement : si le mot « Python » apparaît souvent dans un article mais rarement dans les autres articles du corpus, son score TF-IDF sera élevé pour cet article. Si le mot « le » apparaît partout, son score sera quasi nul. TF-IDF transforme du texte brut en vecteurs numériques exploitables par les algorithmes de machine learning, et reste la méthode de référence pour la recherche d’information, la classification de texte et l’extraction de mots-clés.
- Catégorie
- Représentation vectorielle de texte / NLP
- Type
- Pondération statistique (fréquence × rareté)
- Formule
- TF-IDF(t,d,D) = TF(t,d) × IDF(t,D)
- Librairies
- scikit-learn (TfidfVectorizer), Gensim, Elasticsearch, Apache Solr/Lucene
- Successeur
- BM25 (Okapi), word embeddings, BERT
- Inventé
- Karen Spärck Jones (1972, IDF) + combinaison TF×IDF formalisée dans les années 1970-80
La formule TF-IDF décortiquée
TF-IDF est le produit de deux composantes indépendantes. Chacune répond à une question différente.
Term Frequency (TF) : le mot est-il important dans CE document ?
La fréquence du terme mesure combien de fois un mot apparaît dans un document donné, normalisé par le nombre total de mots du document :
TF(t, d) = nombre d'occurrences de t dans d / nombre total de mots dans d
Si le mot « Python » apparaît 5 fois dans un article de 200 mots, son TF est 5/200 = 0,025. Cette normalisation est essentielle : sans elle, les documents longs seraient systématiquement favorisés simplement parce qu’ils contiennent plus de mots.
Il existe des variantes de TF. La forme logarithmique TF(t,d) = 1 + log(f(t,d)) atténue l’effet des fréquences très élevées : un mot qui apparaît 100 fois n’est pas 100 fois plus important qu’un mot qui apparaît une fois. scikit-learn propose cette variante via le paramètre sublinear_tf=True.
Inverse Document Frequency (IDF) : le mot est-il rare dans LE CORPUS ?
L’IDF mesure la rareté d’un terme à travers l’ensemble du corpus :
IDF(t, D) = log(N / df(t))
Où N est le nombre total de documents et df(t) le nombre de documents contenant le terme t. Un mot présent dans tous les documents (comme « le » ou « the ») aura un IDF proche de zéro. Un mot présent dans un seul document aura un IDF maximal.
En pratique, scikit-learn utilise une version lissée (smoothed IDF) pour éviter la division par zéro et stabiliser les scores : IDF(t) = log((1 + N) / (1 + df(t))) + 1. Ce lissage ajoute 1 au numérateur et au dénominateur, puis ajoute 1 au résultat final.
Le produit TF × IDF
Le score TF-IDF final est simplement le produit des deux composantes :
TF-IDF(t, d, D) = TF(t, d) × IDF(t, D)
Un mot fréquent dans un document spécifique mais rare dans le corpus obtient un score élevé. Un mot fréquent partout (« le », « de », « est ») obtient un score faible car son IDF est proche de zéro. C’est précisément ce mécanisme qui rend TF-IDF efficace sans avoir besoin de supprimer explicitement les stopwords : ils se retrouvent naturellement avec des poids négligeables.
Implémentation Python avec scikit-learn
Usage basique avec TfidfVectorizer
scikit-learn fournit TfidfVectorizer, qui combine la tokenisation, le comptage de fréquences et le calcul TF-IDF en une seule classe :
from sklearn.feature_extraction.text import TfidfVectorizer
corpus = [
"L'intelligence artificielle transforme le monde",
"Le machine learning est une branche de l'intelligence artificielle",
"Python est le langage le plus utilisé en data science",
"Les réseaux de neurones sont au coeur du deep learning"
]
vectorizer = TfidfVectorizer()
X = vectorizer.fit_transform(corpus)
# Vocabulaire extrait
print(vectorizer.get_feature_names_out())
# ['artificielle', 'au', 'branche', 'coeur', 'data', 'de', 'deep',
# 'du', 'en', 'est', 'intelligence', 'langage', 'le', 'learning',
# 'les', 'machine', 'monde', 'neurones', 'plus', 'python',
# 'réseaux', 'science', 'sont', 'transforme', 'une', 'utilisé']
# Matrice TF-IDF (sparse)
print(X.shape) # (4, 26) : 4 documents, 26 termes uniques
Paramètres clés de TfidfVectorizer
Les paramètres les plus importants à maîtriser :
vectorizer = TfidfVectorizer(
stop_words='english', # Suppression des stopwords (ou liste custom)
ngram_range=(1, 2), # Unigrams + bigrams
max_df=0.95, # Ignorer les termes présents dans 95%+ des docs
min_df=2, # Ignorer les termes présents dans moins de 2 docs
max_features=10000, # Limiter le vocabulaire à 10 000 termes
sublinear_tf=True, # TF logarithmique (recommandé)
norm='l2', # Normalisation L2 (par défaut)
smooth_idf=True # Lissage IDF (par défaut)
)
Le paramètre sublinear_tf=True est particulièrement recommandé : il applique 1 + log(tf) au lieu du TF brut, ce qui atténue l’effet des mots qui apparaissent très souvent dans un document. La normalisation L2 (par défaut) garantit que chaque vecteur document a une norme unitaire, ce qui facilite le calcul de similarité cosinus.
TF-IDF avec n-grams
Par défaut, TfidfVectorizer travaille avec des unigrams (mots individuels). Le paramètre ngram_range permet d’inclure des n-grams (séquences de mots consécutifs) pour capturer plus de contexte :
vectorizer = TfidfVectorizer(ngram_range=(1, 2))
X = vectorizer.fit_transform(corpus)
# Les bigrams comme "intelligence artificielle" ou "machine learning"
# sont maintenant des features à part entière
Les bigrams comme « intelligence artificielle » ou « machine learning » capturent des expressions composées que les unigrams seuls ne peuvent pas représenter. Attention cependant : inclure des trigrams ou plus explose la dimensionnalité du vocabulaire.
Bag-of-Words vs TF-IDF
Le bag-of-words (sac de mots) est la version simplifiée : il compte les occurrences de chaque mot sans pondération. Le mot « le » qui apparaît 50 fois reçoit un poids de 50, ce qui noie les termes réellement informatifs.
TF-IDF résout ce problème en introduisant l’IDF qui pénalise les mots omniprésents. C’est pourquoi TF-IDF a largement remplacé le bag-of-words pur dans les applications pratiques. scikit-learn fournit CountVectorizer pour le bag-of-words et TfidfVectorizer pour TF-IDF ; dans la quasi-totalité des cas, TF-IDF donne de meilleurs résultats.
| Critère | Bag-of-Words | TF-IDF |
|---|---|---|
| Pondération | Comptage brut | Fréquence × rareté |
| Stopwords | Poids élevé (fréquents) | Poids faible (IDF ≈ 0) |
| Mots rares | Sous-représentés | Valorisés (IDF élevé) |
| Performance classification | Correcte | Supérieure |
| Contexte sémantique | Aucun | Aucun |
| scikit-learn | CountVectorizer | TfidfVectorizer |
BM25 : le successeur de TF-IDF pour la recherche
Okapi BM25 (Best Matching 25), développé dans les années 1990, est une évolution probabiliste de TF-IDF qui corrige deux faiblesses majeures.
Premièrement, la saturation de la fréquence. En TF-IDF classique, si un mot apparaît 100 fois, son poids est 100 fois plus élevé que s’il apparaît une fois. BM25 introduit un paramètre k1 (par défaut 1,2 dans Elasticsearch) qui contrôle cette saturation : au-delà d’un certain nombre d’occurrences, les apparitions supplémentaires contribuent de moins en moins au score. C’est plus intuitif : un document qui mentionne « Python » 100 fois n’est pas 100 fois plus pertinent qu’un document qui le mentionne une fois.
Deuxièmement, la normalisation par la longueur du document. Le paramètre b (par défaut 0,75) contrôle à quel point la longueur du document est prise en compte. Un document plus long que la moyenne est légèrement pénalisé, un document plus court est légèrement avantagé. TF-IDF classique ne fait pas cette correction.
BM25 est l’algorithme de scoring par défaut d’Elasticsearch, Apache Solr et Apache Lucene. Si vous faites de la recherche d’information, c’est BM25 qui tourne sous le capot, pas TF-IDF pur.
Cas d’usage en production
Classification de texte
C’est le cas d’usage classique. Combiné avec un classifieur comme Naive Bayes, SVM ou régression logistique, TF-IDF produit des résultats solides pour la détection de spam, la catégorisation d’articles, la classification de tickets support et l’analyse de sentiment. Des études récentes montrent qu’un modèle bien optimisé TF-IDF + régression logistique atteint encore 81 % de précision sur des datasets multi-classes, ce qui en fait une baseline robuste avant de passer à des approches plus complexes.
Moteurs de recherche
TF-IDF est la fondation sur laquelle reposent les moteurs de recherche modernes. Même si Elasticsearch utilise BM25 par défaut, le principe reste le même : pondérer les termes par leur importance relative. La recherche hybride combinant BM25 (rappel lexical) et embeddings vectoriels (pertinence sémantique) est la tendance dominante dans les systèmes de RAG.
Extraction de mots-clés
Les mots avec les scores TF-IDF les plus élevés dans un document sont ses mots-clés les plus discriminants. C’est une méthode simple et efficace pour le résumé extractif, le tagging automatique et l’indexation.
Similarité de documents
En transformant chaque document en vecteur TF-IDF, vous pouvez calculer la similarité cosinus entre paires de documents pour la détection de duplicats, la recommandation de contenu et le clustering thématique.
Limites de TF-IDF
Aucune compréhension sémantique
TF-IDF ne capture pas le sens des mots. « voiture » et « automobile » sont deux termes complètement indépendants, même s’ils sont synonymes. « bank » dans « river bank » et « bank account » reçoit le même poids, sans distinction de sens. C’est la limite fondamentale de toutes les approches bag-of-words.
Pas de prise en compte de l’ordre des mots
« le chat mange la souris » et « la souris mange le chat » produisent le même vecteur TF-IDF. L’utilisation de n-grams atténue partiellement ce problème en capturant des séquences courtes, mais la structure syntaxique complète est perdue.
Haute dimensionnalité et matrices creuses
Un corpus de taille moyenne peut facilement produire un vocabulaire de 50 000 à 100 000 termes. Chaque document est alors représenté par un vecteur de cette taille, dont la plupart des valeurs sont nulles (matrice creuse). scikit-learn gère cela efficacement avec des matrices sparse, mais la dimensionnalité reste un défi pour les grands corpus.
TF-IDF vs word embeddings vs transformers
L’évolution historique des représentations textuelles suit une progression claire : les méthodes statistiques éparses (bag-of-words, TF-IDF) ont cédé la place aux vecteurs denses statiques (Word2Vec, GloVe, FastText), eux-mêmes supplantés par les modèles contextuels (BERT, GPT). Chaque génération résout les limites de la précédente.
| Critère | TF-IDF | Word2Vec/GloVe | BERT/Transformers |
|---|---|---|---|
| Type de vecteur | Épars (sparse) | Dense, statique | Dense, contextuel |
| Sémantique | Aucune | Similarité par cooccurrence | Compréhension contextuelle |
| Polysémie | Non gérée | Non gérée (1 vecteur par mot) | Gérée (vecteur par contexte) |
| Vitesse | Très rapide | Rapide | Lent (GPU recommandé) |
| Interprétabilité | Totale (poids = importance) | Partielle | Faible (boîte noire) |
| Ressources | CPU suffit | CPU suffit | GPU recommandé |
| Baseline solide | Oui | Oui | État de l’art |
Malgré cette évolution, TF-IDF n’est pas obsolète. Il reste la méthode recommandée comme baseline pour toute tâche de classification ou de recherche. Les nouvelles méthodes NLP sont systématiquement comparées à une baseline TF-IDF pour démontrer leur valeur ajoutée. Et dans beaucoup de cas pratiques (corpus petits, contraintes de latence, besoin d’interprétabilité), TF-IDF reste le meilleur choix.
TF-IDF dans Elasticsearch
Elasticsearch a remplacé TF-IDF par BM25 comme algorithme de scoring par défaut depuis la version 5.0. Cependant, les principes restent identiques : pondération par fréquence du terme et rareté dans le corpus, avec normalisation par la longueur du document en plus.
Si vous avez besoin de TF-IDF pur dans Elasticsearch (cas rare), vous pouvez configurer la similarité :
PUT /mon_index
{
"settings": {
"index": {
"similarity": {
"default": {
"type": "classic"
}
}
}
}
}
Mais dans 99 % des cas, BM25 est préférable car il gère mieux les documents de longueurs variables et la saturation des fréquences élevées.
Pipeline complet : de texte brut à la classification
Voici un pipeline complet qui utilise TF-IDF pour classifier des textes français avec scikit-learn :
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report
# Corpus d'exemple (remplacer par vos données)
textes = [
"Le nouveau modèle GPT excelle en raisonnement",
"Les actions technologiques chutent en bourse",
"Claude Sonnet améliore la génération de code",
"La BCE relève ses taux directeurs",
# ... plus de données
]
labels = ["tech", "finance", "tech", "finance"]
# 1. Vectorisation TF-IDF
vectorizer = TfidfVectorizer(
sublinear_tf=True,
max_df=0.95,
min_df=2,
ngram_range=(1, 2)
)
# 2. Split train/test
X_train, X_test, y_train, y_test = train_test_split(
textes, labels, test_size=0.2, random_state=42
)
# 3. Fit sur le train, transform sur les deux
X_train_tfidf = vectorizer.fit_transform(X_train)
X_test_tfidf = vectorizer.transform(X_test)
# 4. Classification
clf = LogisticRegression(max_iter=1000)
clf.fit(X_train_tfidf, y_train)
# 5. Évaluation
y_pred = clf.predict(X_test_tfidf)
print(classification_report(y_test, y_pred))
Ce pipeline est volontairement minimaliste. En production, vous ajouteriez le prétraitement (suppression de stopwords, lemmatisation), la validation croisée, et l’optimisation des hyperparamètres du vectorizer et du classifieur. Mais même dans cette forme simple, TF-IDF + régression logistique constitue une baseline redoutablement efficace que beaucoup de modèles plus complexes peinent à battre sur des corpus de petite ou moyenne taille.
Bonnes pratiques
Activez toujours sublinear_tf=True : la forme logarithmique du TF produit systématiquement de meilleurs résultats que le TF brut. Utilisez max_df et min_df pour éliminer les extrêmes : les mots trop fréquents (présents dans 95 %+ des documents) et les mots trop rares (présents dans moins de 2 documents) n’apportent rien à la classification. Testez les bigrams (ngram_range=(1,2)) : ils capturent les expressions composées comme « intelligence artificielle » ou « machine learning » qui sont perdues avec les unigrams seuls. Si la dimensionnalité devient un problème, limitez le vocabulaire avec max_features ou appliquez une réduction par SVD tronquée (LSA). Enfin, combinez TF-IDF avec la suppression de stopwords et la lemmatisation en amont pour des résultats optimaux.
Questions fréquentes sur TF-IDF
Comment fonctionne TF-IDF simplement ?
TF-IDF multiplie deux scores pour chaque mot dans chaque document. Le premier score (TF) mesure la fréquence du mot dans le document. Le second score (IDF) mesure la rareté du mot dans le corpus entier. Résultat : un mot fréquent dans un document mais rare dans le corpus obtient un score élevé, tandis qu’un mot courant partout (comme « le » ou « de ») obtient un score proche de zéro. C’est ce qui rend TF-IDF efficace pour identifier les termes distinctifs de chaque document.
Quelle est la différence entre TF-IDF et BM25 ?
BM25 est une évolution probabiliste de TF-IDF qui ajoute deux améliorations majeures : la saturation de la fréquence des termes (un mot qui apparaît 100 fois ne compte pas 100 fois plus qu’un mot qui apparaît une fois) et la normalisation par la longueur du document. BM25 est l’algorithme par défaut d’Elasticsearch et de la plupart des moteurs de recherche. Utilisez TF-IDF pour la classification et le feature engineering (scikit-learn), BM25 pour la recherche et le ranking de documents.
TF-IDF est-il encore utile avec les LLM et BERT ?
Oui. TF-IDF reste pertinent comme baseline de comparaison (toute nouvelle méthode est comparée à TF-IDF), pour les projets avec contraintes de ressources (pas de GPU nécessaire), quand l’interprétabilité est requise (vous voyez exactement quels mots contribuent au score), et dans les systèmes hybrides de RAG combinant recherche lexicale (TF-IDF/BM25) et recherche sémantique (embeddings). Les LLM ne rendent pas TF-IDF obsolète : ils le complètent.
Comment implémenter TF-IDF en Python ?
La méthode standard utilise scikit-learn : from sklearn.feature_extraction.text import TfidfVectorizer, puis vectorizer = TfidfVectorizer() et X = vectorizer.fit_transform(corpus). Les paramètres recommandés sont sublinear_tf=True (TF logarithmique), max_df=0.95 (ignorer les mots trop fréquents) et min_df=2 (ignorer les mots trop rares). Pour les bigrams, ajoutez ngram_range=(1,2).
Quelle est la différence entre TF-IDF et bag-of-words ?
Le bag-of-words compte simplement les occurrences de chaque mot, sans pondération. TF-IDF ajoute l’IDF qui pénalise les mots courants et valorise les mots rares. Résultat : en bag-of-words, le mot « le » qui apparaît 50 fois domine le vecteur. En TF-IDF, son poids est quasi nul car il apparaît dans tous les documents (IDF ≈ 0). TF-IDF produit systématiquement de meilleurs résultats pour la classification et la recherche.