Réponse courte
Comparez l’ancien et le nouveau sur le même jeu de test intact, comptez les exemples que chacun est seul à réussir, placez un intervalle de confiance sur la différence et répétez avec d’autres graines. Dans un test mesuré sur des nombres purement aléatoires, une erreur de fuite courante a affiché 84 % d’exactitude alors que la réponse honnête était 51 %.
Question de suivi Que doit contenir une note de résultats pour qu’une autre personne puisse s’y fier?
Un chiffre plus élevé sur une seule exécution est une preuve faible. Les modèles s’entraînent à partir de points de départ aléatoires, les tests contiennent un nombre limité d’exemples et les gens ont tendance à cesser de regarder quand le score monte. Chacun de ces éléments peut vous offrir une « amélioration » qui disparaît au prochain essai.
Ce projet mesure directement quatre pièges, à l’aide des chiffres de scikit-learn et d’un ensemble de nombres purement aléatoires. Le script compagnon, 04_honest_testing.py, s’est exécuté ici en cinq secondes environ sur un Apple M5 Max avec deux fils de processeur. Pour tester les modèles de langage en particulier, y compris les bits par octet et les bancs d’essai, voyez comment savoir si un modèle de langage que vous avez entraîné est bon. Cet article couvre ce qui s’applique à tout modèle.
Piège 1 : la chance a l’air d’un progrès
Mêmes données, même recette, 20 graines aléatoires différentes. Entraînement sur 300 images de chiffres et test sur 300 autres :
| Mesure | Valeur |
|---|---|
| Exactitude la plus basse | 94,33 % |
| Exactitude la plus haute | 97,33 % |
| Écart type | 0,85 point |
Une « amélioration » de trois points peut donc apparaître sans aucun changement, sauf la graine. Avant de comparer deux idées, voyez de combien le score bouge quand rien d’autre ne change. Un changement plus petit que cette bande n’est pas encore une preuve.
Piège 2 : comparer des totaux plutôt que des exemples
Deux modèles testés sur les mêmes 300 images vous en disent plus que deux pourcentages. Comptez leurs désaccords.
import numpy as np
def compare(a_right, b_right, rounds=5000, seed=0):
"""a_right et b_right : tableaux de True/False, un par exemple de test."""
rng, n = np.random.default_rng(seed), len(a_right)
diffs = []
for _ in range(rounds):
i = rng.integers(0, n, n) # rééchantillonner le jeu de test
diffs.append(b_right[i].mean() - a_right[i].mean())
return {"only_a": int((a_right & ~b_right).sum()),
"only_b": int((b_right & ~a_right).sum()),
"ci95": np.percentile(diffs, [2.5, 97.5]).round(4)}
C’est un bootstrap apparié : il réutilise le jeu de test bien des fois, tiré au hasard avec remise, pour voir à quel point la différence oscillerait avec un autre échantillon. Mesuré ici :
| Comparaison | A | B | A seul juste | B seul juste | Intervalle à 95 % pour B − A |
|---|---|---|---|---|---|
| Petit ajustement du réglage de régularisation | 96,67 % | 96,00 % | 2 | 0 | −1,67 à 0,00 point |
| Autre famille de modèle (machine à vecteurs de support) | 96,67 % | 98,33 % | 1 | 6 | 0,00 à +3,33 points |
Le second résultat paraît nettement meilleur : 6 exemples contre 1. Pourtant, l’intervalle touche zéro. Un compte rendu honnête dit « probablement meilleur, et un test plus grand trancherait ». Seules sept images sur 300 distinguent les deux modèles. Tout repose sur ces sept-là, une bonne raison de les lire.
Piège 3 : le jeu de test s’use
Chaque fois que vous regardez un score de test et réagissez, une partie du jeu de test s’infiltre dans vos décisions. Pour le mesurer, le script a essayé 60 réglages sur un petit jeu d’entraînement, gardé celui qui obtenait le meilleur score sur un test de 60 images, puis vérifié ce gagnant sur 900 images fraîches. Il a répété l’opération 20 fois.
| Exactitude moyenne | |
|---|---|
| Rapportée : le meilleur de 60 sur le petit test | 92,67 % |
| Mêmes gagnants sur des données fraîches | 91,50 % |
L’écart de 1,2 point est modeste ici, parce que ces réglages ne diffèrent pas énormément et que les chiffres sont faciles. Il grandit avec plus d’essais, des tests plus petits et des problèmes plus difficiles. Gardez trois piles : les données d’entraînement, un jeu de développement que vous pouvez consulter librement et un test final que vous ouvrez rarement. Si vous changez quoi que ce soit à cause du test final, il est devenu un jeu de développement.
Piège 4 : la fuite transforme le bruit en compétence
La fuite de données survient quand de l’information indisponible au moment de la prédiction entre dans la construction du modèle. C’est le résultat le plus spectaculaire du projet. Les données sont 100 lignes de 2 000 variables aléatoires avec des étiquettes aléatoires : rien ne peut être appris et la vraie compétence est de 50 %.
from sklearn.feature_selection import SelectKBest, f_classif
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
from sklearn.pipeline import make_pipeline
# FAUX : choisir les variables avec toutes les lignes, celles de test comprises
chosen = SelectKBest(f_classif, k=20).fit(N, Y).transform(N)
leaky = cross_val_score(LogisticRegression(max_iter=500), chosen, Y, cv=5).mean()
# JUSTE : le choix des variables fait partie de l'entraînement, refait dans chaque partage
honest_model = make_pipeline(SelectKBest(f_classif, k=20), LogisticRegression(max_iter=500))
honest = cross_val_score(honest_model, N, Y, cv=5).mean()
| Méthode, moyenne sur 20 tirages | Exactitude sur du pur bruit |
|---|---|
| Variables choisies avant le partage | 84,3 % |
| Variables choisies dans chaque partage | 50,9 % |
Mêmes données, une ligne placée ailleurs, et un modèle inutile affiche 84 %. Formes courantes dans la vraie vie : la mise à l’échelle sur toutes les données, des quasi-doublons des deux côtés du partage et une variable qui n’existe qu’une fois le résultat connu, comme « remboursement émis » pour prédire les demandes de remboursement.
Deux vérifications peu coûteuses en attrapent beaucoup. Entraînez avec des étiquettes mélangées : un pipeline honnête doit tomber au niveau du hasard, et il a obtenu ici 8,67 % pour un hasard de 10 %. Et comparez avec une référence : toujours répondre le chiffre le plus courant a obtenu 10,33 % à ce test.
À essayer : ajoutez un test : exécutez votre meilleur modèle actuel avec des étiquettes d’entraînement mélangées. Si le score ne tombe pas au niveau du hasard, arrêtez tout et cherchez une fuite.
Que doit contenir une note de résultats pour qu’une autre personne puisse s’y fier?
Six lignes suffisent, et elles s’écrivent en quelques minutes :
| Ligne | Exemple |
|---|---|
| Changement | Régression logistique remplacée par une machine à vecteurs de support |
| Données et partage | Chiffres, 300 entraînement, 300 test, stratifié, graine 1, intact |
| Références | Classe la plus courante 10,33 %; modèle précédent 96,67 % |
| Résultat | 98,33 %; nouveau seul juste 6, ancien seul juste 1; intervalle à 95 % de 0,00 à +3,33 points |
| Vérification du bruit | La même recette sur 20 graines a bougé de 3 points; étiquettes mélangées tombées au hasard |
| Décision | Probablement meilleur; confirmer sur un test plus grand avant d’adopter |
La vraie valeur de la note est la discipline d’écrire la décision après les chiffres. Pour les outils que vous utilisez plutôt qu’entraînez, la même idée s’applique avec un petit ensemble de tâches réalistes. Un score faible est utile aussi, puisqu’il vous dit où regarder. Ce qui gaspille des mois, c’est un score élevé que vous ne pouvez pas expliquer.
Téléchargez le code de cette série (zip), avec tous les scripts et les résultats enregistrés.
Sources
- Common pitfalls and recommended practices — scikit-learn
- Cross-validation: evaluating estimator performance — scikit-learn
- A Recipe for Training Neural Networks — Andrej Karpathy, 25 avril 2019
Tiré du Guide de l’intelligence artificielle, par Robert Scales, publié par Holistic Hybrid inc. Partage et adaptation permis selon la licence CC BY-NC-SA 4.0 : citez le guide, mettez un lien vers https://guidetoartificialintelligence.com/fr/apprendre/votre-modele-a-t-il-progresse-tests-honnetes/, pas d’usage commercial sans permission, et gardez la même licence.