Implementierung einer verschachtelten Kreuzvalidierung

10

Ich versuche herauszufinden, ob mein Verständnis der verschachtelten Kreuzvalidierung korrekt ist. Deshalb habe ich dieses Spielzeugbeispiel geschrieben, um zu sehen, ob ich Recht habe:

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)

Alle Gedanken geschätzt.

abudis
quelle
3
Können Sie auch eine Version Ihres Verständnisses der Kreuzvalidierung im Text für diejenigen bereitstellen, die Python nicht lesen?
Gung - Reinstate Monica

Antworten:

14

UPS, der Code ist falsch, aber auf sehr subtile Weise!

a) Die Aufteilung des Zugsatzes in einen inneren Trainingssatz und einen Testsatz ist in Ordnung.

b) Das Problem sind die letzten beiden Zeilen, die das subtile Missverständnis über den Zweck einer verschachtelten Kreuzvalidierung widerspiegeln . Der Zweck eines verschachtelten Lebenslaufs besteht nicht darin, die Parameter auszuwählen, sondern eine unvoreingenommene Bewertung der erwarteten Genauigkeit Ihres Algorithmus vorzunehmen, in diesem Fall ensemble.ExtraTreesRegressorin diesen Daten mit dem besten Hyperparameter, was auch immer sie sein mögen .

Und das ist es, was Ihr Code bis zur Zeile richtig berechnet:

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

Es verwendete den verschachtelten Lebenslauf, um eine unvoreingenommene Vorhersage des Klassifikators zu berechnen. Beachten Sie jedoch, dass jeder Durchgang der äußeren Schleife einen anderen besten Hyperparameter erzeugen kann, wie Sie beim Schreiben der Zeile wussten:

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

Jetzt benötigen Sie eine Standard-CV-Schleife, um den endgültigen besten Hyperparameter mithilfe von Falten auszuwählen :

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

Das ist dein Code, aber mit Verweisen auf innere entfernt.

Jetzt ist der beste Parameter tuned_parameter[index], und jetzt können Sie den endgültigen Klassifikator clf3wie in Ihrem Code lernen .

Jacques Wainer
quelle
Vielen Dank! Ich habe überlegt, ob ich verschiedene bestParameter in verschiedenen Falten auswählen kann, aber ich wusste nicht, wie ich die besten auswählen soll. stats.stackexchange.com/questions/65128/… - hier wird in der Antwort erwähnt, dass es tatsächlich unerwünscht ist, das beste Modell aus den äußeren k Modellen auszuwählen. Vielleicht verstehe ich immer noch etwas falsch, aber ich dachte, dass die Idee der inneren CV-Schleife darin besteht, das Modell mit der besten Leistung auszuwählen, und die äußere CV-Schleife darin besteht, die Leistung abzuschätzen. Könnten Sie bitte den vollständig geänderten Code angeben?
Abudis
Okay, ich glaube ich habe das verstanden. Ich möchte jedoch den vollständigen modifizierten Code sehen, nur um sicherzugehen. Vielen Dank.
Abudis
1
Ich bin verwirrt über die Antwort von Jacques Wainer und ich denke, es lohnt sich, sie zu klären. Schlägt Wainer also vor, dass eine Standard-CV-Schleife dem Code der ersten Frage folgt oder nur den ursprünglichen "inneren" Teilecode ersetzt? Danke
Die Standard-CV-Schleife folgt der verschachtelten CV-Schleife
Jacques Wainer
2
Der erste Teil besteht darin, eine unvoreingenommene Vorhersage des Fehlers zu berechnen. Wenn Sie viele verschiedene Algorithmen testen, sollten Sie nur den ersten Teil ausführen, dann den Algorithmus mit dem niedrigsten Fehler auswählen und nur für diesen den zweiten Teil ausführen, um die Hyperparameter auszuwählen. Wenn Sie nur einen Algorithmus verwenden möchten, ist der erste Teil weniger wichtig, es sei denn, Sie möchten Ihrem Chef oder Kunden mitteilen, dass Ihre beste Vorhersage für den zukünftigen Fehler des Klassifikators x ist, und Sie müssen das x mit dem ersten berechnen verschachtelter Lebenslauf.
Jacques Wainer
0

Um Jacques 'Antwort zusammenzufassen:

Für die unverzerrte Fehlerschätzung eines Modells ist ein verschachtelter Lebenslauf erforderlich. Auf diese Weise können wir die Punktzahl verschiedener Modelle vergleichen. Mit diesen Informationen können wir dann eine separate K-fache CV-Schleife zur Parametereinstellung der ausgewählten Modelle durchführen.

Sharan Naribole
quelle