Les pièges de la validation croisée

Piège 1: le split

Splitter en 10 folds “au hasard” est rarement une bonne idée, car il faut viser une dépendance la moins grande possible entre les folds si nous voulons pouvoir généraliser sur le test. Or, les exemples d’un corpus ne sont jamais totalement indépendants. Souvent, des efforts conséquents sont réalisés pour garantir que le corpus de test est bien décorrélé du corpus de train. Par exemple, si nous utilisons un corpus extrait de Twitter, alors le corpus de test correspondra à une plage temporelle différente de celle utilisée pour le train. Si nous construisons un corpus de dev par simple tirage aléatoire dans le train, alors train et dev correspondront à la même plage temporelle et l’information risque d’être redondante entre les deux. Cet aspect temporel n’est qu’un facteur de dépendance possible, il en existe une multitude d’autres (auteurs des textes du corpus, thèmes abordés, etc.) et il est impossible de tous les prévoir. Mais lorsque l’on split, il faut néanmoins essayer d’en prévoir le plus possible, et bien réfléchir à la manière de splitter.

Piège 2: le choix du modèle

Quel modèle choisir pour réaliser le test final, sur le corpus de test ?

En pratique, la réponse est loin d’être aussi simple que l’on pourrait l’imaginer au premier abord en lisant la question.

Prenons un exemple classique, où nous disposons d’un corpus de train, pas très gros, et d’un corpus de test. Nous voulons utiliser la validation croisée pour construire un corpus de dev et ainsi tuner les hyper-paramètres. Nous divisions donc le corpus de train en 10 folds, ce qui nous donne 10 modèles que l’on peut tester sur 10 corpus de dev très petits, mais indépendants.

  • Option 1: on estime les meilleurs hyper-paramètres en moyennant les 10 résultats sur le dev, puis on retrain un nouveau modèle global, sur tout le corpus de train, avec ces hyper-paramètres. Le problème est que presque tous les modèles de machine learning ont une part aléatoire très importante, généralement dûe à l’initialisation aléatoire des paramètres, au shuffling du corpus de train ou encore au sampling d’échantillons négatifs comme dans le célèbre Word2Vec, sans parler des apprentissages asynchrones sur plusieurs cartes GPU (cf. Hogwild) qui génèrent également des comportements aléatoires. Bref, l’aléatoire fait partie intégrante des méthodes de deep learning, ce qui fait qu’un même modèle entraîné 2 fois de suite peut avoir des courbes d’apprentissage très différentes, même si au final ces modèles tendent à produire des résultats comparables. C’est d’ailleurs entre autre pour cela que les performances des modèles de deep learning doivent être données en moyenne et que l’écart-type doit aussi être indiqué. Notons en passant que cela implique d’exécuter 10 fois la même expérience, qui lorsque elle-même implique une validation croisée, résulte sur 100 apprentissages à réaliser pour chaque valeur possible des hyper-paramètres à tester, ce qui peut être extrêmement coûteux en temps et en ressources de calcul pour des modèles de deep learning ! Rien que ce fait devrait nous faire douter de nombreux résultats qui sont publiés: est-ce que toutes ces expériences ont bien été réalisées ? Est-ce que les hyper-paramètres ont été proprement tunés sur des dev ? Au vu du surcoût qu’implique la validation croisée, ces doutes sont légitimes, et ne peuvent être levés à mon avis que en distribuant le code et les corpus pour reproduire les expériences.

Mais revenons à notre option 1: à cause du comportement fortement alétoire des modèles, les hyper-paramètres optimaux moyens peuvent très bien ne pas être optimaux du tout sur le dernier modèle appris sur tout le corpus de train ! A quoi bon alors faire autant d’efforts pour estimer ces hyper-paramètres ? De plus, les modèles entraînés sur 9/10ème du corpus de train sont entraînés avec moins de données que le modèle final, et nous savons que les meilleurs hyper-paramètres dépendent entre autre de la taille du corpus: ce ne sont donc de toute manière pas les meilleurs hyper-paramètres pour le dernier modèle qui lui, est entraîné sur tout le corpus !

  • Option 2: pour chacun des 10 folds, on conserve non pas les meilleurs hyper-paramètres mais le meilleur modèle selon le dev correspondant, puis on teste ce meilleur modèle sur le vrai test et on moyenne les performances des 10 meilleurs modèles sur le test. Mais il y a au moins 2 problèmes: Tout d’abord, pour sélectionner un meilleur modèle, on utilise un tout petit dev qui n’est probablement par représentatif du test. Donc le modèle choisi n’est sans doute pas aussi bon sur le test. Ensuite, ce modèle n’est entraîné que sur 9/10ème du corpus de train, il est donc probablement moins performant qu’un modèle entraîné sur tout le corpus, et les quelques points de performances ainsi perdus sont importants dans les benchmarks internationaux, avec des niveaux de performances très difficiles à atteindre en général.

Après tout ceci, un conseil: éviter la validation croisée comme la peste, et préférez de loin un split train/dev/test classique, quite à annoter de nouvelles données à la main !

Voir également