Evalml: Ansambel bertumpuk berkinerja buruk

Dibuat pada 6 Apr 2021  ·  11Komentar  ·  Sumber: alteryx/evalml

Langkah-langkah untuk mereproduksi:

  1. Muat dataset Happiness ke evalml
  2. Jalankan cukup lama untuk memasukkan ansambel
  3. Regresor dasar menunjukkan peringkat lebih tinggi dari regresor bertumpuk.
    Data Kebahagiaan Kumpulan Lengkap.csv.zip
bug performance

Semua 11 komentar

@dancuarini Saya mencoba mereproduksi ini secara lokal tetapi tidak dapat; bisa jadi karena langkah tambahan sebelum menjalankan AutoMLSearch (mis: ukuran pemisahan data, penurunan cols). Mari kita bicara tentang konfigurasi masalah!

Inilah yang saya coba jalankan secara lokal:

from evalml.automl import AutoMLSearch
import pandas as pd
import woodwork as ww
from evalml.automl.callbacks import raise_error_callback

happiness_data_set = pd.read_csv("Happiness Data Full Set.csv")
y = happiness_data_set['Happiness']
X = happiness_data_set.drop(['Happiness'], axis=1)
# display(X.head())

X = ww.DataTable(X)
X_train, X_holdout, y_train, y_holdout = evalml.preprocessing.split_data(X, y, problem_type='regression', test_size=0.2, random_seed=0)
# print(X.types)

automl = AutoMLSearch(X, y, problem_type="regression", objective="MAE", error_callback=raise_error_callback, max_batches=20, ensembling=True)
automl.search()

Ini menghasilkan peringkat berikut:

image

Kemajuan saat ini: didiskusikan dengan @dancuarini tentang tidak dapat melakukan repro secara lokal, akan tetap berhubungan dengan @Cmancuso tentang repro-ing dan langkah selanjutnya.

@ angela97lin tunggu, apakah Anda yakin tidak dapat mengulangi ini? Di sini ensembler yang ditumpuk muncul di tengah peringkat - saya berharap itu berada di puncak!

Terima kasih telah berbagi reproduksi :)

@dsherry Meskipun agak mencurigakan bahwa stacked ensembler tidak berada di atas, masalah aslinya adalah bahwa stacked ensembler berkinerja sangat buruk sehingga peringkatnya di atas regressor baseline!

@ angela97lin ah ya mengerti! Saya mengirimi Anda beberapa catatan.

Saya pikir bukti bahwa ansambel kami tidak selalu dekat dengan puncak adalah masalah.

Menggali ini sedikit lebih. Saya pikir ada beberapa alasan potensial mengapa ensembler berkinerja buruk dengan kumpulan data ini:

  1. Dataset sangat kecil, dan strategi pemisahan data kami saat ini berarti bahwa ensembler disediakan dan divalidasi pada subset data yang sangat kecil. Saat ini, jika kita ingin melatih ensembler bertumpuk, kita membagi beberapa data (diidentifikasi dengan ensembling_indices ) untuk dilatih oleh ensembler. Ini untuk mencegah overfitting pada ensembler melalui pelatihan metalearner pada data yang sama dengan pipeline input yang sudah dilatih. Kami kemudian melakukan satu CV split, selanjutnya memisahkan data dari ensembling_indices . Untuk kumpulan data 128 baris ini, kami melatih dan memvalidasi masing-masing pada 17 dan 8 baris. Saya mengajukan #2144 untuk mendiskusikan apakah kami ingin melakukan pemisahan CV tambahan ini.

  2. Ensembler kami saat ini dibangun dengan mengambil jalur pipa terbaik dari setiap keluarga model yang ditemukan dan menggunakannya sebagai jalur pipa masukan untuk ensembler bertumpuk. Namun, jika beberapa pipeline input berkinerja sangat buruk, maka stacked ensembler mungkin tidak berkinerja sebaik pipeline individu berperforma tinggi.

Misalnya, ini adalah tabel peringkat akhir:
image

Kami melihat bahwa ensemble yang ditumpuk melakukan pukulan tepat di tengah--jika kami menyederhanakan dan mengatakan bahwa ensemble yang ditumpuk rata-rata memprediksi saluran inputnya, ini masuk akal. Untuk menguji hipotesis saya, saya memutuskan untuk hanya menggunakan keluarga model yang berkinerja lebih baik daripada ensembler bertumpuk, daripada semua keluarga model, dan memperhatikan bahwa skor yang dihasilkan berkinerja jauh lebih baik daripada pipa individu mana pun. Ini membuat saya percaya bahwa saluran pipa individu yang berkinerja buruk menyebabkan ensembler bertumpuk berkinerja lebih buruk.

Berikut kode repro untuk ini:

Dari atas:

import pandas as pd
import woodwork as ww
happiness_data_set = pd.read_csv("Happiness Data Full Set.csv")
y = happiness_data_set['Happiness']
X = happiness_data_set.drop(['Happiness'], axis=1)

X = ww.DataTable(X)
X_train, X_holdout, y_train, y_holdout = evalml.preprocessing.split_data(X, y, problem_type='regression', test_size=0.25, random_seed=0)

automl = AutoMLSearch(X, y, problem_type="regression", objective="MAE", error_callback=raise_error_callback, max_batches=10, ensembling=True)
automl.search()

import woodwork as ww
from evalml.automl.engine import train_and_score_pipeline
from evalml.automl.engine.engine_base import JobLogger

# Get the pipelines fed into the ensemble but only use the ones better than the stacked ensemble
input_pipelines = []
input_info = automl._automl_algorithm._best_pipeline_info
from evalml.model_family import ModelFamily

trimmed = dict()
trimmed.update({ModelFamily.RANDOM_FOREST: input_info[ModelFamily.RANDOM_FOREST]})
trimmed.update({ModelFamily.XGBOOST: input_info[ModelFamily.XGBOOST]})
trimmed.update({ModelFamily.DECISION_TREE: input_info[ModelFamily.EXTRA_TREES]})

for pipeline_dict in trimmed.values():
    pipeline_class = pipeline_dict['pipeline_class']
    pipeline_params = pipeline_dict['parameters']
    input_pipelines.append(pipeline_class(parameters=automl._automl_algorithm._transform_parameters(pipeline_class, pipeline_params),
                                                      random_seed=automl._automl_algorithm.random_seed))
ensemble_pipeline = _make_stacked_ensemble_pipeline(input_pipelines, "regression")
X_train = X.iloc[automl.ensembling_indices]
y_train = ww.DataColumn(y.iloc[automl.ensembling_indices])
train_and_score_pipeline(ensemble_pipeline, automl.automl_config, X_train, y_train, JobLogger())

Dengan hanya menggunakan tiga keluarga model ini, kami mendapatkan skor MAE ~0,22, yang jauh lebih baik daripada pipeline individu mana pun.

#output of train_and_score_pipeline(ensemble_pipeline, automl.automl_config, X_train, y_train, JobLogger())
{'scores': {'cv_data': [{'all_objective_scores': OrderedDict([('MAE',
                  0.22281276417465426),
                 ('ExpVariance', 0.9578811127332543),
                 ('MaxError', 0.3858477236606914),
                 ('MedianAE', 0.2790362808260225),
                 ('MSE', 0.0642654425375983),
                 ('R2', 0.9152119239698017),
                 ('Root Mean Squared Error', 0.2535062968401343),
                 ('# Training', 17),
                 ('# Validation', 9)]),
    'mean_cv_score': 0.22281276417465426,
    'binary_classification_threshold': None}],
  'training_time': 9.944366216659546,
  'cv_scores': 0    0.222813
  dtype: float64,
  'cv_score_mean': 0.22281276417465426},
 'pipeline': TemplatedPipeline(parameters={'Stacked Ensemble Regressor':{'input_pipelines': [GeneratedPipeline(parameters={'Imputer':{'categorical_impute_strategy': 'most_frequent', 'numeric_impute_strategy': 'most_frequent', 'categorical_fill_value': None, 'numeric_fill_value': None}, 'One Hot Encoder':{'top_n': 10, 'features_to_encode': None, 'categories': None, 'drop': 'if_binary', 'handle_unknown': 'ignore', 'handle_missing': 'error'}, 'Random Forest Regressor':{'n_estimators': 184, 'max_depth': 25, 'n_jobs': -1},}), GeneratedPipeline(parameters={'Imputer':{'categorical_impute_strategy': 'most_frequent', 'numeric_impute_strategy': 'mean', 'categorical_fill_value': None, 'numeric_fill_value': None}, 'One Hot Encoder':{'top_n': 10, 'features_to_encode': None, 'categories': None, 'drop': 'if_binary', 'handle_unknown': 'ignore', 'handle_missing': 'error'}, 'XGBoost Regressor':{'eta': 0.1, 'max_depth': 6, 'min_child_weight': 1, 'n_estimators': 100},}), GeneratedPipeline(parameters={'Imputer':{'categorical_impute_strategy': 'most_frequent', 'numeric_impute_strategy': 'mean', 'categorical_fill_value': None, 'numeric_fill_value': None}, 'One Hot Encoder':{'top_n': 10, 'features_to_encode': None, 'categories': None, 'drop': 'if_binary', 'handle_unknown': 'ignore', 'handle_missing': 'error'}, 'Extra Trees Regressor':{'n_estimators': 100, 'max_features': 'auto', 'max_depth': 6, 'min_samples_split': 2, 'min_weight_fraction_leaf': 0.0, 'n_jobs': -1},})], 'final_estimator': None, 'cv': None, 'n_jobs': -1},}),

Ini membuat saya bertanya-tanya apakah kita perlu memikirkan kembali tentang jalur pipa input apa yang harus kita masukkan ke ensembler bertumpuk kita.

  1. Metalearner yang kami gunakan (LinearRegressor) bukanlah yang terbaik. Saya menguji ini melalui cabang stacking_test saya buat di mana saya memperbarui metalearner default ke RidgeCV (scikit-learn default, tetapi kami tidak memilikinya di EvalML), dan kinerja ensembler jauh lebih baik:
    image

Langkah selanjutnya setelah berdiskusi dengan @dsherry :

Coba #1 dan #3 (menggunakan Elastic Net) pada dataset lain, jalankan uji kinerja, lihat apakah kami bisa mendapatkan kinerja yang lebih baik secara keseluruhan.

@ angela97lin Poin Anda tentang pemisahan, untuk kumpulan data kecil, tepat sasaran. Pada akhirnya, kita perlu menangani kumpulan data kecil dengan sangat berbeda dari yang lebih besar, misalnya dengan hanya menggunakan xval jumlah lipatan tinggi pada seluruh kumpulan data, bahkan LOOCV, dan memastikan bahwa kita menyusun lipatan secara berbeda untuk pelatihan ensemble metalearner.

Saya juga setuju bahwa metalearner perlu menggunakan regularisasi yang kuat. Saya menggunakan Elastic Net di H2O-3 StackedEnsemble, dan hanya ingat satu kali bahwa ensemble berada di urutan kedua di papan peringkat. Setiap kali saya menguji, itu yang pertama. Regularisasi tidak boleh membiarkan model yang buruk menurunkan kinerja ansambel.

Dan ini memberi makan seluruh papan peringkat bahkan 50 model ke dalam metalearner. :-)

Hanya memposting beberapa pembaruan tambahan tentang ini:

Diuji secara lokal menggunakan semua kumpulan data regresi. Hasil dapat ditemukan di sini atau hanya grafik di sini .

Dari sini:

  • Setuju @rpeck! Kami harus memperbarui metalearner untuk menggunakan regularisasi yang kuat dengan pasti. ElasticNetCV tampaknya berkinerja lebih baik daripada LinearRegressor kami di banyak kumpulan data. Masalah ini melacak ini: https://github.com/alteryx/evalml/issues/1739
  • @dsherry dan saya membahas kembali strategi pemisahan data kami: Saat ini, kami memisahkan data untuk ansambel. Namun, ini dengan asumsi bahwa kami ingin para metalearner dilatih pada indeks ensemble ini. Dengan implementasi scikit-learn, ketika kami melatih StackedEnsembler pada pemisahan indeks ensemble ini, kami akhirnya melatih saluran input dan metalearner pada kumpulan data kecil ini. Ini mungkin bisa menjadi alasan kami tidak berkinerja baik. Sementara parameter untuk saluran pipa input kami berasal dari penyetelan menggunakan data lain, saluran pipa ini tidak dipasang. Dalam jangka panjang, menggulirkan implementasi kita sendiri dapat memungkinkan kita untuk meneruskan pipeline terlatih ke ensembler, dalam hal ini kita akan memiliki perilaku yang kita inginkan. Untuk saat ini, tidak demikian.

Langkah selanjutnya: Uji hipotesis ini dengan ensembler secara manual. Coba latih jalur input secara manual pada 80% data, buat prediksi yang divalidasi silang pada data yang disisihkan untuk ensambling, dan latih metalearner dengan prediksi di luar.

Hasil dari eksperimen terlihat bagus: https://alteryx.quip.com/4hEyAaTBZDap/Ensembling-Performance-Using-More-Data

Langkah selanjutnya:

Setelah menggali lebih dalam, kami percaya bahwa masalahnya bukan pada bagaimana kinerja ansambel, tetapi lebih pada bagaimana kami melaporkan kinerja ansambel. Saat ini, kami melakukan pemisahan ensemble terpisah yang merupakan 20% dari data, dan kemudian melakukan pemisahan validasi kereta lainnya, dan melaporkan skor ensemble sebagai data validasi. Ini berarti bahwa dalam beberapa kasus, skor ensemble dihitung menggunakan jumlah baris yang sangat sedikit (seperti dataset kebahagiaan di atas).

Dengan menghapus pembagian indeks ensemble dan menggunakan metode lama kami untuk menghitung skor pelatihan cv untuk ansambel (berikan semua data, latih, dan validasi dalam satu lipatan), kami melihat bahwa ensemble berperingkat lebih tinggi di hampir semua kasus, dan muncul sebagai # 1 dalam lebih banyak kasus. Sedangkan nilai validasinya sama atau sedikit lebih baik.

Perhatikan bahwa karena kami tidak melakukan penyetelan hyperparameter apa pun, saluran pipa input tidak dilatih, dan ansambel hanya mendapatkan prediksi saluran pipa input sebagai input, overfitting tidak menjadi masalah. Kami dapat meninjau kembali penerapan ansambel kami sendiri dan memperbarui strategi pemisahan, tetapi untuk saat ini, kami dapat melihat peningkatan hanya dengan mengubah strategi pemisahan data dan implementasi scikit-learn.

Perhatikan bahwa ini akan menyebabkan peningkatan waktu kecocokan saat ensemble diaktifkan: semua pipeline melihat lebih banyak data (tidak ada indeks ensemble yang dicadangkan), dan ensemble dilatih pada lebih banyak data. Saya pikir ini baik-baik saja.

Hasil ditabulasikan di sini: https://alteryx.quip.com/jI2mArnWZfTU/Ensembling-vs-Best-Pipeline-Validation-Scores#MKWACADlCDt

Apakah halaman ini membantu?
0 / 5 - 0 peringkat