Groupe K-fold avec stratification cible
J'ai un pd.DataFrame
pd.DataFrame({
'person': ['a', 'b', 'c', 'aa', 'bb', 'cc', 'aaa', 'bbb', 'ccc'],
'group': [10, 10, 10, 20, 20, 20, 30, 30, 30],
'target': [1, 2, 2, 3, 2, 3, 1, 2, 3]
})
Je veux diviser df en train et tester plusieurs fois par groupe (K-Fold), donc train et test contient des exemples de sous-ensembles de groupes mutuellement exclusifs . Par exemple, si le test contient des exemples des groupes [10, 20], le test ne doit contenir que des exemples du groupe [30]. Je souhaite également conserver la stratification par cible. La distribution cible est donc également prise en compte pour la création de sous-ensembles de groupes. Comment est-ce possible? Je n'ai rien trouvé dans scikit-learn qui puisse aider.
En pratique, j'ai un df avec environ 500000 lignes, 2,5k personnes différentes, 30 groupes et une distribution cible suivante
Réponses
Pour utiliser à la fois les groupes et la stratification, il semble que vous deviez écrire votre propre code. Notez que vous perdrez inévitablement des échantillons lors de la formation et / ou du test (sauf si vous avez de la chance).
Une façon d'y parvenir est de:
- faites votre division par groupes (vous pouvez utiliser la méthode GroupKFold à partir de
sklearn) - vérifier la répartition des cibles dans les ensembles de formation / tests.
- supprimer aléatoirement des cibles dans un ensemble d'entraînement ou de test pour équilibrer les distributions
Remarque : il est possible qu'un groupe disparaisse en utilisant un tel algorithme. Vous préférerez peut-être ne pas supprimer les cibles au hasard lors de l'équilibrage des ensembles d'entraînement / de test.
Voici un exemple de code
import pandas as pd
import numpy as np
from sklearn.model_selection import GroupKFold
df = pd.DataFrame({
'person': ['a', 'b', 'c', 'aa', 'bb', 'cc', 'aaa', 'bbb', 'ccc'],
'group': [10, 10, 20, 20, 20, 20, 20, 30, 30],
'target': [1, 2, 2, 3, 2, 3, 1, 2, 3]
})
X = df['person']
y = df['target']
groups = df['group'].values
group_kfold = GroupKFold(n_splits=3)
group_kfold.get_n_splits(X, y, groups)
# First split by groups
for train_index, test_index in group_kfold.split(X, y, groups):
print("Groups split: TRAIN:", train_index, "TEST:", test_index)
y_train_grouped, y_test_grouped = y[train_index], y[test_index]
final_train_index = []
final_test_index = []
# Then balance the distributions for each target
for target in df['target'].unique():
target_train_index = y_train_grouped[y_train_grouped == target].index.tolist()
target_test_index = y_test_grouped[y_test_grouped == target].index.tolist()
n_training = len(target_train_index)
n_testing = len(target_test_index)
print("Target:" + str(target) + " - n_training:" + str(n_training) + " - n_testing:" + str(n_testing) +
" | target_train_index:" + str(target_train_index) + " - target_test_index:" + str(target_test_index))
# Shuffle to remove randomly
np.random.shuffle(target_train_index)
np.random.shuffle(target_test_index)
# Check if we need to remove samples from training or testing set
if n_training > n_testing:
while n_training > n_testing:
target_train_index.pop(0)
n_training = len(target_train_index)
if n_training < n_testing:
while n_training < n_testing:
target_test_index.pop(0)
n_testing = len(target_test_index)
# Append new indexes to global train/test indexes
final_train_index.append(target_train_index)
final_test_index.append(target_test_index)
# Flatten for readability
final_train_index = [item for sublist in final_train_index for item in sublist]
final_test_index = [item for sublist in final_test_index for item in sublist]
print("FINAL split: TRAIN:", final_train_index," TEST:", final_test_index, "\n")
ÉDITER
L'utilisation de la validation croisée stratifiée ne semble pas être obligatoire (voir les liens ci-dessous), vous pouvez donc reconsidérer son utilisation.
Vous pourriez trouver ces liens utiles:
- Pourquoi utiliser la validation croisée stratifiée? Pourquoi cela n'affecte-t-il pas l'avantage lié à la variance?
- Comprendre la validation croisée stratifiée
- Une étude de la validation croisée et du bootstrap pour l'estimation de l'exactitude et la sélection du modèle
En ce qui concerne le sur / sous-échantillonnage, je pense qu'il est difficile de répondre sans plus de détails sur la distribution des données et le déséquilibre de vos classes.