Gruppo K-fold con stratificazione target
Ho 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]
})
Voglio dividere df in train e testare per gruppo più volte (K-Fold), quindi train and test contiene esempi di sottoinsiemi di gruppi che si escludono a vicenda . Ad esempio, se test contiene esempi di gruppi [10, 20], test dovrebbe contenere esempi solo di gruppo [30]. Voglio anche mantenere la stratificazione per target. Quindi la distribuzione di destinazione viene considerata anche per la creazione di sottoinsiemi di gruppi. Come è possibile? Non ho trovato nulla in scikit-learn che possa aiutare.
In pratica ho un df con circa 500k righe, 2,5k persone diverse, 30 gruppi e la seguente distribuzione target
Risposte
Per utilizzare sia i gruppi che la stratificazione, sembra che dovresti scrivere il tuo codice. Nota che inevitabilmente perderai campioni durante l'addestramento e / o nel set di test (a meno che tu non sia fortunato).
Un modo per ottenerlo è:
- fai la tua divisione per gruppi (potresti usare il metodo GroupKFold da
sklearn) - controllare la distribuzione degli obiettivi nei set di addestramento / test.
- rimuovere casualmente gli obiettivi in allenamento o test impostati per bilanciare le distribuzioni.
Nota : è possibile che un gruppo scompaia utilizzando tale algoritmo. Potresti preferire di non rimuovere casualmente gli obiettivi durante il bilanciamento dei set di addestramento / test.
Ecco un codice di esempio
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")
MODIFICARE
L'uso della convalida incrociata stratificata sembra non essere obbligatorio (vedere i collegamenti di seguito), quindi potresti riconsiderare l'utilizzo.
Potresti trovare utili questi link:
- Perché utilizzare la convalida incrociata stratificata? Perché questo non danneggia il vantaggio relativo alla varianza?
- Comprensione della convalida incrociata stratificata
- Uno studio di convalida incrociata e bootstrap per la stima dell'accuratezza e la selezione del modello
Per quanto riguarda l'eccesso / sottocampionamento, penso che sia difficile rispondere senza maggiori dettagli sulla distribuzione dei dati e su quanto siano sbilanciate le tue classi.