Wdrożenie zagnieżdżonej weryfikacji krzyżowej

10

Próbuję dowiedzieć się, czy moje rozumienie zagnieżdżonej weryfikacji krzyżowej jest prawidłowe, dlatego napisałem ten zabawkowy przykład, aby sprawdzić, czy mam rację:

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)

Doceniamy wszelkie myśli.

abudis
źródło
3
Czy możesz również przedstawić wersję swojego rozumienia weryfikacji krzyżowej w tekście dla tych, którzy nie czytają Pythona?
gung - Przywróć Monikę

Odpowiedzi:

14

UPS, kod jest błędny, ale w bardzo subtelny sposób!

a) podział zestawu pociągów na wewnętrzny zestaw treningowy i zestaw testowy jest OK.

b) problemem są dwie ostatnie linie, które odzwierciedlają subtelne nieporozumienie dotyczące celu zagnieżdżonej weryfikacji krzyżowej. Zagnieżdżone CV nie ma na celu wybrania parametrów, ale obiektywnej oceny oczekiwanej dokładności algorytmu, w tym przypadku ensemble.ExtraTreesRegressorw tych danych z najlepszym hiperparametrem, niezależnie od tego, jakie mogą być .

I to jest twój kod poprawnie oblicza do wiersza:

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

Użył zagnieżdżonego CV do obliczenia obiektywnej prognozy klasyfikatora. Zauważ jednak, że każde przejście zewnętrznej pętli może generować inny najlepszy hiperparametr, o czym wiedziałeś podczas pisania linii:

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

Więc teraz potrzebujesz standardowej pętli CV, aby wybrać ostateczny najlepszy hiperparametr, używając foldów:

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])

który jest twoim kodem, ale usunięto odniesienia do wewnętrznej .

Teraz najlepszym parametrem jest tuned_parameter[index]i teraz możesz nauczyć się ostatecznego klasyfikatora clf3jak w kodzie.

Jacques Wainer
źródło
Dzięki! Rozważyłem, że mogę wybrać różne bestparametry w różnych fałdach, ale nie wiedziałem, jak wybrać najlepsze. stats.stackexchange.com/questions/65128/… - tutaj, w odpowiedzi wspomniano, że tak naprawdę niepożądane jest wybranie najlepszego modelu spośród zewnętrznych modeli k. Być może nadal coś nie rozumiem, ale pomyślałem, że idea wewnętrznej pętli CV polega na wybraniu modelu o najlepszych wynikach, a zewnętrzna pętla CV polega na oszacowaniu wydajności. Czy możesz podać pełny zmodyfikowany kod?
abudis
Okej, chyba mam to. Chciałbym jednak zobaczyć w pełni zmodyfikowany kod, dla pewności. Dzięki.
abudis
1
Jestem zdezorientowany co do odpowiedzi Jacquesa Wainera i myślę, że warto to wyjaśnić. Czy zatem Wainer sugeruje, że standardowa pętla CV powinna podążać za kodem podanym w pytaniu początkowym, czy też powinna zastąpić początkowy „wewnętrzny” kod części? thanx
Standardowa pętla CV podąża za zagnieżdżoną pętlą CV
Jacques Wainer
2
Pierwsza część polega na obliczeniu obiektywnej prognozy błędu. Jeśli testujesz wiele różnych algorytmów, powinieneś wykonać tylko pierwszą część, a następnie wybrać algorytm o najniższym błędzie, i tylko dla tego, wykonaj 2 część, aby wybrać hiperparametry. Jeśli używasz tylko jednego algorytmu, to pierwsza część jest mniej ważna, chyba że chcesz stwierdzić swojemu szefowi lub klientowi, że najlepsza prognoza przyszłego błędu klasyfikatora wynosi x, i musisz obliczyć x używając pierwszego zagnieżdżone CV.
Jacques Wainer,
0

Podsumowując odpowiedź Jacquesa:

Zagnieżdżone CV jest wymagane do obiektywnego oszacowania błędu modelu. W ten sposób możemy porównać wyniki różnych modeli. Korzystając z tych informacji, możemy następnie wykonać osobną pętlę CV z K-krotnie do strojenia parametrów wybranych modeli.

Sharan Naribole
źródło