XGBoost untuk klasifikasi biner: memilih ambang batas yang tepat

8

Saya sedang mengerjakan dataset berlabel biner yang sangat tidak seimbang, di mana jumlah label yang benar hanya 7% dari keseluruhan dataset. Tetapi beberapa kombinasi fitur dapat menghasilkan jumlah yang lebih tinggi dari yang di himpunan bagian.

Misalnya kita memiliki dataset berikut dengan satu fitur (warna):

180 sampel merah - 0

20 sampel merah - 1

300 sampel hijau - 0

100 sampel hijau - 1

Kita dapat membangun pohon keputusan sederhana:

                      (color)

                red /       \ green

 P(1 | red) = 0.1              P(1 | green) = 0.25

P (1) = 0,2 untuk keseluruhan dataset

Jika saya menjalankan XGBoost pada dataset ini, ia dapat memprediksi probabilitas tidak lebih besar dari 0,25. Yang berarti, bahwa jika saya membuat keputusan pada ambang 0,5:

  • 0 - P <0,5
  • 1 - P> = 0,5

Maka saya akan selalu mendapatkan semua sampel berlabel nol . Semoga saya jelas menggambarkan masalahnya.

Sekarang, pada dataset awal saya mendapatkan plot berikut (ambang x-axis):

masukkan deskripsi gambar di sini

Memiliki maksimum f1_score pada ambang = 0,1. Sekarang saya punya dua pertanyaan:

  • haruskah saya menggunakan f1_score untuk dataset struktur seperti itu?
  • apakah selalu masuk akal untuk menggunakan ambang 0,5 untuk memetakan probabilitas label ketika menggunakan XGBoost untuk klasifikasi biner?

Memperbarui. Saya melihat bahwa topik tersebut menarik minat. Di bawah ini adalah kode Python untuk mereproduksi percobaan merah / hijau menggunakan XGBoost. Ini sebenarnya menghasilkan probabilitas yang diharapkan:

from xgboost import XGBClassifier
from sklearn.model_selection import train_test_split
import numpy as np

X0_0 = np.zeros(180) # red - 0
Y0_0 = np.zeros(180)

X0_1 = np.zeros(20) # red - 1
Y0_1 = np.ones(20)

X1_0 = np.ones(300) # green - 0
Y1_0 = np.zeros(300)

X1_1 = np.ones(100) # green  - 1
Y1_1 = np.ones(100)

X = np.concatenate((X0_0, X0_1, X1_0, Y1_1))
Y = np.concatenate((Y0_0, Y0_1, Y1_0, Y1_1))

# reshaping into 2-dim array
X = X.reshape(-1, 1)

import xgboost as xgb

xgb_dmat = xgb.DMatrix(X_train, label=y_train)

param = {'max_depth': 1,
         'eta': 0.01,
         'objective': 'binary:logistic',
         'eval_metric': 'error',
         'nthread': 4}

model = xgb.train(param, xg_mat, 400)

X0_sample = np.array([[0]])
X1_sample = np.array([[1]])

print('P(1 | red), predicted: ' + str(model.predict(xgb.DMatrix(X0_sample))))
print('P(1 | green), predicted: ' + str(model.predict(xgb.DMatrix(X1_sample))))

Keluaran:

P(1 | red), predicted: [ 0.1073855]
P(1 | green), predicted: [ 0.24398108]
Denis Kulagin
sumber

Jawaban:

5

Anda harus memutuskan apa yang ingin Anda maksimalkan.

Mengklasifikasikan dengan membandingkan probabilitas ke 0,5 sesuai jika Anda ingin memaksimalkan akurasi. Tidak tepat jika Anda ingin memaksimalkan metrik f1.

Jika Anda ingin memaksimalkan akurasi, selalu memprediksi nol adalah penggolong optimal.

Atau, diberi nilai probabilitas , opsi lain adalah membalikkan koin bias secara acak; dengan probabilitas , klasifikasi keluaran 1, sebaliknya klasifikasi keluaran 0. Ini tidak selalu memprediksi nol. Namun itu mungkin sebenarnya tidak lebih baik dengan cara apa pun yang bermanfaat.pp

Jika Anda ingin memaksimalkan metrik f1, salah satu pendekatan adalah melatih classifier Anda untuk memprediksi probabilitas, lalu pilih ambang batas yang memaksimalkan skor f1. Ambang batas mungkin tidak akan menjadi 0,5.

Pilihan lain adalah untuk memahami biaya kesalahan tipe I vs kesalahan tipe II, dan kemudian menetapkan bobot kelas yang sesuai.

DW
sumber
1
Saya hanya ingin menyebutkan dua hal lagi: (a) alih-alih skor F1, OP juga dapat menggunakan akurasi tertimbang, atau bahkan memaksimalkan metrik peringkat seperti AUC ROC (b) xgboostmendukung bobot kelas, OP harus bermain dengan mereka jika ia tidak puas dengan metrik apa pun yang ingin dimaksimalkan.
Ricardo Cruz
@ RicardoCruz, terima kasih - saran bagus! (Saya memang menyebutkan bobot kelas secara singkat - paragraf terakhir dari jawaban - tapi saya senang Anda menyorotnya.)
DW
Btw, sebuah class_weights umum digunakan adalah frekuensi terbalik: n_samples / (n_classes * np.bincount(y)). Ini menghindari pengklasifikasi yang memberi bobot lebih ke kelas yang lebih padat.
Ricardo Cruz