← semua materi
05:00 Arsitektur · 12 Oct 2026 · 22:01

📚 Backpropagation — Derivasi Gradient dari Chain Rule

Sesi: 05:00 Deep Learning · Level: Advanced · Kategori: AI/ML


🎯 Tujuan Belajar

Setelah sesi ini, kamu harus bisa: 1. Menurunkan gradient setiap parameter di multilayer perceptron (MLP) dari nol, tanpa mengandalkan rumus hafalan. 2. Menjelaskan mengapa backprop sebenarnya bukan algoritma baru, melainkan aplikasi reverse-mode automatic differentiation pada computational graph. 3. Mengidentifikasi sumber kesalahan umum: sumbu yang salah saat operasi matriks, gradient yang tidak di-reset, dan derivative activation yang salah. 4. Menyambungkan konsep ini ke autograd PyTorch/JAX secara mental.


🧠 Penjelasan Inti

Apa itu backpropagation?

Kita punya jaringan saraf yang, secara matematis, hanyalah fungsi komposit:

L = loss( ŷ, y )
ŷ = f_L( f_{L-1}( ... f_1(x; W_1) ... ; W_L ) )

Untuk melatihnya kita butuh ∂L/∂W_i untuk setiap W_i. Cara naif (numerical differentiation): perturbasi tiap parameter satu per satu dan lihat perubahan loss. Satu parameter = satu forward pass penuh. Untuk model dengan 10⁹ parameter, itu mustahil.

Masalah yang diselesaikan backprop: menghitung semua gradient ini dalam satu kali forward pass + satu kali backward pass, dengan biaya komputasi yang secara asimptotik sebanding dengan forward pass itu sendiri (≈4× biaya forward untuk kasus matmul).

Kenapa ada?

Fondasi: chain rule

Untuk fungsi skalar dari komposisi skalar:

z = f(y),  y = g(x)
dz/dx = (dz/dy) · (dy/dx)

Untuk fungsi multi-variabel, ini menjadi penjumlahan atas semua jalur kontribusi:

∂z/∂x = Σ_i  (∂z/∂y_i) · (∂y_i/∂x)

Perhatikan: jumlah, bukan kali. Kalau satu variabel x mengalir ke banyak node, gradient dari tiap jalur dijumlahkan. Ini akar dari banyak bug saat menulis backprop manual.


⚙️ Cara Kerjanya

1. Forward pass — bangun graph + simpan aktivasi

Setiap operasi menghasilkan node di graph. Untuk tiap node kita simpan: - nilai output, - referensi ke input node, - local Jacobian (turunan operasi terhadap inputnya).

Contoh MLP sederhana, satu hidden layer, loss MSE:

z1 = x  @ W1 + b1        # pre-activation layer 1
a1 = σ(z1)               # aktivasi
z2 = a1 @ W2 + b2        # pre-activation layer 2 (output)
L  = mean( (z2 - y)^2 )  # scalar loss

2. Backward pass — reverse topological order

Mulai dari ∂L/∂L = 1, lalu "mendorong" gradient ke belakang. Definisikan δ_k = ∂L/∂z_k (gradient terhadap pre-activation). Ini praktisnya yang kita propagasikan.

Langkah demi langkah (pakai chain rule + aturan penjumlahan):

Loss layer:

∂L/∂z2 = ∂L/∂z2 = δ2   (untuk MSE: δ2 = 2(z2 - y)/N)

Perhatikan z2 muncul di setiap elemen loss → itulah kenapa ∂L/∂z2 dijumlahkan atas semua elemen z2.

Layer 2 parameter:

z2 = a1 @ W2 + b2
∂L/∂W2 = a1ᵀ @ δ2          # bentuk (hidden × output)
∂L/∂b2 = sum(δ2, axis=0)   # broadcast b2 → jumlahkan gradient

Alirkan ke layer 1:

∂L/∂a1 = δ2 @ W2ᵀ
∂L/∂z1 = ∂L/∂a1 ⊙ σ'(z1)   # ⊙ = elementwise (Hadamard)

Kunci: σ bekerja elementwise, jadi Jacobian-nya diagonal → cukup perkalian elementwise, bukan matmul.

Layer 1 parameter:

∂L/∂W1 = xᵀ @ δ1
∂L/∂b1 = sum(δ1, axis=0)

3. Pola yang harus kamu hafal sebagai kerangka, bukan rumus

Lihat simetri-nya. Untuk node generik z = a @ W + b: - gradient weight = aᵀ @ δ - gradient bias = sum(δ) - gradient input (untuk diteruskan) = δ @ Wᵀ

Ukuran selalu cocok: ∂L/∂W harus berdimensi sama dengan W. Ini sanity check paling ampuh.

4. Generalisasi: computational graph & AD

Yang baru saja kita lakukan adalah reverse-mode automatic differentiation. Dua mode:

Mode Arah Bagus untuk Biaya per gradient
Forward-mode maju, sepanjang graph input sedikit, output banyak 1 pass per kolom Jacobian
Reverse-mode (backprop) mundur output skalar, input banyak (neural net!) 1 pass total untuk 1 output

Karena loss kita skalar dan parameter banyak, reverse-mode menang telak. Itu sebabnya training pakai backprop; kalkulasi Jacobian penuh dengan input banyak (misal semua elemen x) lebih efisien dengan forward-mode.


💡 Contoh / Analogi

Analogi tanggung jawab kerugian. Bayangkan L itu total kerugian perusahaan. W2 adalah keputusan di level cabang; W1 keputusan di level staf. Backprop bertanya: "Kalau staf ini mengubah outputnya sedikit, seberapa besar total kerugian berubah?" Untuk menjawab, kita mulai dari kerugian → besarnya pengaruh tiap cabang → lalu turun ke tiap staf. Aturan rantai = mengalikan faktor pengaruh di sepanjang rantai; aturan jumlah = menambah pengaruh dari semua jalur yang dilalui.

Contoh angka (satu neuron).

z = w·x + b,   w=2, x=3, b=1  → z=7
L = z² → ∂L/∂z = 2z = 14
∂L/∂w = 14 · x = 42
∂L/∂b = 14 · 1  = 14

Cek numerik: perturbasi w ke 2.0001 → z = 7.0003, L = 49.00420009, ΔL/Δw ≈ 42.0009 → cocok.

Contoh graph bercabang (sumber bug).

a = 3
b = a * 2      # b = 6
c = a * 5      # c = 15
d = b + c      # d = 21

∂d/∂a = ∂d/∂b·∂b/∂a + ∂d/∂c·∂c/∂a = 1·2 + 1·5 = 7. Bukan 2 atau 5 saja. Setiap kali variabel dipakai lebih dari sekali, gradient-nya dijumlahkan. Di framework, ini terjadi otomatis; saat menulis backprop manual, ini yang paling sering salah.


⚠️ Miskonsepsi Umum

  1. "Backprop adalah cara belajar." Bukan. Backprop hanya menghitung gradient. Bagaimana gradient dipakai (SGD, Adam, dsb.) adalah hal terpisah. Backprop = kalkulus; optimizer = keputusan algoritmik.

  2. "Backprop = gradient descent." Beda. Gradient descent adalah strategi update parameter; backprop adalah metode efisien menghitung gradient untuk dipakai strategi apa pun.

  3. "Backprop sekadar chain rule biasa." Ia chain rule, tapi dengan memoisasi: hasil antara dipakai ulang, bukan dihitung ulang per jalur eksponensial. Tanpa memoisasi, kompleksitasnya bisa eksponensial.

  4. "Graph harus dibangun backward." Tidak. Graph dibangun saat forward pass; backward hanya traversal terurut-topologis-terbalik. Ini kesalahan pemahaman umum soal "reverse-mode".

  5. "Derivative activation cukup dikali saja." Hanya benar bila aktivasi elementwise. Untuk operasi yang mencampur elemen (softmax, matmul, batchnorm) Jacobian-nya bukan diagonal — harus diperlakukan sebagai transformasi penuh.

  6. "Gradient tidak perlu di-reset." Di framework, akumulasi gradient adalah fitur (berguna untuk gradient accumulation) tapi jadi bug kalau lupa zero_grad() antar-langkah. Konsekuensi: update meledak atau salah.


⚖️ Trade-off / Limitasi


🛠️ Latihan Praktis

Latihan 1 — Derivasi manual. Turunkan ∂L/∂W1 dan ∂L/∂W2 untuk MLP dua-layer dengan σ = tanh dan L = ½‖z2 − y‖². Tulis dalam notasi matriks.

Latihan 2 — Gradient check. Implementasi forward+backward MLP dari nol (NumPy), lalu verifikasi tiap ∂L/∂W dengan finite difference:

(grad_analytic - grad_numeric) / (|grad_analytic| + |grad_numeric| + eps) < 1e-6

Kalau tidak lolos, curigai: salah sumbu matmul, salah transpose, atau lupa jumlahkan jalur bercabang.

Latihan 3 — Bug yang disengaja. Ambil graph bercabang di atas (d = 2a + 5a), lalu implementasi backward yang hanya memakai satu jalur. Bandingkan dengan torch.autograd — selisih faktor berapa? Mengapa?

Latihan 4 (advanced). Turunkan backward pass untuk operasi softmax + cross-entropy digabung. Tunjukkan bahwa hasilnya menyederhana menjadi (ŷ − y) — hasil ini adalah alasan mengapa fused softmax-crossentropy lebih stabil secara numerik.


✅ Cek Pemahaman

  1. Mengapa backprop memerlukan one pass untuk seluruh gradient, sedangkan finite-difference memerlukan satu pass per parameter? Diagramkan aliran data-nya.

  2. Untuk operasi z = a @ W, jelaskan mengapa ∂L/∂W = aᵀ @ δ dan bukan δ @ aᵀ. Apa peran dimensi di sini?

  3. Sebuah jaringan kehilangan konvergensi setelah menambahkan cabang residual (skip connection). Mengapa ini bisa terjadi (petunjuk: skala gradient), dan bagaimana arsitektur modern mengatasinya?


🔑 Poin Kunci


➡️ Eksplorasi Lanjut

Backprop through time (BPTT) dan vanishing/exploding gradient pada RNN. Bagaimana chain rule yang sama menghasilkan produk tak hingga dari Wᵀ yang mengeksploitasi/meredam gradient? Lanjut ke LSTM/GRU dan mengapa gating adalah trik rekayasa untuk menstabilkan aliran gradient.