Xgboost: [Fitur Baru] Kendala Monoton dalam Konstruksi Pohon

Dibuat pada 27 Agu 2016  ·  46Komentar  ·  Sumber: dmlc/xgboost

Saya mendapat beberapa permintaan untuk mendukung kendala monoton pada fitur tertentu sehubungan dengan output,

yaitu ketika fitur lain diperbaiki, memaksa prediksi menjadi monoton meningkat sehubungan dengan fitur tertentu yang ditentukan. Saya membuka edisi ini untuk melihat minat umum pada fitur ini. Saya dapat menambahkan ini jika ada cukup minat pada ini,

Saya membutuhkan bantuan dari sukarelawan dari komunitas untuk menguji fitur beta dan menyumbangkan dokumen dan tutorial tentang penggunaan fitur ini. Harap balas masalah ini jika Anda tertarik

Komentar yang paling membantu

Saat ini fitur ini tidak ada di api Sklearn. Bisakah Anda atau seseorang tolong bantu menambahkannya? Terima kasih!

Semua 46 komentar

Versi eksperimental disediakan di https://github.com/dmlc/xgboost/pull/1516. Untuk menggunakan ini sebelum digabungkan, klon repo https://github.com/tqchen/xgboost ,

Aktifkan opsi berikut (kemungkinan mungkin melalui python, r API)

monotone_constraints = "(0,1,1,0)"

Ada dua argumen

  • monotone_constraints adalah daftar panjang jumlah fitur, 1 menunjukkan peningkatan monoton, - 1 berarti menurun, 0 berarti tidak ada kendala. Jika lebih pendek dari jumlah fitur, 0 akan diisi.

    • Saat ini mendukung format tuple python, Anda dapat meneruskan hal-hal sebagai string saat menggunakan r

Hal-hal yang perlu diverifikasi

  • [x] Kecepatan pemacu pohon asli tidak melambat (saya sedikit mengubah struktur kode, secara teori pengoptimalan templat akan menyejajarkannya, tetapi perlu mengonfirmasi)
  • [x] Kecepatan dan ketepatan regresi monoton
  • [x] Pertunjukan dengan memperkenalkan batasan ini

Batasan yang diketahui

Saat ini hanya mendukung algoritma serakah yang tepat pada multi-core. Belum tersedia dalam versi terdistribusi

@tqchen Saya mendapat permintaan di tempat kerja hari ini untuk membangun beberapa GBM dengan kendala monoton untuk menguji vs. kinerja beberapa model lain. Ini akan dengan kerugian penyimpangan tweedie, jadi saya harus pergi dengan fungsi kerugian kustom seperti berdiri hari ini.

Bagaimanapun, sepertinya ini kesempatan bagus untuk membantu dan menyelesaikan beberapa pekerjaan pada saat yang bersamaan.

Berdasarkan pembicaraan di sini , GBM(R Package) hanya memberlakukan monotonisitas secara lokal.
Bisakah Anda mengklarifikasi bagaimana XGBoost menegakkan batasan monoton?
Akan sangat bagus jika XGBoost dapat menerapkan batasan global.

Saya tidak mengerti apa yang Anda maksud dengan batasan lokal atau gloabl, dapatkah Anda menjelaskannya?

Maaf, saya menempelkan tautan yang salah, ini yang benar (Tautan)
Setiap pohon hanya dapat mengikuti kendala monoton di subset tertentu dari fitur yang menarik, sehingga banyak pohon yang bergabung bersama dapat menciptakan pelanggaran monotonisitas keseluruhan pada seluruh rentang fitur tersebut.

Oke, menurut pemahaman saya, ini diberlakukan secara global. Anda dipersilakan untuk mencobanya.

Baru saja melakukan beberapa tes sederhana kendala monotonisitas dalam konteks regresi univariat. Anda dapat menemukan kode dan beberapa dokumentasi yang sangat singkat di sini:

https://github.com/XiaoxiaoWang87/xgboost_mono_test/blob/master/xgb_monotonicity_constraint_testing1-univariate.ipynb

Beberapa pengamatan awal:

  • Untuk masalah regresi variabel tunggal, kendala monoton = +1 tampaknya bekerja dengan baik
  • Untuk masalah regresi variabel tunggal, dalam dataset saya kendala monoton = -1 tampaknya tidak menghasilkan fungsi yang menurun secara monoton. Sebaliknya, itu memberikan konstanta. Namun hal ini juga bisa disebabkan karena kurangnya perbaikan saat memaksakan constraint. Untuk dikonfirmasi (sesuai saran Tianqi, coba balikkan dataset dan tetapkan batasan sebagai +1).
  • Menambahkan batasan (dengan benar) berpotensi mencegah overfitting dan memberikan beberapa manfaat kinerja/interpretasi.

Ternyata saya memperkenalkan bug dalam kasus kendala = -1. Saya mendorong perbaikan, harap lihat apakah versi terbaru berfungsi dengan baik. Harap periksa juga apakah itu berfungsi ketika ada banyak kendala

@tqchen Saya menguji perbaikan Anda untuk bug yang berkurang, sepertinya itu berfungsi sekarang.

xgboost-no-constraint
xgboost-with-constraint

Mari kita konfirmasi jika ada penurunan kecepatan vs versi asli pada beberapa dataset standar, maka kita dapat menggabungkannya

@tqchen Saya menguji dua model variabel, satu dengan kendala yang meningkat dan satu lagi dengan penurunan:

params_constrained = params.copy()
params_constrained['updater'] = "grow_monotone_colmaker,prune"
params_constrained['monotone_constraints'] = "(1,-1)"

Hasilnya bagus

xgboost-two-vars-increasing
xgboost-two-vars-decreasing

Saya akan mencoba mencari sedikit waktu untuk melakukan beberapa tes waktu sore ini.

Saya membuat pembaruan ke #1516 untuk memungkinkan deteksi otomatis opsi montone, sekarang pengguna hanya perlu memasukkan monotone_constraints = "(0,1,1,0)" , silakan periksa apakah itu berfungsi.

Saya akan menggabungkan ini jika tes kecepatan berjalan dengan baik, dan mari kita lanjutkan ke tahap berikutnya untuk menambahkan tutorial

@madrury @XiaoxiaoWang87

Menambahkan tes untuk kasus multivariat di sini:

https://github.com/XiaoxiaoWang87/xgboost_mono_test/blob/master/xgb_monotonicity_constraint_testing2-multivariate.ipynb

  • Saya mengkonfirmasi sekarang kedua kendala monoton = 1 dan = -1 bekerja seperti yang diharapkan.
  • Membatasi monotonisitas tidak menyebabkan penurunan kecepatan* yang jelas
    *kecepatan = rata-rata [ waktu hingga penghentian awal / jumlah iterasi peningkatan hingga penghentian awal ]

no constraint: 964.9 microseconds per iteration
with constraint: 861.7 microseconds per iteration

(silakan komentar jika Anda memiliki cara yang lebih baik untuk melakukan tes kecepatan)

  • Perlu berhati-hati ketika membatasi arah untuk variabel non-monoton. Hal ini dapat menyebabkan penurunan kinerja.
  • Melihat kode macet karena Check failed: (wleft) <= (wright) saat bermain di sekitar parameter hiper yang berbeda.

Saya menjalankan beberapa percobaan waktu di notebook jupyter.

Tes pertama: beberapa data simulasi sederhana. Ada dua fitur, satu meningkat dan satu menurun, tetapi dengan gelombang sinusoidal kecil yang ditumpangkan sehingga setiap fitur tidak benar-benar monoton

X = np.random.random(size=(N, K))
y = (5*X[:, 0] + np.sin(5*2*pi*X[:, 0])
     - 5*X[:, 1] - np.cos(5*2*pi*X[:, 1])
     + np.random.normal(loc=0.0, scale=0.01, size=N))

Berikut adalah hasil pengaturan waktu dari xgboosts dengan dan tanpa kendala monoton. Saya mematikan penghentian awal dan meningkatkan sejumlah iterasi untuk masing-masing.

Pertama tanpa kendala monoton:

%%timeit -n 100
model_no_constraints = xgb.train(params, dtrain, 
                                 num_boost_round = 2500, 
                                 verbose_eval = False)

100 loops, best of 3: 246 ms per loop

Dan di sini dengan kendala monoton

%%timeit -n 100
model_with_constraints = xgb.train(params_constrained, dtrain, 
                                 num_boost_round = 2500, 
                                 verbose_eval = False)

100 loops, best of 3: 196 ms per loop

Tes kedua: Data perumahan California dari sklearn. Tanpa kendala

%%timeit -n 10
model_no_constraints = xgb.train(params, dtrain, 
                                 num_boost_round = 2500, 
                                 verbose_eval = False)

10 loops, best of 3: 5.9 s per loop

Berikut adalah batasan yang saya gunakan

print(params_constrained['monotone_constraints'])

(1,1,1,0,0,1,0,0)

Dan waktu untuk model yang dibatasi

%%timeit -n 10
model_no_constraints = xgb.train(params, dtrain, 
                                 num_boost_round = 2500, 
                                 verbose_eval = False)

10 loops, best of 3: 6.08 s per loop

@XiaoxiaoWang87 Saya telah mendorong PR lain untuk kehilangan centang pada wleft dan wright, harap lihat itu berfungsi.
@madrury Bisakah Anda juga membandingkan dengan versi XGBoost sebelumnya tanpa fitur kendala?

@tqchen Tentu. Bisakah Anda merekomendasikan hash komit untuk dibandingkan? Haruskah saya menggunakan komit sebelum penambahan batasan monoton Anda?

Ya yang sebelumnya akan dilakukan

@tqchen Saat membangun kembali versi yang diperbarui, saya mendapatkan beberapa kesalahan yang tidak saya alami sebelumnya. Saya berharap alasannya melompat keluar pada Anda dengan jelas.

Jika saya mencoba menjalankan kode yang sama seperti sebelumnya, saya mendapatkan pengecualian, berikut traceback lengkapnya:

XGBoostError                              Traceback (most recent call last)
<ipython-input-14-63a9f6e16c9a> in <module>()
      8    model_with_constraints = xgb.train(params, dtrain, 
      9                                        num_boost_round = 1000, evals = evallist,
---> 10                                    early_stopping_rounds = 10)  

/Users/matthewdrury/anaconda/lib/python2.7/site-packages/xgboost-0.6-py2.7.egg/xgboost/training.pyc in train(params, dtrain, num_boost_round, evals, obj, feval, maximize, early_stopping_rounds, evals_result, verbose_eval, learning_rates, xgb_model, callbacks)
    201                            evals=evals,
    202                            obj=obj, feval=feval,
--> 203                            xgb_model=xgb_model, callbacks=callbacks)
    204 
    205 

/Users/matthewdrury/anaconda/lib/python2.7/site-packages/xgboost-0.6-py2.7.egg/xgboost/training.pyc in _train_internal(params, dtrain, num_boost_round, evals, obj, feval, xgb_model, callbacks)
     72         # Skip the first update if it is a recovery step.
     73         if version % 2 == 0:
---> 74             bst.update(dtrain, i, obj)
     75             bst.save_rabit_checkpoint()
     76             version += 1

/Users/matthewdrury/anaconda/lib/python2.7/site-packages/xgboost-0.6-py2.7.egg/xgboost/core.pyc in update(self, dtrain, iteration, fobj)
    804 
    805         if fobj is None:
--> 806             _check_call(_LIB.XGBoosterUpdateOneIter(self.handle, iteration, dtrain.handle))
    807         else:
    808             pred = self.predict(dtrain)

/Users/matthewdrury/anaconda/lib/python2.7/site-packages/xgboost-0.6-py2.7.egg/xgboost/core.pyc in _check_call(ret)
    125     """
    126     if ret != 0:
--> 127         raise XGBoostError(_LIB.XGBGetLastError())
    128 
    129 

XGBoostError: [14:08:41] src/tree/tree_updater.cc:18: Unknown tree updater grow_monotone_colmaker

Jika saya mengganti semuanya untuk argumen kata kunci yang Anda terapkan, saya juga mendapatkan kesalahan:

TypeError                                 Traceback (most recent call last)
<ipython-input-15-ef7671f72925> in <module>()
      8                                    monotone_constraints="(1)",
      9                                    num_boost_round = 1000, evals = evallist,
---> 10                                    early_stopping_rounds = 10)  

TypeError: train() got an unexpected keyword argument 'monotone_constraints'

hapus argumen pembaru dan pertahankan argumen kendala monoton dalam parameter, sekarang pembaruan kendala monoton diaktifkan secara otomatis saat kendala monoton disajikan

@tqchen Teman saya @amontz membantu saya mengetahuinya segera setelah saya memposting pesan. Saya telah menafsirkan komentar Anda sebagai meneruskan monotone_constraints sebagai kwarg ke .train .

Ia bekerja dengan penyesuaian itu. Terima kasih.

@madrury dapatkah Anda mengkonfirmasi kecepatannya?

Juga @madrury dan @XiaoxiaoWang87 karena fitur ini sekarang hampir digabungkan, alangkah baiknya jika Anda dapat berkoordinasi untuk membuat tutorial yang memperkenalkan fitur ini kepada pengguna.

Kami tidak dapat langsung membawa ipy notebook ke repo utama. tetapi gambar dapat didorong ke https://github.com/dmlc/web-data/tree/master/xgboost dan penurunan harga ke repo utama.

Kita juga perlu mengubah konversi string antarmuka front-end, sehingga tupel int dapat diubah menjadi format tupel string yang dapat diterima oleh backend.

@hetong007 untuk perubahan di R dan @slundberg untuk Julia

@tqchen Julia saat ini terpasang ke XGBoost versi 0,4 jadi lain kali saya perlu menggunakannya dan punya waktu, saya akan memperbarui binding jika tidak ada orang lain yang memilikinya saat itu. Pada saat itu perubahan ini juga bisa ditambahkan.

Berikut perbandingan antara model _tanpa_ kendala monoton dari sebelum implementasi hingga sesudahnya.

Komit 8cac37 : Sebelum penerapan kendala monoton.'
Data Simulasi : 100 loops, best of 3: 232 ms per loop
Data California : 10 loops, best of 3: 5.89 s per loop

Komit b1c224 : Setelah penerapan kendala monoton.
Data Simulasi : 100 loops, best of 3: 231 ms per loop
Data California : 10 loops, best of 3: 5.61 s per loop

Percepatan untuk california setelah implementasi terlihat mencurigakan bagi saya, tetapi saya mencobanya dua kali setiap jalan, dan itu konsisten.

Saya akan senang untuk mencoba menulis tutorial. Saya akan melihat-lihat dokumentasi yang ada dan mengumpulkan sesuatu dalam beberapa hari ke depan.

Ini bagus, PR sekarang resmi bergabung dengan master. Nantikan tutorialnya

Terima kasih @madrury. Nantikan itu. Beri tahu saya apa yang bisa saya bantu. Saya pasti akan bersedia untuk mempelajari lebih lanjut tentang topik ini.

Saya akan meningkatkannya besok. Saya hanya ingin tahu tentang alasan berkomunikasi dengan C++ melalui string alih-alih array.

Saya menguji dari R. Saya secara acak menghasilkan data dua variabel dan mencoba membuat prediksi.

Namun, saya menemukan bahwa

  1. xgboost tidak membatasi prediksi.
  2. parameter monotone_constraints membuat prediksi sedikit berbeda.

Tolong tunjukkan jika saya membuat kesalahan.

Kode untuk mereproduksinya (diuji pada versi github terbaru , bukan dari drat ):

set.seed(1024)
x1 = rnorm(1000, 10)
x2 = rnorm(1000, 10)
y = -1*x1 + rnorm(1000, 0.001) + 3*sin(x2)
train = cbind(x1, x2)

bst = xgboost(data = train, label = y, max_depth = 2,
                   eta = 0.1, nthread = 2, nrounds = 10,
                   monotone_constraints = '(1,-1)')

pred = predict(bst, train)
ind = order(train[,1])
pred.ord = pred[ind]
plot(train[,1], y, main = 'with constraint')
pred.ord = pred[order(train[,1])]
lines(pred.ord)

wc

bst = xgboost(data = train, label = y, max_depth = 2,
                   eta = 0.1, nthread = 2, nrounds = 10)

pred = predict(bst, train)
ind = order(train[,1])
pred.ord = pred[ind]
plot(train[,1], y, main = 'without constraint')
pred.ord = pred[order(train[,1])]
lines(pred.ord)

woc

Pembatasan dilakukan pada urutan parsial. Jadi kendala hanya berlaku jika kita menggerakkan sumbu montone, menjaga sumbu lainnya tetap

@hetong007 Untuk membuat plot saya, saya

  • Membuat larik yang berisi kisi-kisi koordinat-x. Saya ingin memprediksi variabel itu di dan kemudian bergabung ke dalam plot garis. Ini akan menggunakan seq di R.
  • Tetapkan semua variabel lain sama dengan nilai rata-ratanya dalam data pelatihan. Ini akan menjadi sesuatu seperti colmeans di R.

Inilah kode python yang saya gunakan untuk plot yang saya sertakan di atas, seharusnya cukup mudah dikonversi ke kode R yang setara.

def plot_one_feature_effect(model, X, y, idx=1):

    x_scan = np.linspace(0, 1, 100)    
    X_scan = np.empty((100, X.shape[1]))
    X_scan[:, idx] = x_scan

    left_feature_means = np.tile(X[:, :idx].mean(axis=0), (100, 1))
    right_feature_means = np.tile(X[:, (idx+1):].mean(axis=0), (100, 1))
    X_scan[:, :idx] = left_feature_means
    X_scan[:, (idx+1):] = right_feature_means

    X_plot = xgb.DMatrix(X_scan)
    y_plot = model.predict(X_plot, ntree_limit=bst.best_ntree_limit)

    plt.plot(x_scan, y_plot, color = 'black')
    plt.plot(X[:, idx], y, 'o', alpha = 0.25)

Inilah cara saya melakukan plot ketergantungan parsial (untuk model arbitrer):

  • Pindai kisi nilai untuk fitur X.
  • Untuk setiap nilai kisi fitur X:

    • Setel seluruh kolom fitur X (semua baris) ke nilai ini. Fitur lainnya tidak berubah.

    • Buat prediksi untuk semua baris.

    • Ambil rata-rata prediksi.

  • Pasangan yang dihasilkan (nilai fitur X, prediksi rata-rata) memberi Anda ketergantungan sebagian fitur X.

Kode:

def plot_partial_dependency(bst, X, y, f_id):

    X_temp = X.copy()

    x_scan = np.linspace(np.percentile(X_temp[:, f_id], 0.1), np.percentile(X_temp[:, f_id], 99.5), 50)
    y_partial = []

    for point in x_scan:

        X_temp[:, f_id] = point

        dpartial = xgb.DMatrix(X_temp[:, feature_ids])
        y_partial.append(np.average(bst.predict(dpartial)))

    y_partial = np.array(y_partial)

    # Plot partial dependence

    fig, ax = plt.subplots()
    fig.set_size_inches(5, 5)
    plt.subplots_adjust(left = 0.17, right = 0.94, bottom = 0.15, top = 0.9)

    ax.plot(x_scan, y_partial, '-', color = 'black', linewidth = 1)
    ax.plot(X[:, f_id], y, 'o', color = 'blue', alpha = 0.02)

    ax.set_xlim(min(x_scan), max(x_scan))
    ax.set_xlabel('Feature X', fontsize = 10)    
    ax.set_ylabel('Partial Dependence', fontsize = 12)

Terima kasih atas bimbingannya! Saya menyadari bahwa saya membuat kesalahan konyol dalam plot. Inilah tes lain pada data univariat, plotnya tampak baik-baik saja:

set.seed(1024)
x = rnorm(1000, 10)
y = -1*x + rnorm(1000, 0.001) + 3*sin(x)
train = matrix(x, ncol = 1)

bst = xgboost(data = train, label = y, max_depth = 2,
               eta = 0.1, nthread = 2, nrounds = 100,
               monotone_constraints = '(-1)')
pred = predict(bst, train)
ind = order(train[,1])
pred.ord = pred[ind]
plot(train[,1], y, main = 'with constraint', pch=20)
lines(train[ind,1], pred.ord, col=2, lwd = 5)

rplot

bst = xgboost(data = train, label = y, max_depth = 2,
               eta = 0.1, nthread = 2, nrounds = 100)
pred = predict(bst, train)
ind = order(train[,1])
pred.ord = pred[ind]
plot(train[,1], y, main = 'without constraint', pch=20)
lines(train[ind,1], pred.ord, col=2, lwd = 5)

woc

@hetong007 Jadi tujuan dalam antarmuka R adalah untuk memungkinkan pengguna meneruskan array R selain string

monotone_constraints=c(1,-1)

Tolong beri tahu kami ketika Anda PR tutorialnya

@hetong007 Anda juga sangat disambut untuk membuat versi r-blogger

@tqchen Maaf teman-teman, saya sedang dalam perjalanan kerja selama seminggu.

Saya mengirim beberapa permintaan tarik dengan tutorial kendala monoton. Tolong beri tahu saya apa yang Anda pikirkan, saya senang dengan kritik atau kritik apa pun.

Semoga pantas untuk menanyakan ini di sini: apakah ini akan berfungsi jika kita memperbarui menggunakan git clone --recursive https://github.com/dmlc/xgboost ?

Saya bertanya ketika saya melihat tutorial baru tetapi tidak ada yang baru tentang perubahan pada kode itu sendiri. Terima kasih semua!

ya, fitur baru digabungkan sebelum tutorial digabungkan

Halo,

Saya tidak yakin Anda berhasil menerapkan montonisitas global, dari apa yang saya lihat dalam kode Anda, itu lebih sesuai dengan monotonisitas lokal.

Berikut adalah contoh sederhana memecahkan monotonisitas:

`
df <- data.frame(y = c(2,rep(6,100),1,rep(11,100)),
x1= c(rep(1,101),rep(2,101)),x2 = c(1,rep(2,100),1,rep(2,100)))

perpustakaan (xgboost)
set.seed(0)
XGB <- xgboost(data=data.matrix(df[,-1]),label=df[,1],
objektif=" reg:linier ",
bag.fraction=1,nround=100,monotone_constraints=c(1,0),
eta=0.1 )

sans_corr <- data.frame(x1=c(1,2,1,2),x2=c(1,1,2,2))

sans_corr$prediksi <- prediksi(XGB,data.matrix(sans_corr))
`

Semoga pemahaman saya tentang kode Anda dan contoh saya tidak salah

Saat ini fitur ini tidak ada di api Sklearn. Bisakah Anda atau seseorang tolong bantu menambahkannya? Terima kasih!

Dimungkinkan untuk memaksakan monotonisitas umum pada suatu variabel, tanpa menentukan apakah itu harus meningkat atau menurun?

@davidADSP Anda dapat melakukan pemeriksaan korelasi spearman pada prediktor dan target yang diinginkan untuk melihat apakah peningkatan atau penurunan itu tepat.

Fitur ini tampaknya tidak valid ketika 'tree_method':'hist'. @tqchen ada bantuan? Terima kasih semuanya.

Bagaimana cara kerja kendala untuk tujuan multikelas seperti mlogloss? Apakah kendala monotonisitas didukung untuk kerugian multikelas? Jika ya, bagaimana penegakannya. (Adapun setiap kelas ada pohon)

Apakah ada whitepaper tentang Algoritma Monotisitas yang diterapkan di XGBOOST? Apakah Global atau Lokal? Lokal berarti khusus untuk node tertentu tetapi node di bagian lain dari pohon mungkin membuat pelanggaran monotonisitas keseluruhan. Juga adakah yang bisa membantu saya dalam memahami baris L412-417 . Mengapa "w" dibatasi- atas dan bawah. Bagaimana ini membantu untuk mempertahankan Monotonisitas. Baris 457 - Mengapa "pertengahan" digunakan?

Apakah halaman ini membantu?
0 / 5 - 0 peringkat