Bagaimana Model "Belajar"
Penjelasan rinci proses learning ketiga algoritma regresi — Multiple Linear Regression, Random Forest, dan XGBoost — dengan matematika nyata dan contoh hitung memakai data invoice PT Kreasi Pandawa Sakti.
01Apa arti "belajar" di sini
“Belajar” berarti mencari sebuah fungsi f yang memetakan fitur invoice (klien, termin, nilai, …) ke perkiraan lama bayar, sedemikian rupa sehingga kesalahannya sekecil mungkin pada data historis. Kesalahan diukur dengan fungsi kerugian (loss). Untuk regresi kita pakai squared error:
dengan y_i = lama bayar aktual invoice ke-i, \hat{y}_i = prediksi model, dan \mathbf{x}_i = vektor fiturnya. Setiap algoritma punya cara berbeda untuk meminimalkan \mathcal{L} — itulah "proses learning" yang dibahas di bawah.
02Data contoh (6 invoice)
Agar bisa dihitung tangan, kita pakai 6 invoice nyata-mirip. Untuk kejelasan, satu fitur
numerik dipakai sebagai contoh split: top_days (termin). Target = lama bayar aktual.
| Invoice | Klien | top_days | Lama bayar aktual y (hari) |
|---|---|---|---|
| INV-A | Sayap Mas | 14 | 12 |
| INV-B | Sayap Mas | 14 | 18 |
| INV-C | Ichi Tan | 30 | 27 |
| INV-D | Ichi Tan | 30 | 35 |
| INV-E | Energizer | 45 | 50 |
| INV-F | Energizer | 45 | 42 |
Rata-rata target: \bar{y} = (12+18+27+35+50+42)/6 = 30{,}67 hari.
03Pondasi: bagaimana satu pohon regresi belajar
Random Forest dan XGBoost sama-sama dibangun dari pohon keputusan, jadi kita mulai dari sini. Sebuah pohon "belajar" dengan mencari titik pemisah (split) yang paling mengurangi kesalahan. Untuk regresi, ukurannya adalah jumlah kuadrat galat (SSE) dalam tiap simpul; prediksi sebuah daun = rata-rata target anggotanya.
Pohon mencoba semua kandidat ambang (mis. top_days < 22,
top_days < 37,5) dan memilih yang memberi reduksi SSE terbesar, lalu mengulang
di tiap cabang sampai kedalaman tertentu. Itulah inti "belajar" satu pohon.
04AMultiple Linear Regression — least squares
MLR menganggap target adalah kombinasi linear berbobot dari fitur:
Proses learning = mencari bobot \boldsymbol{\beta} yang meminimalkan total galat kuadrat. Solusinya bisa langsung (closed-form, normal equation):
atau iteratif via gradient descent — bergerak melawan arah gradien loss:
Kelebihan
Tiap fitur punya bobot yang bisa dibaca; cepat; jadi pembanding dasar.Kelemahan
Hanya menangkap hubungan lurus. Pola "klien × termin" pada data kita tidak linear.05BRandom Forest — belajar lewat bagging
Random Forest melatih banyak pohon (300 pada proyek ini) yang masing-masing melihat data berbeda, lalu merata-ratakan hasilnya:
- Bootstrap: tiap pohon dilatih pada sampel acak (dengan pengembalian) dari data.
- Feature subsampling: tiap split hanya mempertimbangkan sebagian fitur acak
(
max_features="sqrt") → pohon jadi beragam. - Averaging: rata-rata banyak pohon meredam kesalahan acak (menurunkan variance) tanpa menambah bias berarti.
flowchart LR D[Data latih
356 invoice] --> B1[Sampel bootstrap 1] --> T1[Pohon 1] D --> B2[Sampel bootstrap 2] --> T2[Pohon 2] D --> Bn[Sampel bootstrap 300] --> Tn[Pohon 300] T1 --> AVG[Rata-rata prediksi] T2 --> AVG Tn --> AVG AVG --> Y[Estimasi lama bayar]
Gambar 1 — Bagging: pohon-pohon sejajar dilatih independen, hasilnya dirata-rata.
06CXGBoost — matematika proses belajarnya
XGBoost membangun model secara aditif & bertahap (gradient boosting): mulai dari tebakan awal, lalu tiap pohon baru menambahkan koreksi.
6.1 Objektif: loss + regularisasi
Yang diminimalkan bukan hanya loss, tapi juga kompleksitas pohon (agar tidak overfitting):
dengan T = jumlah daun, w_j = nilai daun ke-j, \gamma = penalti per daun (pemangkasan), \lambda = regularisasi L2 (di model kita \lambda=1).
6.2 Pendekatan Taylor orde-2
Loss diuraikan dengan deret Taylor sampai orde dua di sekitar prediksi sebelumnya, memakai gradien g_i dan hessian h_i:
Untuk squared error \mathcal{L}=\tfrac12(y-\hat{y})^2, keduanya sederhana:
di mana r_i = y_i-\hat{y}_i^{(t-1)} adalah residual (sisa kesalahan). Jadi pohon berikutnya pada dasarnya belajar memprediksi residual.
6.3 Nilai daun optimal
Untuk sebuah daun berisi himpunan instance I_j, dengan G_j=\sum_{i\in I_j} g_i dan H_j=\sum_{i\in I_j} h_i, nilai daun yang meminimalkan objektif adalah:
(ruas kanan memakai g_i=-r_i,\ h_i=1, jadi H_j=N_j = jumlah anggota daun).
6.4 Skor kemiripan & Gain sebuah split
Mutu sebuah simpul diukur dengan structure / similarity score, dan sebuah split dinilai dari Gain-nya:
Split dengan Gain tertinggi dipilih. Jika Gain < 0 (lebih kecil dari penalti \gamma), cabang dipangkas — inilah regularisasi struktural XGBoost.
6.5 Learning rate (shrinkage)
Tiap pohon baru tidak ditambahkan penuh, melainkan diperkecil oleh \eta (di model kita \eta=0{,}05) agar langkah belajar kecil & stabil — mengurangi risiko overfitting, dengan kompensasi memakai lebih banyak pohon (400).
07Contoh hitung langkah demi langkah (XGBoost)
Memakai 6 invoice di bagian 02, \lambda=1, \gamma=0, dan \eta=0{,}3 (sengaja besar agar perubahan terlihat).
Ronde 0 — tebakan awal
Prediksi awal = rata-rata target: \hat{y}^{(0)}=30{,}67. Residual awal r_i=y_i-30{,}67:
| Invoice | A | B | C | D | E | F |
|---|---|---|---|---|---|---|
| r_i | −18,67 | −12,67 | −3,67 | +4,33 | +19,33 | +11,33 |
Ronde 1 — cari split terbaik
Skor akar: \text{Sim}_{root}=(\sum r)^2/(6+1)=0 (residual dari rata-rata
berjumlah nol). Bandingkan dua kandidat split pada top_days:
| Split | Kiri (anggota) | \text{Sim}_L | Kanan | \text{Sim}_R | Gain |
|---|---|---|---|---|---|
top_days < 22 | A,B | 327,26 | C,D,E,F | 196,36 | 523,62 |
top_days < 37,5 | A,B,C,D | 188,09 | E,F | 313,48 | 501,57 |
Split top_days < 22 menang (Gain 523,62). Contoh perhitungan
\text{Sim}_L untuk daun {A,B}:
flowchart TB
R{"top_days < 22 ?"}
R -- "ya (A,B)" --> L["Daun kiri
w* = Σr/(N+λ) = −31,33/3 = −10,44
×η(0,3) = −3,13"]
R -- "tidak (C,D,E,F)" --> Rn["Daun kanan
w* = 31,33/5 = 6,27
×η(0,3) = +1,88"]
Gambar 2 — Pohon ronde 1: satu split, dua daun. Nilai daun = w^{*}=\sum r/(N+\lambda), lalu diperkecil oleh \eta.
Perbarui prediksi & lihat residual mengecil
\hat{y}^{(1)}=\hat{y}^{(0)}+\eta\,w^{*}:
| Invoice | aktual y | \hat{y}^{(0)} | daun ×η | \hat{y}^{(1)} | residual baru | |sebelum| → |sesudah| |
|---|---|---|---|---|---|---|
| A | 12 | 30,67 | −3,13 | 27,53 | −15,53 | 18,67 → 15,53 ✓ |
| B | 18 | 30,67 | −3,13 | 27,53 | −9,53 | 12,67 → 9,53 ✓ |
| C | 27 | 30,67 | +1,88 | 32,55 | −5,55 | 3,67 → 5,55 |
| D | 35 | 30,67 | +1,88 | 32,55 | +2,45 | 4,33 → 2,45 ✓ |
| E | 50 | 30,67 | +1,88 | 32,55 | +17,45 | 19,33 → 17,45 ✓ |
| F | 42 | 30,67 | +1,88 | 32,55 | +9,45 | 11,33 → 9,45 ✓ |
Total galat absolut turun dari 70,0 → 59,97 hanya dalam satu ronde. Invoice C sempat sedikit naik karena daun kanan merata-ratakan C,D,E,F — persis ini yang diperbaiki ronde berikutnya.
Ronde 2 — perbaiki sisa kesalahan
Pohon ke-2 dibangun atas residual baru. Pada simpul kanan {C,D,E,F}, split terbaik
top_days < 37,5 memisahkan {C,D} dari {E,F} dengan Gain ≈ 131,1, menghasilkan
daun w_{CD}^{*}=-1{,}03 dan w_{EF}^{*}=8{,}97 —
menaikkan prediksi E,F yang memang masih kurang. Proses ini diulang hingga 400 pohon.
08Loop pelatihan penuh
flowchart TB
S[Mulai: ŷ = rata-rata] --> R[Hitung residual r = y − ŷ]
R --> G[Hitung g = −r, h = 1]
G --> T[Bangun pohon: pilih split ber-Gain tertinggi]
T --> W["Hitung nilai daun w* = −G/(H+λ)"]
W --> U["Perbarui ŷ ← ŷ + η·w*"]
U --> C{Sudah 400 pohon
atau galat stabil?}
C -- belum --> R
C -- sudah --> F[Model final XGBoost]
Gambar 3 — Satu siklus boosting diulang ratusan kali; tiap putaran mengejar sisa kesalahan.
Pada data nyata (446 invoice, 12 fitur) inilah yang menghasilkan R² 0,617 · RMSE 10,34 · MAE 6,64 hari — mengungguli Random Forest dan MLR. Keunggulannya: koreksi berurutan + regularisasi menangkap pola tak-linear "klien × termin" yang tidak bisa ditangkap garis lurus.
09Dari model ke layar — alur saat memprediksi
Setelah dilatih, model dipakai saat halaman dibuka. Berikut urutan panggilannya (diagram sekuens):
sequenceDiagram
autonumber
actor U as Finance / BOD
participant App as Aplikasi (Go)
participant PP as paymentpred (client)
participant ML as Sidecar ML (Python)
participant M as Model XGBoost
U->>App: Buka AR Aging / Beranda BOD
App->>App: Ambil invoice belum lunas + bentuk fitur
App->>PP: PredictBatch(daftar invoice)
PP->>ML: POST /predict/payment-days/batch
ML->>M: model.predict(fitur)
M-->>ML: estimasi hari per invoice
ML-->>PP: {predictions:[...]}
PP-->>App: map id → hari (atau "tak tersedia")
alt sidecar sehat
App-->>U: Tampilkan estimasi + flag risiko
else timeout / mati (>3 dtk)
App-->>U: Sembunyikan estimasi (halaman tetap jalan)
end
Gambar 4 — Sequence diagram inferensi: aplikasi → client → sidecar → model → UI, dengan jalur cadangan bila ML tidak tersedia.