AI & Data Science

Random Forest: Ensemble Learning

TOKEN

Tutorial lengkap Random Forest β€” bagging, bootstrap aggregation, feature importance, hyperparameter tuning, dan implementasi dengan scikit-learn

1. Pengenalan Random Forest

Random Forest adalah salah satu algoritma Machine Learning paling populer dan powerful yang masuk dalam kategori Ensemble Learning. Algoritma ini menggabungkan ratusan bahkan ribuan pohon keputusan (Decision Tree) untuk menghasilkan prediksi yang lebih stabil, akurat, dan generalizable dibandingkan satu pohon tunggal.

Ide dasar di balik Random Forest sangat elegan: jika satu pohon keputusan rentan terhadap overfitting, maka gabungan banyak pohon β€” yang masing-masing sedikit berbeda β€” akan menghasilkan prediksi yang jauh lebih robust. Ibaratnya, keputusan kelompok (wisdom of the crowd) lebih baik daripada keputusan satu individu.

Mengapa Random Forest Penting?

Diagram: Popularitas Algoritma ML di Industri (2025)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                  Popularitas Algoritma ML                        β”‚
β”‚                                                                  β”‚
β”‚  Random Forest   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ  92%   β”‚
β”‚  XGBoost         β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ   90%   β”‚
β”‚  Logistic Reg.   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ     88%   β”‚
β”‚  SVM             β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ            75%   β”‚
β”‚  Neural Network  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ      85%   β”‚
β”‚  KNN             β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ                      52%   β”‚
β”‚  Naive Bayes     β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ                        48%   β”‚
β”‚                                                                  β”‚
β”‚  Sumber: Kaggle ML & Data Science Survey 2025                    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Sejarah Singkat

Random Forest diperkenalkan oleh Leo Breiman pada tahun 2001 dalam makalahnya yang berjudul "Random Forests". Breiman menggabungkan dua ide sebelumnya β€” bootstrap aggregation (bagging) yang ia kembangkan sendiri pada tahun 1996 dan random subspace method dari Tin Kam Ho (1998). Kombinasi ini menghasilkan algoritma yang tidak hanya akurat, tetapi juga relatif mudah digunakan dan diinterpretasikan.

Cocok untuk Masalah Apa?

Jenis Masalah Cocok? Contoh
Klasifikasi (Binary)βœ… Sangat cocokSpam vs Bukan Spam, Kredit Disetujui/Tolak
Klasifikasi (Multi-class)βœ… Sangat cocokJenis Iris, Kategori Sentimen
Regresiβœ… CocokPrediksi Harga Rumah, Estimasi Penjualan
Feature Selectionβœ… BagusMenentukan fitur paling berpengaruh
Data Imbalanced⚠️ Perlu penanganan khususDetect fraud (1% dari transaksi)
Time Series⚠️ Bisa, tapi ada algoritma lebih spesifikPrediksi stock price
Data Unstructured (Image/Text)❌ Kurang cocokPengenalan wajah, NLP

2. Review Decision Tree

Sebelum memahami Random Forest, penting untuk memahami Decision Tree (Pohon Keputusan) karena Random Forest dibangun dari banyak decision tree. Decision tree bekerja dengan membagi data secara rekursif berdasarkan fitur dan threshold terbaik.

Bagaimana Decision Tree Bekerja

Diagram: Contoh Decision Tree untuk Kredit
                        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                        β”‚  Pendapatan > 5jt?  β”‚
                        β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
                         Ya /         \ Tidak
                           /           \
              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”    β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
              β”‚ Usia > 25?  β”‚    β”‚ Riwayat Kreditβ”‚
              β””β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”˜    β”‚   Bagus?      β”‚
            Ya /         \ Tidak  β””β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”˜
              /           \     Ya /          \ Tidak
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”  β”Œβ–Όβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  βœ… Setuju β”‚  β”‚ ⚠️ Tinjauβ”‚  β”‚βœ… Setuju β”‚ β”‚βŒ Ditolak  β”‚
    β”‚  (Leaf)   β”‚  β”‚  (Leaf) β”‚  β”‚ (Leaf)  β”‚ β”‚  (Leaf)   β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Cara Decision Tree Memilih Split

Decision tree memilih split terbaik menggunakan metrik impurity (ketidakmurnian). Dua metrik utama yang digunakan:

Metrik Formula Digunakan di Penjelasan
Gini ImpurityGini = 1 - Ξ£(pα΅’Β²)CART (sklearn default)Mengukur probabilitas salah klasifikasi
Entropy (Information Gain)H = -Ξ£(pα΅’ Β· logβ‚‚(pα΅’))ID3, C4.5Mengukur ketidakpastian dalam data

Contoh Perhitungan Gini Impurity

Python β€” Gini Impurity Manual
import numpy as np

def gini_impurity(labels):
    """Hitung Gini Impurity dari sebuah node."""
    _, counts = np.unique(labels, return_counts=True)
    probabilities = counts / counts.sum()
    return 1 - np.sum(probabilities ** 2)

# Contoh: 10 data β€” 7 positif, 3 negatif
labels_node_a = np.array([1,1,1,1,1,1,1,0,0,0])
print(f"Gini Node A (7+, 3-): {gini_impurity(labels_node_a):.4f}")
# Output: Gini Node A (7+, 3-): 0.4200

# Contoh: node murni (semua sama)
labels_pure = np.array([1,1,1,1,1])
print(f"Gini Node Pure (5+, 0-): {gini_impurity(labels_pure):.4f}")
# Output: Gini Node Pure (5+, 0-): 0.0000

# Contoh: node campuran (50-50 β€” paling impure)
labels_mixed = np.array([1,1,1,0,0,0])
print(f"Gini Node Mixed (3+, 3-): {gini_impurity(labels_mixed):.4f}")
# Output: Gini Node Mixed (3+, 3-): 0.5000

Masalah Decision Tree Tunggal

Meskipun Decision Tree mudah dipahami dan divisualisasikan, pohon tunggal memiliki beberapa masalah serius:

⚠️ Analogi Sederhana

Bayangkan kamu bertanya ke 1 orang saja soal investasi β€” jawabannya mungkin bias. Tapi kalau kamu bertanya ke 1000 orang ahli yang masing-masing punya perspektif berbeda, dan mengambil rata-rata jawaban mereka, hasilnya jauh lebih dapat diandalkan. Ini prinsip dasar Random Forest!

3. Bagging (Bootstrap Aggregation)

Bagging (singkatan dari Bootstrap Aggregation) adalah teknik ensemble yang dikembangkan oleh Leo Breiman pada tahun 1996. Bagging adalah fondasi utama dari Random Forest.

Bagaimana Bagging Bekerja

Diagram: Proses Bagging
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Dataset      β”‚
β”‚ Asli (N      β”‚
β”‚ samples)    β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
       β”‚
       β”‚  Bootstrap Sampling (sampling dengan replacement)
       β”‚
  β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β–Ό         β–Ό            β–Ό            β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”
β”‚Boot- β”‚ β”‚Boot- β”‚   β”‚Boot- β”‚    β”‚Boot- β”‚
β”‚strap β”‚ β”‚strap β”‚   β”‚strap β”‚    β”‚strap β”‚
β”‚Sampleβ”‚ β”‚Sampleβ”‚   β”‚Sampleβ”‚    β”‚Sampleβ”‚
β”‚  #1  β”‚ β”‚  #2  β”‚   β”‚  #3  β”‚    β”‚ #B   β”‚
β””β”€β”€β”¬β”€β”€β”€β”˜ β””β”€β”€β”¬β”€β”€β”€β”˜   β””β”€β”€β”¬β”€β”€β”€β”˜    β””β”€β”€β”¬β”€β”€β”€β”˜
   β–Ό        β–Ό          β–Ό           β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”
β”‚Tree  β”‚ β”‚Tree  β”‚   β”‚Tree  β”‚    β”‚Tree  β”‚
β”‚  #1  β”‚ β”‚  #2  β”‚   β”‚  #3  β”‚    β”‚  #B  β”‚
β””β”€β”€β”¬β”€β”€β”€β”˜ β””β”€β”€β”¬β”€β”€β”€β”˜   β””β”€β”€β”¬β”€β”€β”€β”˜    β””β”€β”€β”¬β”€β”€β”€β”˜
   β”‚        β”‚          β”‚           β”‚
   β–Ό        β–Ό          β–Ό           β–Ό
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚  VOTING (Klasifikasi):         β”‚
   β”‚  Kelas mayoritas menang        β”‚
   β”‚                                β”‚
   β”‚  AVERAGING (Regresi):          β”‚
   β”‚  Rata-rata semua prediksi      β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                   β–Ό
            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
            β”‚  FINAL      β”‚
            β”‚  PREDICTION β”‚
            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Apa Itu Bootstrap Sampling?

Bootstrap sampling adalah teknik sampling di mana kita mengambil N sampel dari dataset asli (yang juga berisi N sampel) dengan replacement (dengan pengembalian). Artinya, satu data point bisa muncul lebih dari sekali dalam satu bootstrap sample.

Secara matematis, probabilitas suatu data point tidak terpilih dalam satu bootstrap sample adalah:

P(tidak terpilih) = (1 - 1/N)ⁿ β‰ˆ 1/e β‰ˆ 0.368

Artinya, sekitar 63.2% data akan muncul dalam setiap bootstrap sample, dan sekitar 36.8% data tidak akan terpilih. Data yang tidak terpilih ini disebut Out-of-Bag (OOB) data dan bisa digunakan untuk validasi!

Contoh Bootstrap Sampling

Python β€” Bootstrap Sampling Manual
import numpy as np

# Dataset asli: 8 data points
dataset = np.array([0, 1, 2, 3, 4, 5, 6, 7])
N = len(dataset)
print(f"Dataset asli: {dataset}")
print(f"Jumlah data: {N}")

np.random.seed(42)

# Bootstrap sample: sampling dengan replacement
bootstrap_indices = np.random.choice(N, size=N, replace=True)
bootstrap_sample = dataset[bootstrap_indices]

print(f"\nBootstrap sample: {bootstrap_sample}")
print(f"Indices yang dipilih: {bootstrap_indices}")

# Identifikasi OOB (Out-of-Bag) data
oob_mask = np.ones(N, dtype=bool)
oob_mask[bootstrap_indices] = False
oob_data = dataset[oob_mask]

print(f"\nOOB data (tidak terpilih): {oob_data}")
print(f"Jumlah OOB: {len(oob_data)} ({len(oob_data)/N*100:.1f}%)")
print(f"Data terpilih: {N - len(oob_data)} ({(N-len(oob_data))/N*100:.1f}%)")

# Demo: bandingkan beberapa bootstrap sample
print("\n--- 5 Bootstrap Sample Berbeda ---")
for i in range(5):
    idx = np.random.choice(N, size=N, replace=True)
    sample = dataset[idx]
    unique = np.unique(sample)
    print(f"Sample {i+1}: {sample} β†’ {len(unique)} data unik dari {N}")

Out-of-Bag (OOB) Evaluation

Salah satu keunggulan bagging adalah adanya OOB evaluation. Karena setiap tree hanya dilatih pada bootstrap sample-nya sendiri (~63.2% data), sisa ~36.8% data (OOB) tidak digunakan saat training tree tersebut. Kita bisa menggunakan data OOB sebagai validation set tanpa perlu memisahkan data secara manual!

Python β€” OOB Evaluation dengan Scikit-learn
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import make_classification
from sklearn.metrics import accuracy_score, classification_report
from sklearn.model_selection import train_test_split

# Generate dataset
X, y = make_classification(
    n_samples=1000,
    n_features=20,
    n_informative=15,
    n_redundant=3,
    random_state=42
)

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# Random Forest DENGAN OOB scoring
rf_with_oob = RandomForestClassifier(
    n_estimators=100,
    oob_score=True,           # Aktifkan OOB scoring
    random_state=42,
    n_jobs=-1
)
rf_with_oob.fit(X_train, y_train)

print("=== OOB Evaluation ===")
print(f"OOB Score: {rf_with_oob.oob_score_:.4f}")

# Bandingkan dengan test set
y_pred = rf_with_oob.predict(X_test)
test_accuracy = accuracy_score(y_test, y_pred)
print(f"Test Score: {test_accuracy:.4f}")
print(f"\nSelisih: {abs(rf_with_oob.oob_score_ - test_accuracy):.4f}")
# Selisih biasanya kecil β€” OOB score β‰ˆ cross-validation score!

4. Cara Kerja Random Forest

Random Forest menggabungkan bagging dengan tambahan elemen kunci: random feature selection pada setiap split pohon. Inilah yang membedakannya dari bagging biasa.

Langkah-Langkah Random Forest

  1. Bootstrap Sampling β€” Buat B bootstrap sample dari dataset asli (B = jumlah tree, biasanya 100-1000)
  2. Grow Decision Tree β€” Untuk setiap bootstrap sample, tumbuhkan decision tree penuh (tanpa pruning)
  3. Random Feature Selection β€” Pada setiap node split, pilih subset fitur secara acak (biasanya √p untuk klasifikasi, p/3 untuk regresi, di mana p = jumlah total fitur)
  4. Vote/Average β€” Klasifikasi: mayoritas voting. Regresi: rata-rata prediksi
Diagram: Perbedaan Bagging Biasa vs Random Forest
BAGGING BIASA:                          RANDOM FOREST:
                                        
Bootstrap Sample #1 ──► Tree #1          Bootstrap Sample #1 ──► Tree #1
  (semua fitur tersedia)                   (random subset fitur per split)
                                        
Bootstrap Sample #2 ──► Tree #2          Bootstrap Sample #2 ──► Tree #2
  (semua fitur tersedia)                   (random subset fitur per split)
                                        
Bootstrap Sample #3 ──► Tree #3          Bootstrap Sample #3 ──► Tree #3
  (semua fitur tersedia)                   (random subset fitur per split)
        ...                                     ...
                                        
KELEMAHAN:                              KEUNGGULAN:
Jika ada fitur dominan,                 Setiap tree "melihat" fitur
semua tree akan menggunakan             berbeda β†’ mengurangi korelasi
fitur tersebut β†’ tree korelasi          antar tree β†’ generalisasi lebih baik

Mengapa Random Feature Selection Penting?

Tanpa random feature selection (hanya bagging), jika ada satu fitur yang sangat kuat, hampir semua tree akan memilih fitur tersebut di root node. Akibatnya, tree-tree tersebut akan sangat mirip (korelasi tinggi), dan manfaat ensemble berkurang drastis.

Dengan random feature selection, setiap tree dipaksa "melihat" subset fitur yang berbeda. Ini memaksa tree untuk menemukan pola menggunakan berbagai kombinasi fitur, sehingga tree menjadi lebih beragam (uncorrelated) dan ensemble menjadi lebih kuat.

Parameter Klasifikasi Regresi Penjelasan
max_features (default sklearn)sqrt(n_features)n_features (semua)Jumlah fitur yang dipertimbangkan per split
max_features (recommended)sqrt atau log2n_features/3Rule of thumb dari Breiman
Jumlah tree (n_estimators)100-1000100-1000Lebih banyak tree = lebih stabil (tapi ada batas diminishing return)
Kedalaman pohonTanpa batas (penuh)Tanpa batas (penuh)Biarkan tree tumbuh penuh; kombinasi banyak tree akan mengurangi overfitting

Visualisasi Proses

Python β€” Visualisasi Prediksi Tiap Tree
import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import make_moons
from sklearn.model_selection import train_test_split

# Dataset: two moons (non-linear boundary)
X, y = make_moons(n_samples=500, noise=0.3, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# Random Forest dengan 5 trees saja (untuk visualisasi)
rf_small = RandomForestClassifier(n_estimators=5, random_state=42)
rf_small.fit(X_train, y_train)

fig, axes = plt.subplots(2, 3, figsize=(15, 10))

# Plot 5 individual trees
for i, tree in enumerate(rf_small.estimators_):
    ax = axes[i // 3, i % 3]
    xx, yy = np.meshgrid(
        np.linspace(X[:, 0].min()-0.5, X[:, 0].max()+0.5, 200),
        np.linspace(X[:, 1].min()-0.5, X[:, 1].max()+0.5, 200)
    )
    Z = tree.predict(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    ax.contourf(xx, yy, Z, alpha=0.3, cmap='RdYlBu')
    ax.scatter(X_train[:, 0], X_train[:, 1], c=y_train, cmap='RdYlBu', 
               edgecolors='k', s=20)
    ax.set_title(f'Tree #{i+1} (acc: {tree.score(X_test, y_test):.2f})')

# Plot final ensemble prediction
ax = axes[1, 2]
Z_rf = rf_small.predict(np.c_[xx.ravel(), yy.ravel()])
Z_rf = Z_rf.reshape(xx.shape)
ax.contourf(xx, yy, Z_rf, alpha=0.3, cmap='RdYlBu')
ax.scatter(X_train[:, 0], X_train[:, 1], c=y_train, cmap='RdYlBu',
           edgecolors='k', s=20)
ax.set_title(f'Ensemble (acc: {rf_small.score(X_test, y_test):.2f})')

plt.suptitle('Random Forest: 5 Trees β†’ 1 Ensemble', fontsize=14)
plt.tight_layout()
plt.show()

5. Feature Importance

Salah satu keunggulan terbesar Random Forest adalah kemampuannya memberikan feature importance β€” mengukur seberapa penting setiap fitur dalam membuat prediksi. Ini sangat berharga untuk interpretabilitas dan feature selection.

Jenis Feature Importance

Metode Penjelasan Kelebihan Kekurangan
Mean Decrease Impurity (MDI)Total penurunan Gini/entropy yang dibawa oleh setiap fitur di seluruh treeCepat, dihitung saat trainingBias terhadap fitur kardinalitas tinggi
Mean Decrease Accuracy (MDA)Penurunan akurasi saat fitur di-shuffle secara acak (permutation importance)Lebih unbiasedLebih lambat, butuh validasi set
SHAP ValuesNilai kontribusi setiap fitur berdasarkan game theory (Shapley value)Interpretasi per-instance, shapelyComputational cost tinggi

Contoh Feature Importance dengan Scikit-learn

Python β€” Feature Importance
import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split

# Load dataset breast cancer
data = load_breast_cancer()
X, y = data.data, data.target
feature_names = data.feature_names

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# Train Random Forest
rf = RandomForestClassifier(
    n_estimators=500,
    random_state=42,
    n_jobs=-1
)
rf.fit(X_train, y_train)

print(f"Test Accuracy: {rf.score(X_test, y_test):.4f}")

# === 1. MDI Feature Importance (Gini Importance) ===
importances_mdi = rf.feature_importances_
indices_mdi = np.argsort(importances_mdi)[::-1]

print("\n=== MDI Feature Importance (Top 10) ===")
for i in range(10):
    print(f"  {i+1}. {feature_names[indices_mdi[i]]}: {importances_mdi[indices_mdi[i]]:.4f}")

# Visualisasi
fig, axes = plt.subplots(1, 2, figsize=(16, 6))

# MDI Importance
axes[0].barh(range(10), importances_mdi[indices_mdi[:10]][::-1])
axes[0].set_yticks(range(10))
axes[0].set_yticklabels([feature_names[i] for i in indices_mdi[:10]][::-1])
axes[0].set_title('MDI Feature Importance (Gini)')
axes[0].set_xlabel('Importance')

# === 2. Permutation Importance ===
from sklearn.inspection import permutation_importance

perm_importance = permutation_importance(
    rf, X_test, y_test, n_repeats=10, random_state=42, n_jobs=-1
)
indices_perm = np.argsort(perm_importance.importances_mean)[::-1]

print("\n=== Permutation Feature Importance (Top 10) ===")
for i in range(10):
    print(f"  {i+1}. {feature_names[indices_perm[i]]}: "
          f"{perm_importance.importances_mean[indices_perm[i]]:.4f} "
          f"Β± {perm_importance.importances_std[indices_perm[i]]:.4f}")

axes[1].barh(range(10), perm_importance.importances_mean[indices_perm[:10]][::-1])
axes[1].set_yticks(range(10))
axes[1].set_yticklabels([feature_names[i] for i in indices_perm[:10]][::-1])
axes[1].set_title('Permutation Feature Importance')
axes[1].set_xlabel('Mean Accuracy Decrease')

plt.tight_layout()
plt.show()
πŸ’‘ Catatan Penting tentang Feature Importance
  • MDI importance bisa menyesatkan jika ada fitur dengan banyak kategori (high cardinality), karena fitur tersebut memiliki lebih banyak kesempatan untuk di-split
  • Permutation importance lebih reliable karena tidak bergantung pada struktur pohon
  • Untuk analisis lebih mendalam, gunakan SHAP (SHapley Additive exPlanations) β€” tersedia di library shap

6. Implementasi dengan Scikit-learn

Sekarang kita akan melihat implementasi lengkap Random Forest untuk masalah klasifikasi menggunakan scikit-learn. Kita akan mencakup preprocessing, training, evaluasi, dan visualisasi.

Contoh Lengkap: Klasifikasi Kanker Payudara

Python β€” Random Forest Klasifikasi Lengkap
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import (
    train_test_split, cross_val_score, 
    StratifiedKFold, learning_curve
)
from sklearn.metrics import (
    accuracy_score, precision_score, recall_score,
    f1_score, roc_auc_score, confusion_matrix,
    classification_report, roc_curve
)
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
import warnings
warnings.filterwarnings('ignore')

# =============================================
# 1. LOAD & PERSIAPAN DATA
# =============================================
data = load_breast_cancer()
X = pd.DataFrame(data.data, columns=data.feature_names)
y = pd.Series(data.target, name='target')

print("=" * 60)
print("DATASET BREAST CANCER")
print("=" * 60)
print(f"Jumlah samples: {X.shape[0]}")
print(f"Jumlah fitur: {X.shape[1]}")
print(f"Kelas: {dict(zip(data.target_names, np.bincount(y)))}")
print(f"\n5 baris pertama:")
print(X.head())

# Train-test split
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)
print(f"\nTrain size: {X_train.shape[0]}")
print(f"Test size: {X_test.shape[0]}")

# =============================================
# 2. TRAINING RANDOM FOREST
# =============================================
rf_model = RandomForestClassifier(
    n_estimators=300,        # Jumlah tree
    max_features='sqrt',     # Fitur per split (sqrt untuk klasifikasi)
    max_depth=None,          # Tanpa batas kedalaman
    min_samples_split=2,     # Minimum sample untuk split
    min_samples_leaf=1,      # Minimum sample di leaf
    bootstrap=True,          # Gunakan bootstrap
    oob_score=True,          # Aktifkan OOB scoring
    random_state=42,
    n_jobs=-1,               # Gunakan semua CPU core
    class_weight='balanced'  # Handle class imbalance
)
rf_model.fit(X_train, y_train)

# =============================================
# 3. EVALUASI MODEL
# =============================================
y_pred = rf_model.predict(X_test)
y_prob = rf_model.predict_proba(X_test)[:, 1]

print("\n" + "=" * 60)
print("EVALUASI MODEL")
print("=" * 60)
print(f"OOB Score: {rf_model.oob_score_:.4f}")
print(f"Test Accuracy: {accuracy_score(y_test, y_pred):.4f}")
print(f"Precision: {precision_score(y_test, y_pred):.4f}")
print(f"Recall: {recall_score(y_test, y_pred):.4f}")
print(f"F1-Score: {f1_score(y_test, y_pred):.4f}")
print(f"ROC AUC: {roc_auc_score(y_test, y_prob):.4f}")
print(f"\nClassification Report:")
print(classification_report(y_test, y_pred, target_names=data.target_names))

# =============================================
# 4. CROSS VALIDATION
# =============================================
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
cv_scores = cross_val_score(rf_model, X, y, cv=cv, scoring='accuracy')

print("=" * 60)
print("CROSS VALIDATION (5-Fold)")
print("=" * 60)
print(f"Scores: {cv_scores}")
print(f"Mean: {cv_scores.mean():.4f} Β± {cv_scores.std():.4f}")

# =============================================
# 5. VISUALISASI
# =============================================
fig, axes = plt.subplots(2, 2, figsize=(14, 12))

# 5a. Confusion Matrix
cm = confusion_matrix(y_test, y_pred)
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
            xticklabels=data.target_names,
            yticklabels=data.target_names, ax=axes[0, 0])
axes[0, 0].set_title('Confusion Matrix')
axes[0, 0].set_xlabel('Prediksi')
axes[0, 0].set_ylabel('Aktual')

# 5b. ROC Curve
fpr, tpr, _ = roc_curve(y_test, y_prob)
axes[0, 1].plot(fpr, tpr, 'b-', linewidth=2,
                label=f'RF (AUC = {roc_auc_score(y_test, y_prob):.3f})')
axes[0, 1].plot([0, 1], [0, 1], 'k--', alpha=0.5)
axes[0, 1].set_title('ROC Curve')
axes[0, 1].set_xlabel('False Positive Rate')
axes[0, 1].set_ylabel('True Positive Rate')
axes[0, 1].legend()

# 5c. Feature Importance (Top 15)
importances = rf_model.feature_importances_
indices = np.argsort(importances)[::-1][:15]
axes[1, 0].barh(range(15), importances[indices][::-1])
axes[1, 0].set_yticks(range(15))
axes[1, 0].set_yticklabels([data.feature_names[i] for i in indices][::-1])
axes[1, 0].set_title('Top 15 Feature Importance')

# 5d. Learning Curve
train_sizes, train_scores, val_scores = learning_curve(
    rf_model, X_train, y_train, cv=5,
    train_sizes=np.linspace(0.1, 1.0, 10),
    scoring='accuracy', n_jobs=-1
)
axes[1, 1].plot(train_sizes, train_scores.mean(axis=1), 'o-', label='Training')
axes[1, 1].plot(train_sizes, val_scores.mean(axis=1), 'o-', label='Validation')
axes[1, 1].fill_between(train_sizes,
                         train_scores.mean(axis=1) - train_scores.std(axis=1),
                         train_scores.mean(axis=1) + train_scores.std(axis=1), alpha=0.2)
axes[1, 1].fill_between(train_sizes,
                         val_scores.mean(axis=1) - val_scores.std(axis=1),
                         val_scores.mean(axis=1) + val_scores.std(axis=1), alpha=0.2)
axes[1, 1].set_title('Learning Curve')
axes[1, 1].set_xlabel('Training Set Size')
axes[1, 1].set_ylabel('Accuracy')
axes[1, 1].legend()

plt.suptitle('Random Forest β€” Analisis Hasil', fontsize=14, y=1.02)
plt.tight_layout()
plt.show()

7. Hyperparameter Tuning

Random Forest memiliki banyak hyperparameter yang bisa dioptimasi. Pemahaman yang baik tentang hyperparameter ini akan membantu mendapatkan performa terbaik dari model.

Daftar Hyperparameter Penting

Hyperparameter Default Rekomendasi Pengaruh
n_estimators100100-1000Jumlah tree. Semakin banyak = lebih stabil, lebih lambat. Diminishing return setelah ~300
max_depthNone10-30 atau NoneKedalaman max tree. None = tree tumbuh penuh
max_featuressqrt (klasifikasi)sqrt, log2, 0.3Jumlah fitur per split. Lebih kecil = tree lebih beragam
min_samples_split22-10Minimum sample untuk melakukan split
min_samples_leaf11-5Minimum sample di leaf node
bootstrapTrueTrueGunakan bootstrap sampling atau tidak
class_weightNone'balanced'Bobot kelas untuk data imbalanced

RandomizedSearchCV vs GridSearchCV

Python β€” Hyperparameter Tuning
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import RandomizedSearchCV, GridSearchCV
from scipy.stats import randint, uniform
import numpy as np

# Dataset
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split

X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# =============================================
# 1. RANDOMIZED SEARCH (Lebih cepat, recommended)
# =============================================
param_distributions = {
    'n_estimators': randint(100, 500),
    'max_depth': [None, 10, 15, 20, 30],
    'max_features': ['sqrt', 'log2', 0.2, 0.3, 0.5],
    'min_samples_split': randint(2, 15),
    'min_samples_leaf': randint(1, 10),
    'class_weight': [None, 'balanced', 'balanced_subsample']
}

rf_random = RandomizedSearchCV(
    estimator=RandomForestClassifier(random_state=42),
    param_distributions=param_distributions,
    n_iter=100,           # 100 kombinasi random
    cv=5,
    scoring='f1_weighted',
    random_state=42,
    n_jobs=-1,
    verbose=1
)

rf_random.fit(X_train, y_train)

print("=" * 60)
print("RANDOMIZED SEARCH RESULTS")
print("=" * 60)
print(f"Best Score (CV): {rf_random.best_score_:.4f}")
print(f"Best Parameters:")
for param, value in rf_random.best_params_.items():
    print(f"  {param}: {value}")

best_rf = rf_random.best_estimator_
print(f"Test Accuracy: {best_rf.score(X_test, y_test):.4f}")

# =============================================
# 2. GRID SEARCH (Lebih teliti, lebih lambat)
# =============================================
# Hanya setelahRandomizedSearch menemukan range yang bagus
param_grid = {
    'n_estimators': [200, 300, 400],
    'max_depth': [None, 15, 20, 25],
    'max_features': ['sqrt', 0.3],
    'min_samples_split': [2, 3, 5],
    'min_samples_leaf': [1, 2, 3]
}

rf_grid = GridSearchCV(
    estimator=RandomForestClassifier(random_state=42),
    param_grid=param_grid,
    cv=5,
    scoring='f1_weighted',
    n_jobs=-1,
    verbose=1
)

rf_grid.fit(X_train, y_train)

print("\n" + "=" * 60)
print("GRID SEARCH RESULTS")
print("=" * 60)
print(f"Best Score (CV): {rf_grid.best_score_:.4f}")
print(f"Best Parameters:")
for param, value in rf_grid.best_params_.items():
    print(f"  {param}: {value}")

best_rf_grid = rf_grid.best_estimator_
print(f"Test Accuracy: {best_rf_grid.score(X_test, y_test):.4f}")
πŸ’‘ Tips Hyperparameter Tuning
  • Mulai dengan RandomizedSearchCV untuk eksplorasi luas, lalu GridSearchCV untuk fine-tuning di range yang sudah ketahui
  • n_estimators = 300-500 sudah cukup untuk kebanyakan masalah; lebih banyak memberikan diminishing return
  • Jika data besar, kecilkan max_features untuk mempercepat training tanpa mengorbankan performa banyak
  • Gunakan class_weight='balanced' jika dataset tidak seimbang

8. Random Forest untuk Regresi

Random Forest juga bisa digunakan untuk masalah regresi β€” memprediksi nilai kontinu (angka). Perbedaan utamanya: alih-alih melakukan voting (klasifikasi), Random Forest Regressor mengambil rata-rata prediksi dari semua tree.

Python β€” Random Forest Regressor
import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestRegressor
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score

# Generate dataset regresi
X, y = make_regression(
    n_samples=1000,
    n_features=10,
    n_informative=7,
    noise=15,
    random_state=42
)

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# Random Forest Regressor
rf_reg = RandomForestRegressor(
    n_estimators=300,
    max_depth=20,
    max_features='sqrt',
    min_samples_split=5,
    min_samples_leaf=2,
    oob_score=True,
    random_state=42,
    n_jobs=-1
)
rf_reg.fit(X_train, y_train)

# Prediksi
y_pred = rf_reg.predict(X_test)

# Evaluasi
print("=" * 50)
print("RANDOM FOREST REGRESSOR β€” HASIL")
print("=" * 50)
print(f"OOB Score (RΒ²): {rf_reg.oob_score_:.4f}")
print(f"Test RΒ² Score: {r2_score(y_test, y_pred):.4f}")
print(f"RMSE: {np.sqrt(mean_squared_error(y_test, y_pred)):.4f}")
print(f"MAE: {mean_absolute_error(y_test, y_pred):.4f}")

# Visualisasi: Prediksi vs Aktual
fig, axes = plt.subplots(1, 3, figsize=(18, 5))

# Plot 1: Prediksi vs Aktual
axes[0].scatter(y_test, y_pred, alpha=0.5, s=20)
axes[0].plot([y_test.min(), y_test.max()], 
             [y_test.min(), y_test.max()], 'r--', linewidth=2)
axes[0].set_xlabel('Nilai Aktual')
axes[0].set_ylabel('Nilai Prediksi')
axes[0].set_title(f'Prediksi vs Aktual (RΒ²={r2_score(y_test, y_pred):.3f})')

# Plot 2: Residuals
residuals = y_test - y_pred
axes[1].scatter(y_pred, residuals, alpha=0.5, s=20)
axes[1].axhline(y=0, color='r', linestyle='--', linewidth=2)
axes[1].set_xlabel('Prediksi')
axes[1].set_ylabel('Residual (Aktual - Prediksi)')
axes[1].set_title('Residual Plot')

# Plot 3: Feature Importance
importances = rf_reg.feature_importances_
indices = np.argsort(importances)[::-1]
axes[2].bar(range(10), importances[indices])
axes[2].set_xticks(range(10))
axes[2].set_xticklabels([f'Feature {i}' for i in indices], rotation=45)
axes[2].set_title('Feature Importance')

plt.tight_layout()
plt.show()

# Perbandingan jumlah tree
print("\n--- Pengaruh Jumlah Tree ---")
n_trees_range = [10, 25, 50, 100, 200, 300, 500, 800]
scores = []
for n in n_trees_range:
    rf_temp = RandomForestRegressor(n_estimators=n, random_state=42, n_jobs=-1)
    cv_score = cross_val_score(rf_temp, X_train, y_train, cv=5, 
                                scoring='r2', n_jobs=-1)
    scores.append(cv_score.mean())
    print(f"  n_estimators={n:4d}: CV RΒ² = {cv_score.mean():.4f} "
          f"(Β±{cv_score.std():.4f})")

plt.figure(figsize=(10, 5))
plt.plot(n_trees_range, scores, 'bo-', linewidth=2)
plt.xlabel('Jumlah Trees')
plt.ylabel('CV RΒ² Score')
plt.title('Pengaruh Jumlah Trees terhadap Performa')
plt.grid(True, alpha=0.3)
plt.show()

9. Kelebihan & Kekurangan

Kelebihan Random Forest

Kelebihan Penjelasan
βœ… Tahan overfittingEnsemble banyak tree mengurangi variance secara signifikan
βœ… Sedikit preprocessingTidak perlu normalisasi/scaling, tahan terhadap outlier
βœ… Handle missing valuesBisa menangani missing values secara internal
βœ… Feature importanceSecara built-in memberikan ranking fitur paling penting
βœ… OOB evaluationValidasi internal tanpa perlu split data terpisah
βœ… ParallelizableTraining tiap tree independen β†’ bisa parallel (n_jobs=-1)
βœ… Works out-of-the-boxHyperparameter default sudah cukup bagus untuk banyak masalah
βœ… Mixed data typesBisa menangani fitur numerik dan kategorikal sekaligus

Kekurangan Random Forest

Kekurangan Penjelasan
❌ Lambat untuk inferencePrediksi harus melewati semua tree (bisa lambat untuk real-time)
❌ Memory intensiveMenyimpan banyak tree membutuhkan memori besar
❌ Black box relatifMeskipun ada feature importance, sulit menjelaskan prediksi individual
❌ Extrapolasi burukTidak bisa memprediksi nilai di luar range data training
❌ Tidak ideal untuk data sekuensialTidak mempertahankan urutan data (time series, NLP)
❌ Imbalanced dataBisa bias ke kelas mayoritas (perlu class_weight atau SMOTE)

Random Forest vs Algoritma Lain

Aspek Random Forest Gradient Boosting SVM Neural Network
Kecepatan Training⚑ Cepat (parallel)🐒 Lambat (sequential)🐒 Lambat (large data)🐌 Sangat lambat (GPU needed)
Kecepatan Inference🐒 Lambat (banyak tree)⚑ Cepat (fewer trees)🐒 Lambat (support vectors)⚑ Cepat (once trained)
Akurasi⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
Mudah Digunakan⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
Interpretability⭐⭐⭐⭐⭐⭐⭐⭐
Overfitting RiskRendahSedang (perlu tuning)SedangTinggi
πŸ“– Kapan Harus Pakai Random Forest?
  • Pertama coba RF β€” Random Forest sering menjadi baseline pertama yang bagus untuk tabular data
  • Tidak ada waktu untuk preprocessing β€” RF relatif robust terhadap outlier dan tidak perlu scaling
  • Perlu feature importance β€” RF memberikan ranking fitur secara built-in
  • Data kecil-menengah β€” RF sangat cocok untuk dataset dengan ribuan sampai ratusan ribu baris
  • Kecepatan training penting β€” RF bisa diparalelkan, training relatif cepat

10. Quiz: Uji Pemahamanmu!

Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang Random Forest:

Pertanyaan 1: Apa yang dimaksud dengan "bagging" dalam Random Forest?

a) Menggabungkan beberapa model secara berurutan untuk memperbaiki error
b) Menggunakan bootstrap sampling dan menggabungkan prediksi banyak tree
c) Mengurangi jumlah fitur secara agresif sebelum training
d) Menambahkan regularization pada setiap node tree

Pertanyaan 2: Dalam bootstrap sampling, berapa persentase data yang TIDAK terpilih (Out-of-Bag) secara rata-rata?

a) ~50%
b) ~36.8%
c) ~10%
d) ~63.2%

Pertanyaan 3: Mengapa Random Forest menggunakan random feature selection pada setiap split?

a) Untuk mempercepat proses training
b) Untuk mengurangi korelasi antar tree dan meningkatkan generalisasi
c) Agar tree lebih dalam
d) Agar model bisa menangani data kategorikal

Pertanyaan 4: Hyperparameter mana yang mengontrol jumlah fitur yang dipertimbangkan pada setiap split?

a) n_estimators
b) max_depth
c) max_features
d) min_samples_leaf

Pertanyaan 5: Kekurangan utama Random Forest untuk masalah regression adalah...

a) Tidak bisa menangani data numerik
b) Tidak bisa memprediksi nilai di luar range data training (extrapolation)
c) Selalu menghasilkan overfitting
d) Membutuhkan GPU untuk training
πŸ” Zoom
100%
🎨 Tema