Validasi silang bersarang dan memilih model regresi terbaik - apakah ini proses SKLearn yang tepat?

8

Jika saya mengerti dengan benar, nested-CV dapat membantu saya mengevaluasi model dan proses penyetelan hyperparameter apa yang terbaik. Loop dalam ( GridSearchCV) menemukan hyperparameter terbaik, dan loop outter ( cross_val_score) mengevaluasi algoritma tuning hyperparameter. Saya kemudian memilih yang tuning / model combo dari loop luar yang meminimalkan mse(Saya sedang melihat classifier regresi) untuk tes model akhir saya.

Saya sudah membaca pertanyaan / jawaban tentang validasi silang-silang, tetapi belum melihat contoh pipeline lengkap yang memanfaatkan ini. Jadi, apakah kode saya di bawah ini (abaikan rentang hyperparameter yang sebenarnya - ini hanya sebagai contoh) dan proses berpikir masuk akal?

from sklearn.cross_validation import cross_val_score, train_test_split
from sklearn.grid_search import GridSearchCV
from sklearn.metrics import mean_squared_error
from sklearn.ensemble import RandomForestRegressor
from sklearn.svm import SVR
from sklearn.datasets import make_regression

# create some regression data
X, y = make_regression(n_samples=1000, n_features=10)
params = [{'C':[0.01,0.05,0.1,1]},{'n_estimators':[10,100,1000]}]

# setup models, variables
mean_score = []
models = [SVR(), RandomForestRegressor()]

# split into train and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.3)

# estimate performance of hyperparameter tuning and model algorithm pipeline
for idx, model in enumerate(models):
    clf = GridSearchCV(model, params[idx], scoring='mean_squared_error')

    # this performs a nested CV in SKLearn
    score = cross_val_score(clf, X_train, y_train, scoring='mean_squared_error')

    # get the mean MSE across each fold
    mean_score.append(np.mean(score))
    print('Model:', model, 'MSE:', mean_score[-1])

# estimate generalization performance of the best model selection technique
best_idx = mean_score.index(max(mean_score)) # because SKLearn flips MSE signs, max works OK here
best_model = models[best_idx]

clf_final = GridSearchCV(best_model, params[best_idx])
clf_final.fit(X_train, y_train)

y_pred = clf_final.predict(X_test)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))

print('Final Model': best_model, 'Final model RMSE:', rmse)
BobbyJohnsonOG
sumber

Jawaban:

8

Anda bukan contoh validasi silang bersarang.

Validasi silang bersarang berguna untuk mengetahui apakah, katakanlah, hutan acak atau SVM lebih cocok untuk masalah Anda. Nested CV hanya menghasilkan skor, itu tidak menampilkan model seperti dalam kode Anda.

Ini akan menjadi contoh validasi lintas bersarang:

from sklearn.datasets import load_boston
from sklearn.cross_validation import KFold
from sklearn.metrics import mean_squared_error
from sklearn.grid_search import GridSearchCV
from sklearn.ensemble import RandomForestRegressor
from sklearn.svm import SVR
import numpy as np

params = [{'C': [0.01, 0.05, 0.1, 1]}, {'n_estimators': [10, 100, 1000]}]
models = [SVR(), RandomForestRegressor()]

df = load_boston()
X = df['data']
y = df['target']

cv = [[] for _ in range(len(models))]
for tr, ts in KFold(len(X)):
    for i, (model, param) in enumerate(zip(models, params)):
        best_m = GridSearchCV(model, param)
        best_m.fit(X[tr], y[tr])
        s = mean_squared_error(y[ts], best_m.predict(X[ts]))
        cv[i].append(s)
print(np.mean(cv, 1))

Ngomong-ngomong, beberapa pemikiran:

  • Saya tidak melihat ada gunanya untuk mencari jaringan untuk n_estimatorshutan acak Anda. Jelas, semakin banyak, semakin meriah. Hal-hal seperti max_depthadalah jenis regularisasi yang ingin Anda optimalkan. Kesalahan untuk CV bersarang RandomForestjauh lebih tinggi karena Anda tidak mengoptimalkan untuk hyperparameter yang tepat, belum tentu karena itu adalah model yang lebih buruk.
  • Anda mungkin juga ingin mencoba meningkatkan pohon gradien.
Ricardo Cruz
sumber
Terima kasih untuk itu. Tujuan saya adalah melakukan persis apa yang Anda katakan - mencari tahu algoritma penglasifikasi apa yang paling cocok untuk masalah saya. Saya kira saya bingung dalam hal dokumentasi SKLearn: scikit-learn.org/stable/tutorial/statribution_inference/… (di bawah 'validasi silang bersarang')
BobbyJohnsonOG
Untuk menguji kinerja model yang dipilih terbaik, apakah saya akan melakukan validasi silang akhir pada seluruh dataset? Atau haruskah saya membagi dataset saya menjadi kereta / tes SEBELUM CV bersarang, jalankan CV bersarang di kereta, dan kemudian pas dengan model terbaik pada data kereta dan tes pada tes?
BobbyJohnsonOG
Maaf atas rentetan komentar. Jadi model terakhir saya adalah:best_idx = np.where(np.mean(cv,1).min())[0]; final_m = GridSearchCV(models[best_idx], params[best_idx]); final_m.fit(X,y)
BobbyJohnsonOG
Membangun dari apa yang Anda katakan, ini adalah apa yang saya akan lakukan dengan fungsi SKLearn built-in (memberikan jawaban yang sama dengan Anda):for model, param in zip(models, params): clf = GridSearchCV(model, param) my_score = cross_val_score(clf, X, y, scoring='mean_squared_error') my_scores.append(my_score)
BobbyJohnsonOG
7

Validasi silang bersarang memperkirakan kesalahan generalisasi suatu model, jadi ini adalah cara yang baik untuk memilih model terbaik dari daftar model kandidat dan kisi-kisi parameter yang terkait. Posting asli dekat dengan melakukan CV bersarang: daripada melakukan split kereta-tes tunggal, orang harus menggunakan splitter validasi silang kedua. Yaitu, satu "sarang" pembagi validasi silang "dalam" di dalam pembagi validasi silang "luar".

Splitter validasi silang dalam digunakan untuk memilih hiperparameter. Pemisah validasi silang luar rata-rata kesalahan pengujian pada beberapa pemisahan kereta-uji. Rata-rata kesalahan generalisasi pada beberapa split kereta-uji memberikan perkiraan yang lebih andal dari akurasi model pada data yang tidak terlihat.

Saya memodifikasi kode posting asli untuk memperbaruinya ke versi terbaru sklearn(dengan sklearn.cross_validationdigantikan oleh sklearn.model_selectiondan dengan 'mean_squared_error'digantikan oleh 'neg_mean_squared_error'), dan saya menggunakan dua KFoldsplitter validasi silang untuk memilih model terbaik. Untuk mempelajari lebih lanjut tentang validasi silang bersarang, lihat sklearn's contoh pada bersarang lintas validasi .

from sklearn.model_selection import KFold, cross_val_score, GridSearchCV
from sklearn.datasets import make_regression
from sklearn.ensemble import RandomForestRegressor
from sklearn.svm import SVR
import numpy as np

# `outer_cv` creates 3 folds for estimating generalization error
outer_cv = KFold(3)

# when we train on a certain fold, we use a second cross-validation
# split in order to choose hyperparameters
inner_cv = KFold(3)

# create some regression data
X, y = make_regression(n_samples=1000, n_features=10)

# give shorthand names to models and use those as dictionary keys mapping
# to models and parameter grids for that model
models_and_parameters = {
    'svr': (SVR(),
            {'C': [0.01, 0.05, 0.1, 1]}),
    'rf': (RandomForestRegressor(),
           {'max_depth': [5, 10, 50, 100, 200, 500]})}

# we will collect the average of the scores on the 3 outer folds in this dictionary
# with keys given by the names of the models in `models_and_parameters`
average_scores_across_outer_folds_for_each_model = dict()

# find the model with the best generalization error
for name, (model, params) in models_and_parameters.items():
    # this object is a regressor that also happens to choose
    # its hyperparameters automatically using `inner_cv`
    regressor_that_optimizes_its_hyperparams = GridSearchCV(
        estimator=model, param_grid=params,
        cv=inner_cv, scoring='neg_mean_squared_error')

    # estimate generalization error on the 3-fold splits of the data
    scores_across_outer_folds = cross_val_score(
        regressor_that_optimizes_its_hyperparams,
        X, y, cv=outer_cv, scoring='neg_mean_squared_error')

    # get the mean MSE across each of outer_cv's 3 folds
    average_scores_across_outer_folds_for_each_model[name] = np.mean(scores_across_outer_folds)
    error_summary = 'Model: {name}\nMSE in the 3 outer folds: {scores}.\nAverage error: {avg}'
    print(error_summary.format(
        name=name, scores=scores_across_outer_folds,
        avg=np.mean(scores_across_outer_folds)))
    print()

print('Average score across the outer folds: ',
      average_scores_across_outer_folds_for_each_model)

many_stars = '\n' + '*' * 100 + '\n'
print(many_stars + 'Now we choose the best model and refit on the whole dataset' + many_stars)

best_model_name, best_model_avg_score = max(
    average_scores_across_outer_folds_for_each_model.items(),
    key=(lambda name_averagescore: name_averagescore[1]))

# get the best model and its associated parameter grid
best_model, best_model_params = models_and_parameters[best_model_name]

# now we refit this best model on the whole dataset so that we can start
# making predictions on other data, and now we have a reliable estimate of
# this model's generalization error and we are confident this is the best model
# among the ones we have tried
final_regressor = GridSearchCV(best_model, best_model_params, cv=inner_cv)
final_regressor.fit(X, y)

print('Best model: \n\t{}'.format(best_model), end='\n\n')
print('Estimation of its generalization error (negative mean squared error):\n\t{}'.format(
    best_model_avg_score), end='\n\n')
print('Best parameter choice for this model: \n\t{params}'
      '\n(according to cross-validation `{cv}` on the whole dataset).'.format(
      params=final_regressor.best_params_, cv=inner_cv))
Charlie Brummitt
sumber
Pada komentar terakhir, Anda mengatakan "... perbaiki model terbaik ini di seluruh rangkaian pelatihan" tetapi Anda benar-benar melakukannya pada seluruh kumpulan data ( Xdan y). Sejauh yang saya mengerti ini adalah hal yang benar untuk dilakukan, tetapi kemudian komentar harus diperbaiki. Bagaimana menurut anda?
Dror Atariah
Terima kasih @DrorAtariah untuk menangkapnya. Kamu benar. Aku telah memperbaikinya.
Charlie Brummitt
1

Anda tidak perlu

# this performs a nested CV in SKLearn
score = cross_val_score(clf, X_train, y_train, scoring='mean_squared_error')

GridSearchCVlakukan ini untukmu. Untuk mendapatkan intuisi dari proses pencarian jaringan, coba gunakan GridSearchCV(... , verbose=3)

Untuk mengekstraksi skor untuk setiap lipatan, lihat contoh ini di dokumentasi scikit-learn

lanenok
sumber
Saya pikir pencarian kotak hanya untuk mengoptimalkan parameter hiper? Bagaimana saya menggunakan gridsearch bersamaan dengan sesuatu yang lain untuk mengetahui algoritma classifier terbaik (yaitu SVR vs. RandomForest)?
BobbyJohnsonOG
Iya. Untuk setiap kombinasi hyper-parameter, GridSearchCV membuat lipatan dan menghitung skor (berarti kuadrat kesalahan dalam kasus Anda) pada data yang ditinggalkan. Dengan demikian setiap kombinasi parameter-hiper mendapatkan skor rata-rata sendiri. "Optimasi" hanya memilih kombinasi dengan skor rata-rata terbaik. Anda dapat mengekstrak skor rata-rata tersebut dan membandingkannya secara langsung untuk berbagai model.
lanenok