Ensemble dari berbagai jenis regresi menggunakan scikit-learn (atau kerangka python lainnya)

27

Saya mencoba menyelesaikan tugas regresi. Saya menemukan bahwa 3 model bekerja dengan baik untuk subset data yang berbeda: LassoLARS, SVR dan Gradient Tree Boosting. Saya perhatikan bahwa ketika saya membuat prediksi menggunakan ketiga model ini dan kemudian membuat tabel 'output nyata' dan output dari 3 model saya, saya melihat bahwa setiap kali setidaknya salah satu model benar-benar dekat dengan output yang sebenarnya, meskipun 2 lainnya bisa relatif jauh.

Ketika saya menghitung kesalahan seminimal mungkin (jika saya mengambil prediksi dari prediktor 'terbaik' untuk setiap contoh pengujian) saya mendapatkan kesalahan yang jauh lebih kecil daripada kesalahan dari model mana pun saja. Jadi saya berpikir untuk mencoba menggabungkan prediksi dari 3 model berbeda ini menjadi semacam ansambel. Pertanyaannya adalah, bagaimana cara melakukannya dengan benar? Ketiga model saya dibuat dan disetel menggunakan scikit-learn, apakah ia menyediakan semacam metode yang dapat digunakan untuk mengemas model ke dalam ansambel? Masalahnya di sini adalah bahwa saya tidak ingin hanya prediksi rata-rata dari ketiga model, saya ingin melakukan ini dengan pembobotan, di mana pembobotan harus ditentukan berdasarkan sifat-sifat contoh tertentu.

Bahkan jika scikit-belajar tidak menyediakan fungsionalitas seperti itu, akan lebih baik jika seseorang tahu bagaimana properti menangani tugas ini - mencari tahu bobot masing-masing model untuk setiap contoh dalam data. Saya pikir itu mungkin dilakukan oleh regressor terpisah yang dibangun di atas semua 3 model ini, yang akan mencoba menghasilkan bobot optimal untuk masing-masing dari 3 model, tetapi saya tidak yakin apakah ini adalah cara terbaik untuk melakukan ini.

Maksim Khaitovich
sumber

Jawaban:

32

Sebenarnya, scikit-learnmemang menyediakan fungsi seperti itu, meskipun mungkin agak sulit untuk diterapkan. Berikut ini adalah contoh kerja lengkap dari regresi rata-rata yang dibangun di atas tiga model. Pertama-tama, mari kita impor semua paket yang diperlukan:

from sklearn.base import TransformerMixin
from sklearn.datasets import make_regression
from sklearn.pipeline import Pipeline, FeatureUnion
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor
from sklearn.neighbors import KNeighborsRegressor
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import LinearRegression, Ridge

Kemudian, kita perlu mengubah tiga model regresi kita menjadi transformer. Ini akan memungkinkan kami untuk menggabungkan prediksi mereka ke dalam vektor fitur tunggal menggunakan FeatureUnion:

class RidgeTransformer(Ridge, TransformerMixin):

    def transform(self, X, *_):
        return self.predict(X)


class RandomForestTransformer(RandomForestRegressor, TransformerMixin):

    def transform(self, X, *_):
        return self.predict(X)


class KNeighborsTransformer(KNeighborsRegressor, TransformerMixin):

    def transform(self, X, *_):
        return self.predict(X)

Sekarang, mari kita mendefinisikan fungsi pembangun untuk model frankenstein kami:

def build_model():
    ridge_transformer = Pipeline(steps=[
        ('scaler', StandardScaler()),
        ('poly_feats', PolynomialFeatures()),
        ('ridge', RidgeTransformer())
    ])

    pred_union = FeatureUnion(
        transformer_list=[
            ('ridge', ridge_transformer),
            ('rand_forest', RandomForestTransformer()),
            ('knn', KNeighborsTransformer())
        ],
        n_jobs=2
    )

    model = Pipeline(steps=[
        ('pred_union', pred_union),
        ('lin_regr', LinearRegression())
    ])

    return model

Akhirnya, mari kita muat modelnya:

print('Build and fit a model...')

model = build_model()

X, y = make_regression(n_features=10, n_targets=2)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

model.fit(X_train, y_train)
score = model.score(X_test, y_test)

print('Done. Score:', score)

Keluaran:

Build and fit a model...
Done. Score: 0.9600413867438636

Mengapa repot menyulitkan hal-hal sedemikian rupa? Nah, pendekatan ini memungkinkan kita untuk mengoptimalkan model hiperparameter menggunakan scikit-learnmodul standar seperti GridSearchCVatau RandomizedSearchCV. Selain itu, sekarang dimungkinkan untuk dengan mudah menyimpan dan memuat dari disk model yang sudah dilatih sebelumnya.

constt
sumber
Saat menggunakan pendekatan ini, apakah ada cara sederhana untuk mengekstrak algo mana yang digunakan ketika / berapa fraksi dari setiap algo?
David Hagan
Mungkin dengan melihat koefisien dari model linier yang dihasilkan ( model.named_steps['lin_regr'].coef_) akan memberi Anda wawasan tentang seberapa banyak masing-masing model dalam sebuah ensemble berkontribusi pada solusi akhir.
constt
@constt Tidakkah Anda perlu menggunakan cross_val_predict dalam model dasar Anda? Sepertinya model tingkat atas Anda akan mendapatkan sinyal optimis berlebihan dari model dasar Anda karena ini sedang diterapkan.
Brian Bien
1
Ini hanya contoh bukti konsep, saya tidak membahas pemilihan model di sini. Saya pikir model seperti itu harus dioptimalkan secara keseluruhan, yaitu, mengoptimalkan hiper-parameter semua model bawaan secara bersamaan menggunakan pendekatan cross-validation.
constt
jika kita menempatkan n_targets = 1 X, y = make_regression(n_features=10, n_targets=1)itu memberikan kesalahan dimensi. adakah yang bisa menjelaskan apa yang harus dilakukan?
Mohit Yadav
9

Ok, setelah menghabiskan beberapa waktu di googling saya menemukan bagaimana saya bisa melakukan pembobotan dengan python bahkan dengan scikit-belajar. Pertimbangkan di bawah ini:

Saya melatih satu set model regresi saya (seperti yang disebutkan SVR, LassoLars dan GradientBoostingRegressor). Kemudian saya menjalankan semuanya pada data pelatihan (data yang sama yang digunakan untuk pelatihan masing-masing dari 3 regresi ini). Saya mendapatkan prediksi untuk contoh dengan masing-masing algoritme saya dan menyimpan 3 hasil ini ke dalam bingkai data panda dengan kolom 'predictSVR', 'predictLASSO' dan 'predictGBGB'. Dan saya menambahkan kolom terakhir ke dalam data data ini yang saya sebut 'diprediksi' yang merupakan nilai prediksi nyata.

Lalu saya hanya melatih regresi linier pada kerangka data baru ini:

 #df - dataframe with results of 3 regressors and true output

 from sklearn linear_model
 stacker= linear_model.LinearRegression()
 stacker.fit(df[['predictedSVR', 'predictedLASSO', 'predictedGBR']], df['predicted'])

Jadi ketika saya ingin membuat prediksi untuk contoh baru, saya hanya menjalankan masing-masing dari 3 regresi saya secara terpisah dan kemudian saya lakukan:

 stacker.predict() 

pada output dari 3 regressor saya. Dan dapatkan hasilnya.

Masalahnya di sini adalah bahwa saya menemukan bobot optimal untuk rata-rata regressor, bobotnya akan sama untuk setiap contoh yang akan saya coba prediksi.

Jika ada yang punya ide tentang bagaimana melakukan penumpukan (pembobotan) menggunakan fitur contoh saat ini akan lebih baik untuk mendengarnya.

Maksim Khaitovich
sumber
Wow, saya sangat menyukai pendekatan ini! Tapi kenapa kau digunakan LinearRegression()bukan LogisticRegression()model yang?
harrison4
1
@ harrison4 karena saya sedang melakukan regresi, bukan tugas klasifikasi? Jadi saya ingin 'bobot' keluaran dari masing-masing model. Bagaimanapun, ini adalah pendekatan yang buruk, yang bagus dijelaskan di sini: stackoverflow.com/a/35170149/3633250
Maksim Khaitovich
Ya, maaf kamu benar! Terima kasih telah membagikan tautannya!
harrison4
5

Jika data Anda memiliki himpunan bagian yang jelas, Anda bisa menjalankan algoritma pengelompokan seperti k-means dan kemudian mengaitkan setiap classifier dengan cluster yang berkinerja baik. Ketika titik data baru tiba, maka tentukan cluster apa itu dan jalankan classifier terkait.

Anda juga bisa menggunakan jarak terbalik dari centroid untuk mendapatkan satu set bobot untuk setiap classifier dan memprediksi menggunakan kombinasi linear dari semua classifier.

anthonybell
sumber
Saya menemukan sebuah makalah menguji stategy ini (bersama dengan perbandingan beberapa ide serupa): paper
anthonybell
Ide yang menarik, meskipun membutuhkan banyak pekerjaan untuk menerapkannya. Terima kasih untuk kertas!
Maksim Khaitovich
1

Saya mencapai jenis pembobotan dengan melakukan hal berikut, setelah semua model Anda sepenuhnya terlatih dan berkinerja baik:

  1. Jalankan semua model Anda pada sejumlah besar data pengujian yang tidak terlihat
  2. Simpan skor f1 pada set tes untuk setiap kelas, untuk setiap model
  3. Saat Anda memprediksi dengan ansambel, setiap model akan memberi Anda kelas yang paling mungkin, jadi beri bobot kepercayaan diri atau probabilitas dengan skor f1 untuk model itu di kelas itu. Jika Anda berurusan dengan jarak (seperti dalam SVM, misalnya), cukup normalkan jarak untuk mendapatkan kepercayaan umum, dan kemudian lanjutkan dengan bobot per kelas f1.

Anda dapat menyetel lebih lanjut ansambel Anda dengan mengukur persentase yang benar dalam beberapa waktu. Setelah Anda memiliki skor data baru yang sangat besar, Anda dapat merencanakan ambang pada langkah-langkah 0,1, misalnya, terhadap persen yang benar jika menggunakan ambang tersebut untuk mencetak skor, untuk mendapatkan gambaran tentang ambang yang akan memberi Anda, katakanlah, 95% benar untuk kelas 1, dan seterusnya. Anda dapat terus memperbarui set tes dan skor f1 saat data baru masuk dan melacak drift, membangun kembali model ketika ambang atau akurasi jatuh.

wwwslinger
sumber
1
Itu menarik, tetapi hanya berfungsi untuk tugas klasifikasi, sejauh yang saya lihat, sementara saya mencoba menyelesaikan tugas regresi. Jadi saya tidak bisa menghitung skor F1.
Maksim Khaitovich