web-dev-qa-db-fra.com

validation croisée stratifiée à pli k avec des classes déséquilibrées

J'ai des données avec 4 classes et j'essaye de construire un classificateur. J'ai ~ 1000 vecteurs pour une classe, ~ 10 ^ 4 pour une autre, ~ 10 ^ 5 pour la troisième et ~ 10 ^ 6 pour la quatrième. J'espérais utiliser la validation croisée, j'ai donc regardé la documentation de scikit-learn

Mon premier essai a été d’utiliser StratifiedShuffleSplit mais cela donne le même pourcentage pour chaque classe, les classes étant toujours très déséquilibrées.

Existe-t-il un moyen d'effectuer une validation croisée, mais avec les classes équilibrées la formation et test set?


En remarque, je ne pouvais pas faire la différence entre StratifiedShuffleSplit et StratifiedKFold . Les descriptions me ressemblent beaucoup.

14
eleanora

Mon premier essai a été d’utiliser StratifiedShuffleSplit, mais cela donne le même pourcentage pour chaque classe, laissant les classes radicalement déséquilibrées.

J'ai l'impression que vous confondez ce qu'une stratégie stratifiée fera, mais vous devrez montrer votre code et vos résultats pour dire avec certitude ce qui se passe (le même pourcentage que leur pourcentage dans l'ensemble d'origine, ou le même pourcentage dans le train/groupe d’essais renvoyé (le premier est la façon dont il est censé être). 

En remarque, je ne pouvais pas faire la différence entre StratifiedShuffleSplit et StratifiedKFold. Les descriptions me ressemblent beaucoup.

L'un d'entre eux devrait certainement fonctionner. La description de la première est certes un peu déroutante, mais voici ce qu’ils font.

StratifiedShuffleSplit

Fournit des index de formation/test pour fractionner les données dans des ensembles de test de train.

Cela signifie que vos données sont divisées en un train et un ensemble de test. La partie stratifiée signifie que pourcentages seront maintenus dans cette division. Donc, si 10% de vos données est en classe 1 et que 90% est en classe 2, cela garantira que 10% de votre rame sera en classe 1 et que 90% sera en classe 2. Même chose pour l'ensemble de test.

Votre message donne l’impression que vous voudriez 50% de chaque classe du test. Ce n'est pas ce que la stratification fait, la stratification maintient les pourcentages d'origine. Vous devriez les conserver, car sinon vous vous donneriez une idée non pertinente de la performance de votre classificateur: peu importe l’importance de la classification d'un fractionnement 50/50, alors qu'en pratique, vous verrez des scissions 10/90?

StratifiéKFold

Cet objet de validation croisée est une variante de KFold qui renvoie des plis stratifiés. Les plis sont réalisés en préservant le pourcentage d'échantillons pour chaque classe.

Voir Validation croisée du pli en k . Sans stratification, il divise simplement vos données en k. Ensuite, chaque fold 1 <= i <= k est utilisé une fois en tant qu'ensemble de test, tandis que les autres sont utilisés pour la formation. Les résultats sont moyennés à la fin. Cela ressemble à l'exécution de ShuffleSplitk fois.

La stratification garantira que les pourcentages de chaque classe dans l'ensemble de vos données seront les mêmes (ou très proches de) dans chaque pli.


Il existe de nombreux ouvrages traitant de classes déséquilibrées. Certaines méthodes simples à utiliser impliquent l'utilisation de pondérations de classe et l'analyse de la courbe ROC. Je suggère les ressources suivantes comme points de départ:

  1. Exemple d'utilisation de poids de classe dans Scikit-learn .
  2. Une question de quora sur la mise en œuvre de réseaux de neurones pour des données déséquilibrées .
  3. Cette question stats.stackexchange avec des réponses plus en profondeur .
20
IVlad

CV K-Fold

K-Fold CV fonctionne en partitionnant de manière aléatoire vos données en k (assez) partitions égales. Si vos données étaient bien équilibrées entre les classes telles que [0,1,0,1,0,1,0,1,0,1], un échantillonnage aléatoire avec (ou sans remplacement) vous donnera une taille d'échantillon approximative de 0 et 1

Cependant, si vos données ressemblent davantage à [0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,1,0,0] Où une classe sur représente les données, le pli à pli k sans échantillonnage pondéré vous donnerait des résultats erronés.

Si vous utilisez un CV plié en k ordinaire sans ajuster les poids d'échantillonnage à partir d'un échantillonnage uniforme, vous obtiendrez quelque chose comme:

## k-fold CV
k = 5
splits = np.array_split(y, k)
for i in range(k):
    print(np.mean(splits[i]))

 [array([0, 0, 0, 0, 0, 0, 0]),
 array([0, 0, 0, 0, 0, 0, 0]),
 array([0, 0, 0, 0, 0, 0]),
 array([0, 0, 0, 0, 0, 0]),
 array([0, 1, 1, 1, 1, 1])]

où il y a clairement des divisions sans représentation utile des deux classes.

Le point k-fold CV est de former/tester un modèle pour tous les sous-ensembles de données, tout en laissant un sous-ensemble à chaque essai et d’entraîner sur les sous-ensembles k-1.

Dans ce scénario, vous souhaiterez utiliser la division par strates. Dans le jeu de données ci-dessus, il y a 27 0s et 5 1s. Si vous souhaitez calculer k = 5 CV, il ne serait pas raisonnable de scinder les strates de 1 en 5 sous-ensembles. Une meilleure solution consiste à le scinder en k <5 sous-ensembles, tels que 2. Les strates de 0s peuvent rester avec k = 5 divisions car elles sont beaucoup plus grandes. Ensuite, pendant votre formation, vous aurez un simple produit de 2 x 5 à partir du jeu de données. Voici du code pour illustrer

from itertools import product

for strata, iterable in groupby(y):
    data = np.array(list(iterable))
    if strata == 0:
        zeros = np.array_split(data, 5)
    else:
        ones = np.array_split(data, 2)


cv_splits = list(product(zeros, ones))
print(cv_splits)

m = len(cv_splits)
for i in range(2):
    for j in range(5):
        data = np.concatenate((ones[-i+1], zeros[-j+1]))
        print("Leave out ONES split {}, and Leave out ZEROS split {}".format(i,j))
        print("train on: ", data)
        print("test on: ", np.concatenate((ones[i], zeros[j])))



Leave out ONES split 0, and Leave out ZEROS split 0
train on:  [1 1 0 0 0 0 0 0]
test on:  [1 1 1 0 0 0 0 0 0]
Leave out ONES split 0, and Leave out ZEROS split 1
train on:  [1 1 0 0 0 0 0 0]
...
Leave out ONES split 1, and Leave out ZEROS split 4
train on:  [1 1 1 0 0 0 0 0]
test on:  [1 1 0 0 0 0 0]

Cette méthode permet de fractionner les données en partitions où toutes les partitions sont éventuellement laissées à l’essai. Il convient de noter que toutes les méthodes d’apprentissage statistique ne permettent pas la pondération. Il est donc essentiel d’ajuster des méthodes telles que le CV pour tenir compte des proportions de l’échantillonnage.

  • Référence: James, G., Witten, D., Hastie, T. et Tibshirani, R. (2013). Une introduction à l’apprentissage statistique: avec des applications en R.
2
Jon