Implementazione della validazione incrociata nidificata


10

Sto cercando di capire se la mia comprensione della convalida incrociata nidificata è corretta, quindi ho scritto questo esempio di giocattolo per vedere se ho ragione:

import operator
import numpy as np
from sklearn import cross_validation
from sklearn import ensemble
from sklearn.datasets import load_boston

# set random state
state = 1

# load boston dataset
boston = load_boston()

X = boston.data
y = boston.target

outer_scores = []

# outer cross-validation
outer = cross_validation.KFold(len(y), n_folds=3, shuffle=True, random_state=state)
for fold, (train_index_outer, test_index_outer) in enumerate(outer):
    X_train_outer, X_test_outer = X[train_index_outer], X[test_index_outer]
    y_train_outer, y_test_outer = y[train_index_outer], y[test_index_outer]

    inner_mean_scores = []

    # define explored parameter space.
    # procedure below should be equal to GridSearchCV
    tuned_parameter = [1000, 1100, 1200]
    for param in tuned_parameter:

        inner_scores = []

        # inner cross-validation
        inner = cross_validation.KFold(len(X_train_outer), n_folds=3, shuffle=True, random_state=state)
        for train_index_inner, test_index_inner in inner:
            # split the training data of outer CV
            X_train_inner, X_test_inner = X_train_outer[train_index_inner], X_train_outer[test_index_inner]
            y_train_inner, y_test_inner = y_train_outer[train_index_inner], y_train_outer[test_index_inner]

            # fit extremely randomized trees regressor to training data of inner CV
            clf = ensemble.ExtraTreesRegressor(param, n_jobs=-1, random_state=1)
            clf.fit(X_train_inner, y_train_inner)
            inner_scores.append(clf.score(X_test_inner, y_test_inner))

        # calculate mean score for inner folds
        inner_mean_scores.append(np.mean(inner_scores))

    # get maximum score index
    index, value = max(enumerate(inner_mean_scores), key=operator.itemgetter(1))

    print 'Best parameter of %i fold: %i' % (fold + 1, tuned_parameter[index])

    # fit the selected model to the training set of outer CV
    # for prediction error estimation
    clf2 = ensemble.ExtraTreesRegressor(tuned_parameter[index], n_jobs=-1, random_state=1)
    clf2.fit(X_train_outer, y_train_outer)
    outer_scores.append(clf2.score(X_test_outer, y_test_outer))

# show the prediction error estimate produced by nested CV
print 'Unbiased prediction error: %.4f' % (np.mean(outer_scores))

# finally, fit the selected model to the whole dataset
clf3 = ensemble.ExtraTreesRegressor(tuned_parameter[index], n_jobs=-1, random_state=1)
clf3.fit(X, y)

Ogni pensiero è apprezzato.


3
Puoi anche fornire una versione della tua comprensione della convalida incrociata nel testo per coloro che non leggono Python?
gung - Ripristina Monica

Risposte:


14

UPS, il codice è sbagliato, ma in modo molto sottile !

a) la divisione del set di treni in un set di allenamento interno e set di test è OK.

b) il problema sono le ultime due righe, che riflettono il sottile malinteso sullo scopo di una validazione incrociata nidificata. Lo scopo di un CV nidificato non è selezionare i parametri, ma avere una valutazione imparziale di quale sia l'accuratezza attesa del tuo algoritmo, in questo caso ensemble.ExtraTreesRegressorin questi dati con il miglior iperparametro qualunque essi siano .

E questo è ciò che il tuo codice calcola correttamente fino alla linea:

    print 'Unbiased prediction error: %.4f' % (np.mean(outer_scores))

Ha usato il CV nidificato per calcolare una previsione imparziale del classificatore. Ma nota che ogni passaggio del ciclo esterno può generare un diverso migliore iperparametro, come sapevi quando hai scritto la riga:

   print 'Best parameter of %i fold: %i' % (fold + 1, tuned_parameter[index])

Quindi ora è necessario un loop CV standard per selezionare il miglior iperparametro finale, usando le pieghe:

tuned_parameter = [1000, 1100, 1200]
for param in tuned_parameter:

    scores = []

    # normal cross-validation
    kfolds = cross_validation.KFold(len(y), n_folds=3, shuffle=True, random_state=state)
    for train_index, test_index in kfolds:
        # split the training data
        X_train, X_test = X[train_index], X[test_index]
        y_train, y_test = y[train_index], y[test_index]

        # fit extremely randomized trees regressor to training data
        clf2_5 = ensemble.ExtraTreesRegressor(param, n_jobs=-1, random_state=1)
        clf2_5.fit(X_train, y_train)
        scores.append(clf2_5.score(X_test, y_test))

    # calculate mean score for folds
    mean_scores.append(np.mean(scores))

# get maximum score index
index, value = max(enumerate(mean_scores), key=operator.itemgetter(1))

print 'Best parameter : %i' % (tuned_parameter[index])

quale è il tuo codice ma con riferimenti a inner rimosso.

Ora il parametro migliore è tuned_parameter[index]e ora puoi imparare il classificatore finale clf3come nel tuo codice.


Grazie! Ho considerato che potevo selezionare bestparametri diversi in diverse pieghe, ma non sapevo come scegliere i migliori. stats.stackexchange.com/questions/65128/… - qui, nella risposta si dice che in realtà è indesiderabile selezionare il modello migliore tra i modelli k esterni. Forse sto ancora fraintendendo qualcosa, ma ho pensato che l'idea del loop CV interno fosse quella di selezionare il modello più performante e il loop CV esterno è di stimare le prestazioni. Potresti fornire il codice modificato completo?
abudis,

Okay, penso di averlo capito. Vorrei vedere il codice completamente modificato, per essere sicuro. Grazie.
abudis,

1
Sono confuso riguardo alla risposta di Jacques Wainer e penso che valga la pena chiarirlo. Quindi, Wainer suggerisce che un ciclo CV standard dovrebbe seguire il codice fornito dalla domanda iniziale o che dovrebbe semplicemente sostituire il codice iniziale "interno"? grazie

Il loop CV standard segue il loop CV nidificato
Jacques Wainer,

2
La prima parte consiste nel calcolare una previsione obiettiva dell'errore. Se si stanno testando molti algoritmi diversi, è necessario eseguire solo la prima parte, quindi selezionare l'algoritmo con l'errore più basso e, solo per quello, eseguire la 2 parte per selezionare gli iperparametri. Se hai impostato un solo algoritmo, la prima parte è meno importante, a meno che tu non voglia stateto al tuo capo o cliente che la tua migliore previsione del futuro errore del classificatore sia x, e devi calcolare la x usando la 1a CV nidificato.
Jacques Wainer,

0

Per riassumere la risposta di Jacques,

Il CV nidificato è necessario per la stima dell'errore imparziale di un modello. In questo modo possiamo confrontare il punteggio di diversi modelli. Utilizzando queste informazioni, possiamo quindi eseguire un loop CV piega a K separato per la messa a punto dei parametri dei modelli selezionati.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.