< TOUTES LES NOTES

99 % de précision, et un modèle qui ne détecte rien

4 min de lecture

Un classifieur de malwares peut atteindre un score excellent en validation et s'effondrer en production. Trois biais de corpus expliquent presque tous les cas — et aucun ne se voit dans la matrice de confusion.

machine learningdétection de malwarebiais de corpusévaluation

Un classifieur de fichiers exécutables annonce 99 % de précision sur son jeu de test. Déployé, il laisse passer des échantillons évidents et lève des alertes sur des binaires internes parfaitement légitimes.

Ce n'est presque jamais un problème de modèle. C'est que le score mesurait autre chose que ce qu'on croyait mesurer. Trois biais reviennent, et aucun n'apparaît dans une matrice de confusion — ils sont dans le jeu de données, donc l'évaluation les reproduit fidèlement au lieu de les révéler.

Biais 1 : le corpus encode la provenance, pas la malveillance

C'est le plus insidieux, parce que la corrélation qu'il crée est parfaitement réelle — elle n'est simplement pas celle qu'on veut apprendre.

Comment se constitue un corpus, en pratique ? Les échantillons malveillants viennent de plateformes de partage, de honeypots, de retours clients. Les échantillons sains viennent de systèmes propres, de dépôts d'éditeurs, de suites logicielles connues.

Résultat mécanique : la quasi-totalité du malware est non signée, et une large majorité du bénin est signée par un éditeur reconnu. Le modèle apprend, très efficacement, à lire la signature. Il atteint 99 % parce que la signature est un prédicteur presque parfait dans ce corpus.

En production, la population change. Un binaire compilé localement, l'outil interne d'une PME, un utilitaire d'un petit éditeur, un cache de compilation .NET : tous non signés, tous bénins. Le modèle les traite comme du malware, et le taux de faux positifs mesuré en interne ne ressemble en rien à celui du terrain.

Le diagnostic ne se fait pas sur la métrique globale mais sur une évaluation stratifiée. Découper l'évaluation en 2 × 2 — bénin/malveillant × signé/non signé — et regarder la performance à l'intérieur de chaque case. Si le modèle est excellent globalement mais médiocre parmi les non signés, il n'a pas appris la malveillance : il a appris la provenance.

Le remède n'est pas de retirer la signature des variables. C'est un signal légitime, et l'amputer rend le modèle aveugle à une information réelle. Le remède est de casser la corrélation à la source, en collectant du bénin non signé et du malveillant signé — c'est-à-dire de payer le coût d'un corpus co-distribué.

Biais 2 : le découpage aléatoire mesure la mémoire, pas la généralisation

Un train_test_split aléatoire est le réflexe par défaut. Sur du malware, il est faux.

Le malware arrive par familles et par campagnes : des centaines de variantes d'un même code, repackées, réempaquetées, recompilées. Un découpage aléatoire place des variantes de la même famille des deux côtés de la barrière. Le modèle voit la famille à l'entraînement et la revoit au test.

Le score obtenu répond alors à la question « sais-tu reconnaître une famille que tu as déjà vue ? » — pas à « sais-tu détecter quelque chose de nouveau ? ». Or c'est la seconde qui décide de l'utilité en production.

L'écart n'est pas marginal. Sur un classifieur de familles, passer d'un découpage aléatoire à un découpage disjoint par groupe — toutes les variantes d'une famille du même côté — peut faire chuter un macro-F1 de l'ordre de 0,7 à quelque chose autour de 0,2. Le modèle n'a pas changé. Seule la question posée a changé.

Ce n'est pas une raison de jeter le modèle : un reconnaisseur de familles connues a une vraie valeur opérationnelle. C'est une raison de ne pas l'appeler détecteur de familles inconnues.

Biais 3 : le temps

Le troisième est le mieux documenté et le plus souvent ignoré. Entraîner sur un mélange chronologique et tester sur le même mélange revient à laisser le modèle apprendre le futur.

Les techniques d'évasion évoluent, les empaqueteurs changent, les campagnes se succèdent. Une évaluation honnête impose une frontière temporelle : entraîner sur tout ce qui précède une date, tester sur ce qui suit. Le score baisse. Il devient comparable à ce que le déploiement produira.

La question à poser à toute métrique

Ces trois biais partagent une forme. Le modèle apprend une variable corrélée à l'étiquette dans le corpus, mais qui n'est pas le phénomène — et l'évaluation, tirée du même corpus, valide la corrélation au lieu de la contester.

D'où une seule question, à poser systématiquement devant un bon score :

Quelle propriété de mon jeu de données rendrait ce résultat faux en production ?

Si la réponse ne vient pas, ce n'est pas que la réponse n'existe pas. C'est qu'on ne l'a pas cherchée.

Concrètement, cela se traduit par trois exigences avant de croire un chiffre :

  1. un découpage disjoint par groupe (famille, campagne, éditeur) ;
  2. une frontière temporelle entre entraînement et test ;
  3. une évaluation stratifiée sur les axes suspects — signature, empaqueteur,

taille, format — et non une moyenne unique.

Une métrique unique sur un découpage aléatoire n'est pas une mesure. C'est une impression.


Voir aussi, sur la même idée appliquée aux tests plutôt qu'aux modèles : le bug qui rendait vide chaque manifeste Android — un test qui passait parce qu'il se comparait à lui-même.

OP_CENTER
STATUT : CHIFFRÉ
SYS_BOOT.exeDOSSIER.exeREGISTRY.exeNOTES.logSERVICES.exeFORMATIONS.dirCV_DATA.dirSECURE_CONNECT