Реализация вложенной перекрестной проверки

10

Я пытаюсь выяснить, правильно ли мое понимание вложенной перекрестной проверки, поэтому я написал этот игрушечный пример, чтобы проверить, прав ли я:

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)

Любые мысли приветствуются.

abudis
источник
3
Можете ли вы предоставить версию вашего понимания перекрестной проверки в тексте для тех, кто не читает Python?
gung - Восстановить Монику
scikit-learnСобственная версия: scikit-learn.org/stable/auto_examples/model_selection/...
ayorgo

Ответы:

14

ИБП, код неправильный, но очень тонким способом!

а) разделение поезда на внутренний тренировочный набор и тестовый набор в порядке.

б) проблема заключается в двух последних строках, которые отражают тонкое недопонимание относительно цели вложенной перекрестной проверки. Целью вложенного резюме является не выбор параметров, а объективная оценка ожидаемой точности вашего алгоритма, в данном случае ensemble.ExtraTreesRegressorдля этих данных с наилучшим гиперпараметром, каким бы они ни были .

И это то, что ваш код правильно вычисляет до строки:

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

Он использовал вложенное резюме для вычисления непредвзятого прогноза классификатора. Но обратите внимание, что каждый проход внешнего цикла может генерировать другой лучший гиперпараметр, как вы знали, когда писали строку:

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

Так что теперь вам нужен стандартный цикл CV, чтобы выбрать последний лучший гиперпараметр, используя складки:

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

это ваш код, но со ссылками на внутреннее удалено.

Теперь лучший параметр tuned_parameter[index], и теперь вы можете узнать окончательный классификатор, clf3как в вашем коде.

Жак Вайнер
источник
Спасибо! Я считал, что могу выбирать разные bestпараметры в разных сгибах, но не знал, как выбрать лучшие. stats.stackexchange.com/questions/65128/… - здесь, в ответе упоминается, что на самом деле нежелательно выбирать лучшую модель из внешних k моделей. Может быть, я все еще что-то неправильно понимаю, но я подумал, что идея внутреннего цикла CV состоит в том, чтобы выбрать наиболее эффективную модель, а внешний цикл CV - оценить производительность. Не могли бы вы предоставить полный модифицированный код?
abudis
Хорошо, я думаю, что получил это. Я хотел бы увидеть полный модифицированный код, хотя, просто чтобы быть уверенным. Спасибо.
abudis
1
Я запутался в ответе Жака Вайнера и думаю, что его стоит уточнить. Итак, предполагает ли Вайнер, что стандартный цикл CV должен следовать коду, указанному в исходном вопросе, или что он должен просто заменить исходный «внутренний» код детали? спасибо
Стандартный цикл CV следует за вложенным циклом CV
Жак Вайнер,
2
Первая часть состоит в том, чтобы вычислить непредвзятый прогноз ошибки. Если вы тестируете много разных алгоритмов, вы должны выполнить только 1-ю часть, затем выбрать алгоритм с наименьшей ошибкой и только для этого выполните 2-ю часть, чтобы выбрать гиперпараметры. Если вы используете только один алгоритм, то первая часть менее важна, если только вы не хотите заявить своему боссу или клиенту, что ваш лучший прогноз будущей ошибки классификатора равен x, и вы должны вычислить x, используя 1-й вложенное резюме.
Жак Вайнер,
0

Резюмируя ответ Жака,

Вложенное резюме требуется для оценки объективной ошибки модели. Мы можем сравнить счет различных моделей таким образом. Используя эту информацию, мы можем затем выполнить отдельный K-кратный цикл CV для настройки параметров выбранных моделей.

Шаран Нариболе
источник