Répartir en base d'apprentissage et de test

C'est un problème plutôt facile puisqu'il s'agit de répartir aléatoirement les lignes d'une base de données d'un côté ou de l'autre. Lorsque le problème de machine learning à résoudre est un problème de classification, il faut s'assurer que chaque côté contient une proportion raisonnable de chaque classe.

Répartition naïve

On considère une base de données qu'on divise en 2/3 apprentissage, 1/3 test. On note cette proportion $t$. Deux classes 0 et 1, la proportion de la classe 1 est de $p$ qu'on choisit petit.

Et on divise en base d'apprentissage et de test.

On recommence un grand nombre de fois et on représente la proportion obtenue dans la base de test.

On considère maintenant la moyenne, les valeurs extrêmes de la proportion en faisant varier $p$.

Et train_test_split...

L'écart entre les extrêmes paraît plus petit. Le générateur pseudo aléatoire utilisé par scikit-learn paraît de meilleur qualité. Nous y reviendront peut-être un jour.

Répartition stratifiée

Nous utilisons maintenant le paramètre stratify qui permet de s'assurer que les deux classes sont équitablement réparties entre les deux ensembles train et test.

La proportion initiale est bien respectée. Comment faire cela en pratique ? Le plus simple est sans doute de :

La méthode est simple mais plus coûteuse puisqu'elle nécessite un tri.

Le coût de la fonction train_test_split semble être entre $O(n)$ et $O(n \ln n)$. Regardons.

C'est difficile à voir sur ce schéma. Il faudrait tirer plus d'exemple, regader les quantiles plutôt que la seule médiane. Le code de scikit-learn est plus explicite, une permutation précède la répartition en train / test.

Streaming splitting

Streaming veut dire qu'on traite les données sous la forme d'un flux et qu'on ne sait pas combien il y en. Concrètement, il faut commencer la répartition train / test au moment sans savoir quand elle s'arrêtera. Par conséquent, il faut qu'à tout instant, on soit capable d'interrompre la répartition et celle-ci doit être valide.

Le premier algorithme qui consiste à tirer un nombre aléatoire et à le répartir en train ou test selon le nombre tiré. Chaque observation est traitée indépendamment. A tout moment, la répartition peut être interrompue. En pratique, on implémente ce type de processus sous la forme d'itérateur ou de mapper. C'est une fonction qui accepte un itérateur sur un ensemble et qui retourne un itérateur sur les valeurs transformées. Dans notre cas, on retourne l'observation, suivi de 0 si elle est classée en train et 1 en test.

La répartition stratifiée repose sur une permutation aléatoire et cela implique d'avoir accès à l'intégralité des données au préalable. En streaming, ce n'est pas possible. Il faut donc penser à autre chose pour obtenir une version stratifiée de la version streaming. Rien n'empêche en version streaming de garder les dernières observations en mémoire pour faire une mini-permutation. Nous allons introduire quelques changements :

  1. Le stream est maintenant un flux sur deux valeurs, l'observation et la classe à laquelle il appartient.
  2. La fonction va conserver les dernières valeurs pour chaque classe.
  3. La fonction va produire des observations de temps en temps quand elle sera sûre que les observations seront stratifiées.
  4. Nuos allons compter les observations distribuées dans chaque base.

Il y a juste un petit problème avec cette implémentation. On multiplie la taille du buffer par un réel. Je suggère d'enlever le nombre 0.5 dans le code pour voir ce qu'il se passe. La somme des arrondis est loin d'être un arrondi même si $N$ choisi tel que $N = \max(\frac{1}{p}, \frac{1}{1-p})$.

Il faut corriger ces erreurs d'arrondi. On s'inspire de l'algorithme de Bresenham et mémoriser les éléments répartis.

Pas trop mal. Le dernier inconvénient est la taille de la fenêtre. Dans l'exemple qui suit, elle sera de 3. L'algorithme va donc grouper les éléments par trois, les permuter aléatoirement et les laisser filer. Nous ne pourrons jamais avoir trois éléments de suite du même côté train ou test. On peut bidouiller comme suit (ligne marquées # changement). La fonction qui suit ne permet toujours pas d'avoir de grandes séquences répartie du même côté mais ce sera l'inconvénient de ce type d'algorithme. La taille du buffer limite cette possibilité.

Streaming distribué

C'est possible mais c'est un peu plus compliqué parce que le hasard en distribué, c'est compliqué. On n'est jamais sûr que des séries pseudo-aléatoires soient tout-à-fait indépendantes lorsqu'elles sont générées en parallèles.