Module 10 — Projet : classificateur d'avis en français
Les neuf modules précédents ont introduit chacun une brique. Ce module les met bout à bout sur le corpus d'avis, dans un ordre qui reproduit ce que l'on ferait en entreprise : constituer le jeu, poser la référence TF-IDF, itérer vers un modèle plus lourd, mesurer le coût. Le fil rouge insiste sur les spécificités du français — élision, accents, féminin-masculin, mots composés — qui, sur ce projet, font la différence entre un modèle qui marche et un modèle qui ronronne.
Spécifier le problème avant tout code
Le projet est simple à formuler et compliqué à cadrer. « Prédire la note » n'a pas la même signification selon l'usage aval. Trois définitions possibles :
- Classification binaire (positif contre négatif) : suffisante pour un tableau de bord, résiste bien au bruit.
- Classification à trois classes (négatif, neutre, positif) : correspond à une lecture humaine typique.
- Régression sur cinq classes (1 à 5) : la plus fine, la plus difficile, la plus fragile.
Sur nos avis, la classe « 3 étoiles » est presque toujours mal reconnue quel que soit le modèle (module 6). Le compromis pratique est le suivant : entraîner en cinq classes, mais évaluer en trois — en regroupant 1-2, 3 seul, 4-5. Cette dissociation évite de sacrifier l'information à l'entraînement tout en donnant une métrique lisible.
Le protocole : partition, référence, itérations
Un projet NLP suit toujours le même squelette. Le respecter évite la moitié des erreurs.
from sklearn.model_selection import train_test_split
# 80 % entraînement, 10 % validation, 10 % test — jamais touché avant la fin
X_temp, X_test, y_temp, y_test = train_test_split(
corpus, y, test_size=0.10, stratify=y, random_state=42)
X_train, X_valid, y_train, y_valid = train_test_split(
X_temp, y_temp, test_size=0.111, stratify=y_temp, random_state=42)
Le jeu de test ne se regarde qu'une fois à la fin, sur le meilleur modèle sélectionné par la validation. Le regarder à chaque itération le contamine, et le score final devient un score d'entraînement déguisé.
Nettoyage adapté au français : la spécificité qui pèse
Le module 1 a exposé les cinq décisions. Sur le français, trois méritent une attention particulière.
L'élision. « L'hôtel », « d'un », « n'était » : l'apostrophe soude un mot vide au mot suivant. TF-IDF les traite comme un mot unique (« l'hôtel ») s'il n'est pas séparé, ce qui gonfle le vocabulaire (« l'hôtel », « d'hôtel », « hôtel » deviennent trois entrées différentes pour le même sens). La séparation par une expression régulière ramène tout à « hôtel ».
Les accents. Une saisie utilisateur mêle « hotel » et « hôtel ». Sur nos données, environ 8 % des avis omettent au moins un accent. Deux stratégies : conserver les accents et espérer que le vocabulaire couvre les deux formes ; désaccentuer systématiquement à l'aide de unidecode. La deuxième stratégie améliore TF-IDF de deux points, mais dégrade CamemBERT de trois points — le modèle a été préentraîné sur du texte accentué.
from unidecode import unidecode
# À utiliser SEULEMENT pour la référence TF-IDF, jamais pour un Transformer
avis_sans_accent = [unidecode(a) for a in corpus]
Les mots composés et les accords. « Petit-déjeuner », « rez-de-chaussée » sont un seul concept. Un tokeniseur naïf les découpe en trois. Une liste minimale d'expressions figées, appliquée avant tokenisation, résout la majorité des cas. Pour les accords féminin-masculin (« impeccable » invariant, « adorable » invariant, « propre » invariant mais « propres » pluriel), la lemmatisation du module 1 les ramène à leur forme canonique.
Trois approches, un même jeu de test
On construit trois modèles sur exactement les mêmes indices d'entraînement et de test.
Approche 1 — TF-IDF plus régression logistique (module 3) :
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
vec = TfidfVectorizer(ngram_range=(1, 2), min_df=5, sublinear_tf=True,
strip_accents=None) # accents conservés
Xt = vec.fit_transform(X_train)
clf = LogisticRegression(max_iter=1000, C=1.0, class_weight="balanced")
clf.fit(Xt, y_train)
Approche 2 — Sentence-BERT plus régression logistique (module 5) :
from sentence_transformers import SentenceTransformer
sbert = SentenceTransformer("dangvantuan/sentence-camembert-base")
Xt2 = sbert.encode(X_train, normalize_embeddings=True, batch_size=64)
clf2 = LogisticRegression(max_iter=1000, class_weight="balanced")
clf2.fit(Xt2, y_train)
Approche 3 — CamemBERT affiné (module 6) : voir le code complet du module 6, avec pondération de la perte.
Le tableau qui décide
Sur nos dix mille avis, la comparaison donne :
| Approche | F1 macro (test) | Entraînement | Inférence CPU | Inférence GPU | Coût mensuel indicatif |
|---|---|---|---|---|---|
| TF-IDF + LR | 0,52 | 12 s | 1 ms | — | 5 € |
| Sentence-BERT + LR | 0,58 | 6 min (GPU) | 40 ms | 5 ms | 40 € |
| CamemBERT affiné | 0,63 | 25 min (GPU) | 200 ms | 15 ms | 120 € |
Le coût mensuel indicatif suppose un flux de 100 000 avis par jour, avec une facturation GPU cloud de 0,50 € par heure. Deux enseignements :
- Sentence-BERT capture les deux tiers du gain de CamemBERT pour un tiers du coût. C'est presque toujours la première étape à privilégier avant l'affinage.
- Passer de TF-IDF à CamemBERT multiplie le coût par 24 pour un gain de 11 points de F1. La question à poser à l'utilisateur métier : combien vaut un point de F1 en euros ?
Analyse d'erreurs : ce que la métrique n'affiche pas
La métrique globale cache la nature des erreurs. Sur le meilleur modèle, on inspecte les cas mal classés et on les range en catégories :
- Sarcasme (« Un accueil digne d'un ministère, on adore l'attente ») : signal 1 étoile avec vocabulaire de 4 étoiles. Aucun modèle ne le résout sans données annotées explicitement.
- Avis contradictoires (« Chambre parfaite mais bruit toute la nuit ») : le modèle hésite entre 3 et 4, l'humain aussi.
- Fautes graves de saisie : soit on nettoie avec un correcteur orthographique en amont, soit on tolère l'erreur.
- Références culturelles (« Un service à la française », dit avec ironie) : le contexte culturel manque au modèle multilingue, moins au modèle français dédié.
Cette analyse guide les prochaines itérations bien mieux que la métrique agrégée. Elle indique où annoter davantage, pas quel modèle plus gros essayer.
Une fois qu'on regarde les erreurs pour améliorer, le jeu inspecté cesse d'être un jeu de test honnête pour les prochaines itérations. Toujours analyser sur la validation, garder le test intact jusqu'à la mesure finale.
Servir le modèle : dernière étape et premier piège
Un modèle affiné en Python n'est utile que si un service HTTP peut l'appeler. Deux options courantes :
- FastAPI plus PyTorch : simple, contrôle total, mais latence de chargement d'un modèle CamemBERT-base d'environ trois secondes. Un service qui redémarre à chaque déploiement doit précharger le modèle.
- Text Generation Inference (ou
Text Embeddings Inference) : conteneur optimisé, quantisation gratuite, sert dix fois plus de requêtes par seconde. C'est le choix par défaut en production pour de gros volumes.
Le classificateur en production reçoit du texte brut d'un formulaire web. Toutes les décisions du module 1 (normalisation NFC, séparation de l'élision, remplacement des URL) doivent être appliquées avant l'appel du modèle, et exactement de la même façon qu'à l'entraînement. Encapsuler ce prétraitement dans une seule fonction, importée dans le code d'entraînement et dans le code de service, évite toute divergence.
En résumé
- Le problème doit être cadré avant tout code : cinq classes à l'entraînement mais évaluation à trois est souvent le bon compromis pour un cas m étier.
- Les spécificités du français — élision, accents, mots composés — pèsent sur la référence TF-IDF plus qu'on ne le pense ; sur les modèles préentraînés, garder le texte tel quel.
- Sur le corpus d'avis, Sentence-BERT capture les deux tiers du gain de CamemBERT pour un tiers du coût ; l'affinage n'est justifié que si les euros valent le point de F1 gagné.
- L'analyse d'erreurs classée par cause (sarcasme, contradiction, saisie, culture) guide mieux les prochaines itérations que la métrique agrégée.
Module suivant : le récapitulatif de tout le cours, l'arbre de décision « quelle approche pour quelle tâche » et l'annonce de l'examen final.