Bagging dan boosting menciptakan keragaman lewat cara yang tidak lazim: salinan data yang digeser acak, atau rantai koreksi yang saling menunggu. Keluarga ketiga mengambil jalan yang paling sederhana sekaligus paling fleksibel — latih beberapa model yang sepenuhnya berbeda algoritme, lalu gabungkan hasilnya. Voting menggabungkan lewat suara mayoritas atau rata-rata probabilitas; stacking naik satu tingkat lebih jauh dengan melatih meta-learner yang belajar sendiri cara terbaik memadukan keluaran para anggotanya. Chapter Konsep Ensemble Learning sudah membuktikan syaratnya — anggota harus akurat dan galatnya tidak berkorelasi — dan demonstrasi voter identik di sana menunjukkan apa yang terjadi bila syarat kedua dilanggar. Kini kita jalankan keduanya secara sungguhan pada satu komite tiga model berbeda jenis, lalu membaca hasilnya dengan jujur: kapan komite mengungguli anggota terbaiknya, dan kapan tidak.

Voting: Suara Mayoritas dan Rata-rata Probabilitas #

VotingClassifier menerima daftar pasangan (nama, estimator) lewat estimators — komite bisa berisi model sejenis atau lintas keluarga, dan seluruhnya dilatih sekali jalan pada pemanggilan fit() tunggal. Dua mode penggabungannya: hard voting mengambil label dari mayoritas suara — tiap anggota menyumbang satu label, label dengan suara terbanyak menang. Soft voting menyimpan informasi lebih kaya: tiap anggota menyumbang vektor probabilitas kelasnya (predict_proba), probabilitas dirata-ratakan per kelas, lalu kelas dengan rata-rata tertinggi dipilih. Karena probabilitas 0,51 dan 0,99 sama-sama menjadi “satu suara kelas 1” pada hard voting, soft voting umumnya lebih peka terhadap keyakinan model — pada harga satu syarat: seluruh anggota harus mendukung predict_proba.

Kedua mode menghormati syarat Condorcet dari Chapter Konsep Ensemble Learning: anggota yang akuratnya beragam tetapi selalu di atas tebakan acak tetap menyumbang informasi; anggota yang identik hanya menduplikasi suara tanpa menambah apa pun. Parameter weights memungkinkan pembobotan tak seragam bila satu anggota terbukti jauh lebih kuat — komite juri memberi suara lebih besar kepada pakar senior.

Stacking: Meta-Learner atas Prediksi Anggota #

Stacking menyerahkan keputusan penggabungan kepada model lain. Base estimator dilatih seperti biasa; keluaran masing-masing (probabilitas kelas untuk klasifikasi) menjadi fitur baru bagi satu estimator tambahan — final estimator atau meta-learner — yang dilatih memetakan deretan prediksi anggota menuju label akhir. Bila anggota cenderung salah pada wilayah data yang berbeda, meta-learner berpeluang mempelajari pola tersebut: percayai random forest saat probabilitasnya tinggi, beri bobot lebih pada regresi logistik di wilayah tertentu, dan seterusnya.

Bahaya desainnya nyata: meta-learner yang dilatih pada prediksi in-sample anggota akan meniru anggota yang paling menghafal data latih — kebocoran yang persis dibahas Chapter Data Leakage dan Cara Mencegahnya. scikit-learn menutupnya dengan out-of-fold predictions: data latih dipecah cv lipatan, tiap anggota memprediksi lipatan validasinya dengan model yang dilatih pada lipatan lainnya, dan hanya prediksi “bersih” itulah yang menjadi bahan latih meta-learner. Setelah seluruh anggota dilatih penuh, prediksi data baru mengalir dari anggota-anggota penuh menuju meta-learner.

Mekanisme gabungBahan gabunganBobot anggotaCocok saat
Hard votingLabel tiap anggotaSeragam (atau weights)Anggota seimbang, cepat dibangun
Soft votingProbabilitas tiap anggotaSeragam (atau weights)Probabilitas anggota bermakna
StackingPrediksi out-of-fold tiap anggotaDipelajari meta-learnerAnggota kuat di wilayah berbeda

Praktik Memilih Anggota Komite #

Komite yang baik meniru tim yang baik: anggotanya kompeten dan cara berpikirnya berbeda. Pilih model dari keluarga algoritme yang berbeda profil galat — model linier (bias stabil), ensemble pohon (varians ditekan lewat rata-rata), dan instance-based seperti k-NN; tiga pohon dengan seed berbeda nyaris tidak menambah keragaman karena mempelajari pola yang sama. Seluruh anggota tetap harus akurat di atas tebakan acak, dan untuk soft voting, probabilitasnya selayaknya terkalibrasi — anggota yang overconfident akan menarik rata-rata probabilitas ke arah yang keliru.

Eksperimen: Komite Tiga Model pada Data Kampanye #

Skrip berikut menyusun komite klasifikasi dari tiga model yang sudah kita kenal — regresi logistik, random forest, gradient boosting — pada data kampanye pemasaran 500 sampel dengan 10% label terbalik. Bagian 1 memetakan kekuatan individual dan kesepakatan komite, Bagian 2 membandingkan hard vs soft voting, Bagian 3 membongkar bobot meta-learner stacking, dan Bagian 4 menutup dengan analogi regresi lewat StackingRegressor.

python
# Voting dan Stacking: ensemble heterogen -- model berbeda jenis digabung.
# Bagian 1: tiga model dasar berbeda jenis + ukuran kesepakatannya.
# Bagian 2: VotingClassifier hard (mayoritas) vs soft (rata-rata probabilitas).
# Bagian 3: StackingClassifier cv=3 dengan meta-learner regresi logistik.
# Bagian 4: StackingRegressor cv=3 pada data regresi sintetis.

import numpy as np
from sklearn.datasets import make_classification
from sklearn.ensemble import (GradientBoostingClassifier,
                              RandomForestClassifier, StackingClassifier,
                              StackingRegressor, VotingClassifier)
from sklearn.linear_model import LinearRegression, LogisticRegression, Ridge
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsRegressor
from sklearn.tree import DecisionTreeRegressor

# Data kampanye pemasaran: 500 sampel, 10 fitur, 10% label terbalik.
X, y = make_classification(n_samples=500, n_features=10, n_informative=6,
                           n_redundant=2, flip_y=0.10, class_sep=1.2,
                           n_clusters_per_class=1, random_state=42)
X_latih, X_uji, y_latih, y_uji = train_test_split(
    X, y, test_size=150, stratify=y, random_state=42)

anggota = [
    ("logreg", LogisticRegression(max_iter=2000, random_state=42)),
    ("rf", RandomForestClassifier(n_estimators=200, random_state=42)),
    ("gb", GradientBoostingClassifier(n_estimators=150, learning_rate=0.1,
                                      max_depth=3, random_state=42)),
]

# --- Bagian 1: tiga model dasar + kesepakatan ---------------------------
print("Bagian 1 - tiga model dasar pada data kampanye (150 sampel uji):")
print(f"  {'model':<12} {'latih':>6} {'uji':>6}")
prediksi = []
for nama, model in anggota:
    model.fit(X_latih, y_latih)
    pred = model.predict(X_uji)
    prediksi.append(pred)
    print(f"  {nama:<12} {model.score(X_latih, y_latih):>6.3f} "
          f"{model.score(X_uji, y_uji):>6.3f}")

sepakat = (prediksi[0] == prediksi[1]) & (prediksi[1] == prediksi[2])
n_sepakat = int(sepakat.sum())
akurasi_sepakat = float(np.mean(prediksi[0][sepakat] == y_uji[sepakat]))
akurasi_terbagi = float(np.mean(
    [np.mean(p[~sepakat] == y_uji[~sepakat]) for p in prediksi]))
print(f"  komite bulat sepakat : {n_sepakat}/150 sampel")
print(f"  akurasi saat sepakat : {akurasi_sepakat:.3f}")
print(f"  akurasi saat terbagi : {akurasi_terbagi:.3f} (rata-rata anggota)")

# --- Bagian 2: VotingClassifier hard vs soft ----------------------------
print("\nBagian 2 - VotingClassifier (tiga anggota yang sama):")
print(f"  {'voting':<24} {'latih':>6} {'uji':>6}")
label = {"hard": "hard (mayoritas suara)", "soft": "soft (rata-rata proba)"}
for jenis in ["hard", "soft"]:
    pemilih = VotingClassifier(estimators=anggota, voting=jenis)
    pemilih.fit(X_latih, y_latih)
    print(f"  {label[jenis]:<24} {pemilih.score(X_latih, y_latih):>6.3f} "
          f"{pemilih.score(X_uji, y_uji):>6.3f}")

# --- Bagian 3: StackingClassifier cv=3 ----------------------------------
meta = LogisticRegression(max_iter=2000, random_state=42)
tumpuk = StackingClassifier(estimators=anggota, final_estimator=meta, cv=3)
tumpuk.fit(X_latih, y_latih)
print("\nBagian 3 - StackingClassifier (cv=3, meta=regresi logistik):")
print(f"  akurasi latih : {tumpuk.score(X_latih, y_latih):.3f}")
print(f"  akurasi uji   : {tumpuk.score(X_uji, y_uji):.3f}")
print("  koefisien meta-learner (bobot probabilitas tiap anggota):")
for nama, koef in zip([a[0] for a in anggota],
                      tumpuk.final_estimator_.coef_[0]):
    print(f"    {nama:<12} {koef:6.2f}")

# --- Bagian 4: StackingRegressor cv=3 -----------------------------------
rng = np.random.default_rng(42)
X_reg = rng.uniform(0, 4, size=(200, 2))
y_reg = (np.sin(2 * X_reg[:, 0]) * np.cos(1.5 * X_reg[:, 1])
         + 0.3 * X_reg[:, 1] + rng.normal(0, 0.1, 200))
Xl_reg, Xu_reg, yl_reg, yu_reg = train_test_split(
    X_reg, y_reg, test_size=60, random_state=42)

anggota_reg = [
    ("linear", LinearRegression()),
    ("knn", KNeighborsRegressor(n_neighbors=7)),
    ("pohon", DecisionTreeRegressor(max_depth=4, random_state=42)),
]
tumpuk_reg = StackingRegressor(estimators=anggota_reg,
                               final_estimator=Ridge(alpha=1.0), cv=3)
tumpuk_reg.fit(Xl_reg, yl_reg)

print("\nBagian 4 - StackingRegressor (data regresi sintetis, cv=3):")
print(f"  {'regressor':<12} {'R2 uji':>7}")
for nama, model in anggota_reg:
    model.fit(Xl_reg, yl_reg)
    print(f"  {nama:<12} {model.score(Xu_reg, yu_reg):>7.3f}")
print(f"  {'stacking':<12} {tumpuk_reg.score(Xu_reg, yu_reg):>7.3f}")

Output yang Dihasilkan:

text
Bagian 1 - tiga model dasar pada data kampanye (150 sampel uji):
  model         latih    uji
  logreg        0.883  0.907
  rf            1.000  0.933
  gb            1.000  0.913
  komite bulat sepakat : 139/150 sampel
  akurasi saat sepakat : 0.950
  akurasi saat terbagi : 0.515 (rata-rata anggota)

Bagian 2 - VotingClassifier (tiga anggota yang sama):
  voting                    latih    uji
  hard (mayoritas suara)    1.000  0.920
  soft (rata-rata proba)    0.991  0.927

Bagian 3 - StackingClassifier (cv=3, meta=regresi logistik):
  akurasi latih : 0.971
  akurasi uji   : 0.940
  koefisien meta-learner (bobot probabilitas tiap anggota):
    logreg         2.11
    rf             2.43
    gb             1.30

Bagian 4 - StackingRegressor (data regresi sintetis, cv=3):
  regressor     R2 uji
  linear         0.309
  knn            0.877
  pohon          0.495
  stacking       0.907

Mari kita telaah bagian-bagian kunci skrip tersebut:

  1. Baris 24–29 (komite): daftar anggota memasangkan nama string dengan estimator — kontrak yang sama dipakai VotingClassifier dan StackingClassifier, sehingga komite yang sama bisa diuji lewat tiga mekanisme gabung.
  2. Baris 34–40 (kekuatan individual): tiap anggota dilatih lalu dievaluasi terpisah; prediksinya pada data uji disimpan ke prediksi sebagai bahan analisis kesepakatan.
  3. Baris 42–49 (analisis kesepakatan): sepakat menandai sampel yang ketiga prediksinya sama; akurasi komite dihitung pada subset sepakat, dan rata-rata akurasi anggota dihitung pada subset terbagi — dua angka yang mengukur nilai keragaman secara langsung.
  4. Baris 54–59 (dua mode voting): satu-satunya perbedaan konstruktor adalah voting="hard" atau "soft"; estimators sama persis sehingga perbandingannya bersih.
  5. Baris 62–64 (stacking): final_estimator menerima LogisticRegression, cv=3 mengatur pembentukan prediksi out-of-fold; pemanggilan fit() tunggal mengerjakan seluruh orkestrasi.
  6. Baris 69–71 (bobot meta): final_estimator_.coef_[0] membuka koefisien meta-learner — satu angka per anggota (klasifikasi biner menyumbang satu kolom probabilitas) yang bisa dibaca sebagai bobot kepercayaan.
  7. Baris 74–88 (analogi regresi): tiga regressor berbeda jenis dirakit dengan StackingRegressor dan meta Ridge; mekanisme out-of-fold-nya identik dengan versi klasifikasi.

Membaca Hasil Eksperimen #

Bagian 1 menegaskan dua syarat ensemble secara empiris. Ketiga anggota berada di rentang sehat — 0,907 (logreg), 0,933 (rf), 0,913 (gb) — dan pada 139 dari 150 sampel uji komite bulat sepakat dengan akurasi 0,950. Sebaliknya, pada 11 sampel yang prediksinya terbagi, rata-rata akurasi anggota runtuh ke 0,515 — nyaris lempar koin. Persis seperti prediksi teorema juri: galat yang tidak tumpang tindih bisa dimusyawarahkan, galat bersama tidak. Angka ini juga membawa pesan praktis: sampel yang membuat komite terpecah adalah kandidat kuat untuk ditinjau manual.

Bagian 2 adalah hasil yang jujur dan perlu dibaca tanpa dipoles. Hard voting mencatat 0,920 dan soft voting 0,927 — keduanya di bawah anggota terbaik (rf, 0,933). Pemilu dengan bobot seragam tidak otomatis mengalahkan juara tunggal: suara logreg yang lebih lemah menarik komite menjauh dari keputusan rf pada sebagian sampel terbagi. Soft voting lebih baik dari hard karena keyakinan probabilitas ikut diperhitungkan, tetapi mekanisme rata-rata tetap tidak bisa belajar. Inilah batasan yang dijawab Bagian 3: stacking mencatat 0,940 — mengungguli seluruh anggota — dengan akurasi latih 0,971 (bukan 1,000: prediksi out-of-fold membuat meta-learner tidak bisa menghafal). Koefisiennya membuka cara berpikir meta-learner: rf diberi bobot 2,43, logreg 2,11, gb 1,30 — regresi logistik yang tampak paling lemah secara individual ternyata dinilai hampir setara rf karena membawa perspektif linier yang berbeda.

Bagian 4 mengulang pelajaran yang sama pada regresi: LinearRegression (R² 0,309) dan DecisionTreeRegressor (0,495) tampak tidak layak bersaing dengan k-NN (0,877), tetapi meta Ridge memadukan ketiganya menjadi R² 0,907 — melampaui anggota terbaik. Dua model “lemah” dengan galat yang berbeda arah tetap menyumbang informasi yang tidak dimiliki model terkuat.

Dengan voting dan stacking, peta ensemble lengkap: bagging menekan varians, boosting menekan bias, dan komite heterogen menggabungkan keduanya. Model-model di seluruh Section ini — dari regresi linier hingga CatBoost — kini tersedia sebagai anggota komite. Namun satu pertanyaan menunggu di pintu keluar: semua estimator datang dengan puluhan tombol yang nilainya tidak dipelajari dari data. Berapa kedalaman pohon, berapa learning rate, berapa tetangga k-NN — dan bagaimana cara memilihnya secara sistematis, bukan coba-coba? Pertanyaan itu membuka Section Optimasi dan Hyperparameter, dimulai dari Chapter Parameter vs Hyperparameter.

Referensi Resmi #