Chapter XGBoost ditutup dengan satu kemacetan: meskipun pemindaian fiturnya sudah diparalelkan, kandidat pemecahan masih dicari pada nilai fitur yang presisi — ribuan nilai unik yang harus dipindai satu per satu pada tiap simpul. LightGBM menyerang kemacetan itu dengan tiga keputusan desain: membulatkan nilai fitur ke dalam bin histogram sebelum pelatihan dimulai, menumbuhkan pohon per daun alih-alih per tingkat, lalu memangkas jumlah baris dan fitur yang benar-benar diproses lewat GOSS dan EFB. Mekanisme boosting-nya sendiri tidak berubah — residual tetap dikoreksi berantai seperti Chapter Gradient Boosting. Chapter ini membedah ketiga perbedaan desain tersebut, lalu mengujinya lewat LGBMClassifier dengan antarmuka scikit-learn yang sudah kita kuasai.
Histogram: Kandidat Pemecahan dari Bin, Bukan Nilai Presisi #
Mencari pemecahan terbaik untuk satu fitur pada cara biasa menuntut pengurutan seluruh nilai fitur, lalu menguji setiap nilai unik sebagai batas pemecahan — pada 478 nilai unik berarti 477 kandidat yang harus dievaluasi, dan pengurutan itu diulang pada tiap simpul pohon. LightGBM mengganti seluruh kerja tersebut dengan langkah persiapan sekali jalan: sebelum pelatihan dimulai, tiap fitur kontinu dipetakan ke bin sebanyak max_bin (bawaan 255) dengan tepi bin yang dipilih dari kuantil — rapat di wilayah padat data, longgar di wilayah sepi. Sejak saat itu kandidat pemecahan hanya berupa tepi bin: 254 kandidat, berapa pun jumlah barisnya.
Keuntungannya berlapis. Pemetaan nilai-ke-bin berbiaya linier O(n) sekali di awal, menggantikan pengurutan O(n log n) per simpul; indeks bin cukup disimpan sebagai bilangan bulat kecil sehingga jejak memori turun drastis dibanding menyimpan nilai float penuh; dan paling penting, histogram anak dapat dihitung dengan mengurangkan histogram satu saudara dari histogram induknya — hanya satu cabang yang perlu dipindai penuh. Presisi yang hilang akibat pembulatan ke bin nyaris tidak terasa: batas pemecahan optimal yang berpindah beberapa inci di dalam satu bin hampir tidak pernah mengubah kualitas pohon.
Pertumbuhan Leaf-wise, Bukan Level-wise #
Perbedaan kedua ada pada cara pohon berkembang. Gradient boosting scikit-learn dan XGBoost tumbuh level-wise: seluruh daun pada kedalaman yang sama dipecah bersamaan, pohon membesar rapi lapis demi lapis. LightGBM memilih leaf-wise (best-first): dari semua daun yang ada, hanya daun dengan penurunan loss terbesar yang dipecah — lalu pada langkah berikutnya daun paling menjanjikan dipilih lagi, dan seterusnya. Hasilnya pohon yang asimetris: cabang yang “panas” (banyak galat tersisa) tumbuh dalam, cabang yang tenang dibiarkan dangkal.
Konsekuensinya dua sisi. Pada data besar, leaf-wise mencapai loss lebih rendah dengan jumlah daun yang sama — kapasitas dipakai tepat di tempat galat masih menumpuk. Namun pada dataset kecil, pertumbuhan selektif ini bisa terus menggali daun yang menjanjikan hingga menghafal derau; karena itu LightGBM menaruh kendali kapasitas utama di num_leaves (bawaan 31), didampingi min_child_samples (bawaan 20, jumlah baris minimum agar sebuah daun boleh dipecah) dan max_depth yang bawaannya tak terbatas (-1).
GOSS dan EFB untuk Data Besar #
Dua teknik penyampelan milik LightGBM menangani sisi lain biaya. GOSS (Gradient-based One-Side Sampling) mengamati bahwa baris dengan gradien besar — baris yang masih salah dan paling informatif bagi ronde boosting berikutnya — layak dipertahankan semua, sementara baris bergradien kecil cukup diwakili sampel acak yang diberi bobot pengali agar distribusinya tetap setara. Pelatihan tiap pohon hanya berjalan pada sebagian baris. EFB (Exclusive Feature Bundling) bekerja di arah fitur: pada data sparse, banyak fitur jarang bernilai tak nol bersamaan (misalnya fitur one-hot dari kategori berbeda), sehingga beberapa fitur semacam itu dapat digabung ke satu histogram tunggal tanpa benturan nilai — jumlah fitur efektif yang dipindai menyusut.
| Aspek | GradientBoostingClassifier | XGBClassifier | LGBMClassifier |
|---|---|---|---|
| Pola pertumbuhan pohon | Level-wise | Level-wise | Leaf-wise (daun paling menjanjikan) |
| Kandidat pemecahan | Nilai presisi per simpul | Bergantung tree_method (hist tersedia) | Histogram bin bawaan |
| Reduksi baris/fitur | Tidak ada | Tidak ada | GOSS (baris) dan EFB (fitur) |
| Fitur kategorikal | Perlu encoding di hulu | Terbatas (enable_categorical) | categorical_feature pada data bertipe kategori |
Tiga dari empat baris tabel tersebut adalah kecepatan; baris terakhir mengantar ke pembahasan Chapter CatBoost — pustaka yang menempatkan fitur kategorikal sebagai warga kelas satu.
Antarmuka scikit-learn dan Parameternya #
LGBMClassifier dan LGBMRegressor mengikuti kontrak fit()/predict() scikit-learn, sehingga keduanya bisa dimasukkan ke Pipeline dan GridSearchCV seperti estimator lain. Tiga kebiasaan lingkungan kerja kita: selalu tulis random_state=42 dan n_jobs=1 untuk reproduksibilitas, serta verbose=-1 untuk mematikan seluruh log pelatihan — tanpa nilai ini LightGBM menyapa konsol pada setiap fit(). Parameter yang paling sering disentuh: n_estimators dan learning_rate (maknanya sama persis dengan Chapter Gradient Boosting), lalu num_leaves dan min_child_samples sebagai kendali kapasitas khas LightGBM.
Eksperimen: Bin, Daun, dan GOSS dalam Satu Skrip #
Skrip berikut berlatar data kampanye pemasaran yang sama dari Chapter XGBoost: 600 sampel, 12 fitur, 6% label terbalik, dengan 180 sampel uji yang disisihkan secara tetap. Tiga bagian skrip menguji tiga klaim chapter ini satu per satu.
# LightGBM: gradient boosting yang dirancang untuk data besar --
# histogram binning, pertumbuhan leaf-wise, dan GOSS.
# Bagian 1: demonstrasi binning histogram pada satu fitur kontinu.
# Bagian 2: sweep num_leaves (kapasitas pertumbuhan leaf-wise).
# Bagian 3: GBDT vs GOSS vs gradient boosting level-wise scikit-learn.
import numpy as np
from lightgbm import LGBMClassifier
from sklearn.datasets import make_classification
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import train_test_split
# --- Bagian 1: histogram binning pada satu fitur kontinu ---------------
# Fitur sintetis "durasi kunjungan halaman produk" (detik):
# mayoritas pengunjung singkat, sebagian kecil menetap lama.
rng = np.random.default_rng(42)
durasi = np.concatenate([
rng.exponential(40, 480),
rng.normal(240, 60, 120),
]).round(1)
n_bin = 32
tepian = np.unique(np.quantile(durasi, np.linspace(0, 1, n_bin + 1)))
indeks_bin = np.searchsorted(tepian[1:-1], durasi, side="right")
isi_bin = np.bincount(indeks_bin, minlength=len(tepian) - 1)
print("Bagian 1 - histogram binning (fitur durasi kunjungan, 600 nilai):")
print(f" nilai unik : {len(np.unique(durasi))}")
print(f" kandidat pemecahan exact scan : {len(np.unique(durasi)) - 1}")
print(f" jumlah bin kuantil : {len(tepian) - 1}")
print(f" kandidat pemecahan histogram : {len(tepian) - 2}")
print(f" tepi bin pertama (detik) : {np.round(tepian[:6], 1).tolist()}")
print(f" tepi bin terakhir (detik) : {np.round(tepian[-3:], 1).tolist()}")
print(f" isi 5 bin pertama : {isi_bin[:5].tolist()}")
# --- Bagian 2: sweep num_leaves ----------------------------------------
# Data kampanye pemasaran: 600 sampel, 12 fitur, 6 informatif,
# 6% label terbalik agar generalisasi benar-benar teruji.
X, y = make_classification(n_samples=600, n_features=12, n_informative=6,
n_redundant=3, flip_y=0.06, class_sep=0.9,
random_state=42)
X_latih, X_uji, y_latih, y_uji = train_test_split(X, y, test_size=180,
random_state=42)
print("\nBagian 2 - sweep num_leaves (200 pohon, lr=0.1):")
print(f" {'num_leaves':<12} {'latih':>6} {'uji':>6}")
for daun in [7, 15, 31, 63]:
model = LGBMClassifier(n_estimators=200, learning_rate=0.1,
num_leaves=daun, random_state=42,
verbose=-1, n_jobs=1)
model.fit(X_latih, y_latih)
print(f" {daun:<12} {model.score(X_latih, y_latih):>6.3f} "
f"{model.score(X_uji, y_uji):>6.3f}")
# --- Bagian 3: GBDT vs GOSS vs level-wise ------------------------------
print("\nBagian 3 - GBDT vs GOSS vs level-wise (data dan split sama):")
print(f" {'model':<32} {'latih':>6} {'uji':>6}")
kandidat = [
("LightGBM gbdt (31 daun)", dict(boosting_type="gbdt")),
("LightGBM goss (31 daun)", dict(boosting_type="goss")),
]
for nama, params in kandidat:
model = LGBMClassifier(n_estimators=200, learning_rate=0.1,
num_leaves=31, random_state=42,
verbose=-1, n_jobs=1, **params)
model.fit(X_latih, y_latih)
print(f" {nama:<32} {model.score(X_latih, y_latih):>6.3f} "
f"{model.score(X_uji, y_uji):>6.3f}")
level_wise = GradientBoostingClassifier(n_estimators=200, learning_rate=0.1,
max_depth=3, random_state=42)
level_wise.fit(X_latih, y_latih)
print(f" {'sklearn level-wise (depth=3)':<32} "
f"{level_wise.score(X_latih, y_latih):>6.3f} "
f"{level_wise.score(X_uji, y_uji):>6.3f}")Output yang Dihasilkan:
Bagian 1 - histogram binning (fitur durasi kunjungan, 600 nilai):
nilai unik : 478
kandidat pemecahan exact scan : 477
jumlah bin kuantil : 32
kandidat pemecahan histogram : 31
tepi bin pertama (detik) : [0.3, 2.2, 3.4, 5.1, 7.2, 8.9]
tepi bin terakhir (detik) : [264.6, 293.9, 395.9]
isi 5 bin pertama : [19, 18, 19, 19, 19]
Bagian 2 - sweep num_leaves (200 pohon, lr=0.1):
num_leaves latih uji
7 1.000 0.839
15 1.000 0.867
31 1.000 0.850
63 1.000 0.850
Bagian 3 - GBDT vs GOSS vs level-wise (data dan split sama):
model latih uji
LightGBM gbdt (31 daun) 1.000 0.850
LightGBM goss (31 daun) 0.990 0.822
sklearn level-wise (depth=3) 1.000 0.817Mari kita telaah bagian-bagian kunci skrip tersebut:
- Baris 16–20 (fitur durasi sintetis):
rngberseed 42 membangkitkan 600 nilai durasi kunjungan dari dua populasi — mayoritas singkat (eksponensial) dan minoritas yang menetap lama (normal) — lalu dibulatkan satu desimal agar jumlah nilai uniknya realistis. - Baris 22–25 (binning kuantil):
np.quantilemenempatkan tepi bin merata menurut kuantil,np.searchsortedmemetakan tiap nilai ke indeks binnya, dannp.bincountmenghitung isi tiap bin — replika tiga baris dari apa yang LightGBM kerjakan pada tiap fitur sebelum pelatihan. - Baris 39–43 (dataset dan split tetap):
make_classification(random_state=42)denganflip_y=0.06dantrain_test_split(..., random_state=42)memastikan angka pada tabel dapat direproduksi persis. - Baris 47–53 (sweep
num_leaves): empat model identik kecuali jumlah daun dilatih dan dievaluasi pada latih dan uji — pola sweep yang juga dipakai saat tuning sungguhan. - Baris 58–68 (GOSS vs GBDT): kedua model hanya berbeda
boosting_type; sisanya, kontrak konstruktor sama persis. - Baris 70–75 (pembanding level-wise):
GradientBoostingClassifierdengan kedalaman 3 menjadi perwakilan pertumbuhan level-wise pada data dan split yang sama.
Membaca Hasil Eksperimen #
Bagian 1 membuktikan klaim histogram dengan angka: 478 nilai unik berarti 477 kandidat pemecahan pada exact scan, sementara 32 bin kuantil hanya menyisakan 31 kandidat — pembulatan ke bin memangkas ruang pencarian hingga tersisa seperlima belasnya. Perhatikan juga bentuk tepinya: bin pertama selebar 0,3–2,2 detik karena pengunjung singkat menumpuk di sana, sedangkan tiga bin terakhir membentang 264,6 sampai 395,9 detik — binning kuantil otomatis menarik resolusi ke wilayah yang padat data, dan isi bin pun nyaris rata (18–19 baris per bin).
Bagian 2 adalah pelajaran kapasitas leaf-wise. Akurasi latih sudah 1,000 sejak 7 daun — 200 ronde boosting dengan koreksi berantai cukup untuk menghafal 420 baris data latih berapa pun bentuk pohonnya. Pembeda sesungguhnya ada di kolom uji: 0,839 pada 7 daun, memuncak 0,867 pada 15 daun, lalu turun dan mendatar di 0,850 untuk 31 dan 63 daun. Menambah daun setelah titik puncak hanya menambah kedalaman cabang tanpa menambah daya generalisasi — pada data 600 sampel, num_leaves moderat adalah pilihan yang tepat, dan angka 31 (bawaan) bukan hukum alam.
Bagian 3 menempatkan GOSS pada konteksnya. GBDT biasa mencatat 1,000/0,850, sedangkan GOSS 0,990/0,822 — penyampelan sepihak bergradien memang membuang sebagian baris dari tiap pohon, dan pada dataset kecil biaya tersebut terlihat. GOSS dirancang untuk data jutaan baris, tempat penghematan pemrosesan jauh melampaui sedikit informasi yang dilepaskan. Pembanding level-wise scikit-learn menutup dengan 0,817 — ketiganya bermain di kisaran yang sama, menegaskan bahwa pada data berukuran ini pembeda utamanya adalah konfigurasi, bukan merek pustaka.
LightGBM mempercepat gradient boosting pada data numerik besar, dan fitur kategorikal pun bisa dilayani lewat categorical_feature. Namun satu keluarga masalah masih menuntut perlakuan berbeda: fitur kategorikal bertingkat tinggi di atas dataset yang tidak terlalu besar, tempat target encoding naif mudah bocor ke label. CatBoost menjawab dengan ordered boosting dan pemrosesan kategorikal bawaan — Chapter CatBoost melanjutkan perjalanan ini.
Referensi Resmi #
- LightGBM Python Package Intro — panduan resmi antarmuka Python, termasuk pemakaian pandas dan fitur kategorikal.
- LightGBM Parameters — daftar lengkap parameter inti beserta nilai bawaannya.
- LightGBM Parameters Tuning
— panduan resmi menyetel
num_leaves,min_child_samples, danmax_bin.