Section sebelumnya ditutup dengan satu kalimat penting: Q-learning tabular tidak lagi memadai ketika jumlah state membengkak, dan jalan keluarnya adalah function approximation — mengganti tabel Q dengan fungsi yang bisa diperkirakan. Alat pendekatan fungsi yang paling sering dipilih di dunia modern adalah neural network, dan di situlah deep learning berada. Pada Section ini kita akan membangun jaringan saraf tersebut dari nol dengan NumPy murni — perkalian matriks untuk maju, gradien manual untuk mundur — supaya kotak hitamnya terbuka sebelum kita percaya padanya. Chapter pembuka ini menjawab dulu pertanyaan motivasi: masalah apa yang tidak bisa diselesaikan model klasik, mengapa menambah “kedalaman” membantu, dan di titik mana model klasik masih justru pilihan terbaik.
Masalah Representasi #
Perhatikan kembali cara kerja seluruh model yang telah kita pakai sepanjang Course ini: LogisticRegression, RandomForest, hingga GradientBoostingClassifier menerima sekumpulan fitur masukan yang sudah jadi. Kualitas prediksi mereka bergantung besar pada kualitas fitur tersebut — sudah kita buktikan di Section Feature Engineering dan Seleksi (Chapter Konsep Feature Engineering) bahwa fitur yang dirancang baik sering lebih menentukan hasil daripada pemilihan algoritmenya. Pendekatan itu disebut feature engineering manual: manusia memeriksa data, menebak transformasi apa yang akan memisahkan kelas (kuadrat, rasio, interaksi, agregasi), lalu menuliskannya satu per satu.
Deep learning mengubah pembagian kerja tersebut. Alih-alih manusia merancang fitur dan model hanya belajar bobot akhir, jaringan saraf belajar keduanya sekaligus: lapisan-lapisan tersembunyi membangun representasi antara dari data mentah, dan lapisan terakhir memakai representasi itu untuk memprediksi. Kemampuan ini disebut representation learning. Ketika masalah datang sebagai piksel gambar, sampel audio, atau urutan kata, menulis fitur manual yang bersaing dengan representasi terpelajari terbukti sangat sulit — inilah wilayah utama deep learning.
Demo XOR: Masalah Kecil yang Menghentikan Model Linier #
Motivasi paling klasik untuk jaringan saraf adalah operasi XOR: prediksi label 1 ketika dua bit masukan berbeda, dan 0 ketika keduanya sama. Tabel kebenarannya hanya empat baris, tetapi keempat titiknya tidak bisa dipisahkan satu garis lurus — properti yang kita kenal sejak perceptron era 1960-an. Mari kita buktikan dengan regresi logistik, lalu lihat dua jalan keluar: fitur interaksi rancangan tangan, dan jaringan saraf kecil yang belajar representasinya sendiri.
# XOR dengan model linier: batas keputusan berupa garis lurus tidak bisa
# memisahkan empat titik ini. Bagian pertama memakai fitur mentah, bagian
# kedua menambahkan fitur interaksi x1*x2 yang dirancang tangan.
import numpy as np
from sklearn.linear_model import LogisticRegression
# Empat titik XOR beserta labelnya.
X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]], dtype=float)
y = np.array([0, 1, 1, 0])
# Bagian 1: regresi logistik pada fitur mentah (x1, x2).
model_linier = LogisticRegression(random_state=42)
model_linier.fit(X, y)
peluang = model_linier.predict_proba(X)[:, 1]
prediksi = model_linier.predict(X)
print("Bagian 1: fitur mentah (x1, x2)")
benar = 0
for i in range(len(y)):
cocok = "benar" if prediksi[i] == y[i] else "salah"
benar += prediksi[i] == y[i]
print(f"x1={X[i, 0]:.0f} x2={X[i, 1]:.0f} label={y[i]} "
f"peluang={peluang[i]:.4f} prediksi={prediksi[i]} -> {cocok}")
print(f"akurasi = {benar / len(y):.2f}")
# Bagian 2: tambahkan fitur interaksi x1*x2 yang dirancang tangan.
X_interaksi = np.column_stack([X, X[:, 0] * X[:, 1]])
model_interaksi = LogisticRegression(C=100.0, random_state=42)
model_interaksi.fit(X_interaksi, y)
peluang2 = model_interaksi.predict_proba(X_interaksi)[:, 1]
prediksi2 = model_interaksi.predict(X_interaksi)
print("\nBagian 2: fitur mentah + interaksi x1*x2")
benar2 = 0
for i in range(len(y)):
cocok = "benar" if prediksi2[i] == y[i] else "salah"
benar2 += prediksi2[i] == y[i]
print(f"x1={X[i, 0]:.0f} x2={X[i, 1]:.0f} label={y[i]} "
f"peluang={peluang2[i]:.4f} prediksi={prediksi2[i]} -> {cocok}")
print(f"akurasi = {benar2 / len(y):.2f}")Output yang Dihasilkan:
Bagian 1: fitur mentah (x1, x2)
x1=0 x2=0 label=0 peluang=0.5000 prediksi=0 -> benar
x1=0 x2=1 label=1 peluang=0.5000 prediksi=0 -> salah
x1=1 x2=0 label=1 peluang=0.5000 prediksi=0 -> salah
x1=1 x2=1 label=0 peluang=0.5000 prediksi=0 -> benar
akurasi = 0.50
Bagian 2: fitur mentah + interaksi x1*x2
x1=0 x2=0 label=0 peluang=0.1553 prediksi=0 -> benar
x1=0 x2=1 label=1 peluang=0.8817 prediksi=1 -> benar
x1=1 x2=0 label=1 peluang=0.8817 prediksi=1 -> benar
x1=1 x2=1 label=0 peluang=0.0813 prediksi=0 -> benar
akurasi = 1.00Mari kita bedah hasilnya:
- Bagian 1 (baris 13–25):
LogisticRegressionpada fitur mentah menyerah total — keempat peluang tercetak 0,5000 dan seluruh prediksi jatuh ke kelas 0, sehingga akurasi hanya 0,50. Model linier hanya bisa memasang satu garis, dan XOR membutuhkan dua. - Bagian 2 (baris 27–41): satu fitur baru
x1*x2mengubah segalanya. Nilai interaksi 0 pada label 0 dan 1 pada label 1, sehingga kini masalahnya linier lagi; denganC=100.0(regularisasi dilemahkan agar koefisien interaksi tumbuh bebas), akurasi mencapai 1,00.
Hasil tersebut menegaskan pelajaran Section Feature Engineering dan Seleksi: kualitas representasi menentukan hasil. Tetapi perhatikan konsekuensinya — fitur x1*x2 tidak jatuh dari langit. Kita harus tahu bahwa interaksi itulah yang dibutuhkan. Untuk empat titik ini pekerjaan tampak sepele; bayangkan melakukannya untuk 784 piksel digit tulisan tangan. Sekarang kita serahkan pekerjaan yang sama kepada jaringan saraf 2-2-1 yang ditulis murni dengan NumPy, tanpa satu pun fitur tambahan:
# XOR dengan jaringan saraf 2-2-1 murni NumPy. Tidak ada fitur tambahan:
# jaringan belajar sendiri representasi internal di lapisan tersembunyi.
# Rincian backpropagation dibahas tuntas pada chapter "Loss Function dan
# Backpropagation"; di sini kita fokus pada hasil representasinya.
import numpy as np
rng = np.random.default_rng(42)
# Data XOR yang sama seperti contoh sebelumnya.
X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]], dtype=float)
y = np.array([[0], [1], [1], [0]], dtype=float)
def sigmoid(z):
return 1.0 / (1.0 + np.exp(-z))
# Inisialisasi bobot lapisan tersembunyi (2 masukan -> 2 neuron) dan
# lapisan keluaran (2 neuron tersembunyi -> 1 keluaran).
W1 = rng.normal(0.0, 1.0, size=(2, 2))
b1 = np.zeros(2)
W2 = rng.normal(0.0, 1.0, size=(2, 1))
b2 = np.zeros(1)
laju = 1.0
for epoch in range(20001):
# Maju: hitung prediksi dari bobot saat ini.
A1 = sigmoid(X @ W1 + b1) # aktivasi lapisan tersembunyi
A2 = sigmoid(A1 @ W2 + b2) # prediksi akhir
loss = np.mean((A2 - y) ** 2)
# Mundur: perbarui bobot melawan arah gradien.
dZ2 = 2.0 * (A2 - y) / len(y) * A2 * (1.0 - A2)
dW2 = A1.T @ dZ2
db2 = dZ2.sum(axis=0)
dZ1 = (dZ2 @ W2.T) * A1 * (1.0 - A1)
dW1 = X.T @ dZ1
db1 = dZ1.sum(axis=0)
W2 -= laju * dW2
b2 -= laju * db2
W1 -= laju * dW1
b1 -= laju * db1
if epoch % 5000 == 0:
print(f"epoch {epoch:5d} loss {loss:.6f}")
prediksi = (A2 > 0.5).astype(int).ravel()
akurasi = (prediksi == y.ravel()).mean()
print(f"\nakurasi = {akurasi:.2f}")
print("\nPrediksi jaringan:")
for i in range(len(y)):
print(f"x1={X[i, 0]:.0f} x2={X[i, 1]:.0f} label={y[i, 0]:.0f} "
f"peluang={A2[i, 0]:.4f} prediksi={prediksi[i]}")
print("\nRepresentasi terpelajari di lapisan tersembunyi:")
print("titik neuron1 neuron2")
for i in range(len(y)):
print(f"x1={X[i, 0]:.0f} x2={X[i, 1]:.0f} {A1[i, 0]:.4f} {A1[i, 1]:.4f}")Output yang Dihasilkan:
epoch 0 loss 0.379805
epoch 5000 loss 0.001247
epoch 10000 loss 0.000536
epoch 15000 loss 0.000338
epoch 20000 loss 0.000246
akurasi = 1.00
Prediksi jaringan:
x1=0 x2=0 label=0 peluang=0.0150 prediksi=0
x1=0 x2=1 label=1 peluang=0.9830 prediksi=1
x1=1 x2=0 label=1 peluang=0.9831 prediksi=1
x1=1 x2=1 label=0 peluang=0.0137 prediksi=0
Representasi terpelajari di lapisan tersembunyi:
titik neuron1 neuron2
x1=0 x2=0 0.9656 0.9735
x1=0 x2=1 0.0360 1.0000
x1=1 x2=0 1.0000 0.0296
x1=1 x2=1 0.9726 0.9770Tabel di akhir output adalah inti chapter ini — mari kita baca dengan teliti:
- Baris 19–24 (inisialisasi): bobot
W1,W2diundi darirng.normalberseed 42;rngini memastikan hasil identik setiap eksekusi. - Baris 27–47 (pelatihan): 20.000 iterasi gradient descent penuh; loss turun dari 0,379805 ke 0,000246 dan akurasi mencapai 1,00 — tanpa satu pun fitur tambahan.
- Baris 57–60 (representasi tersembunyi): dua neuron tersembunyi ternyata membangun fitur pembanding sendiri —
neuron1bernilai tinggi ketikax1 >= x2(titik (1,0) dan (0,0)) danneuron2bernilai tinggi ketikax2 >= x1(titik (0,1) dan (0,0)). Lapisan keluaran lalu mengombinasikan keduanya: label 1 tepat ketika hanya salah satu neuron yang aktif. Tidak ada manusia yang menulis fiturx1 >= x2; representasi itu muncul dari pelatihan.
Kenapa Kedalaman Membantu #
Jaringan 2-2-1 di atas sudah menunjukkan satu tingkat representasi terpelajari. Deep learning menumpuk banyak tingkat: keluaran lapisan pertama menjadi masukan lapisan kedua, dan seterusnya. Kekuatannya berasal dari komposisi fungsi sederhana. Setiap lapisan hanya perlu melakukan transformasi ringan — jumlah tertimbang ditambah nonlinieritas — tetapi susunan bertingkatnya bisa menggambarkan fungsi yang jauh lebih kompleks. Pada pengenalan gambar, representasi lapisan awal berupa tepi dan warna, lapisan tengah berupa tekstur dan pola lokal, lapisan atas berupa bagian objek (mata, roda), dan lapisan akhir berupa objek utuh. Setiap tingkat membangun di atas tingkat sebelumnya, sehingga kompleksitas total tumbuh secara eksponensial terhadap kedalaman, bukan linear terhadap jumlah neuron.
Kedalaman bukan obat segalanya. Menambah lapisan berarti menambah parameter, dan parameter tambahan hanya berguna ketika data cukup banyak untuk mengisinya dengan pola yang benar — kalau tidak, kita hanya mendapat overfitting yang lebih mahal. Kompromi data-versus-kapasitas ini adalah bias-variance tradeoff lama dari Section Evaluasi dan Validasi Model, kini dalam wujud yang lebih ekstrem.
Tempat Machine Learning Klasik Masih Unggul #
Kejujuran penting dalam memilih alat: pada banyak masalah bisnis yang datanya berbentuk tabel (tabular data) berukuran ratusan hingga puluhan ribu baris, gradient boosting masih sulit dikalahkan. Chapter Gradient Boosting pada Section Ensemble Learning telah menunjukkan alasannya: pohon keputusan tidak peduli penskalaan fitur, tahan campuran fitur numerik dan kategorikal, dan ensemble bertahapnya sangat efisien pada struktur tabel. Pedoman praktisnya dapat dirangkum dalam tabel berikut.
| Skenario | Pilihan Awal yang Wajar | Alasan Utama |
|---|---|---|
| Data tabular, < 100 ribu baris | Gradient boosting / random forest | Kuat pada tabel, minim pra-pemrosesan |
| Data tabular, sangat besar | Gradient boosting, pertimbangkan DL pada pola kompleks | Boosting tetap efisien; DL butuh data masif |
| Gambar, audio, video | Deep learning (CNN, transformer) | Fitur piksel/sample terlalu rumit dirancang manual |
| Teks dan bahasa | Deep learning (transformer) | Representasi kata/urutan harus dipelajari |
| Data sangat kecil (< ratusan baris) | Model linier / tree sederhana | Semua model kompleks akan overfitting |
| Butuh penjelasan per fitur | Model linier / tree + SHAP | Jaringan dalam lebih sulit ditafsir |
Harga yang dibayar deep learning juga bukan hanya data: pelatihan jaringan besar membutuhkan komputasi yang jauh lebih besar (GPU), waktu eksperimen yang lebih lama, dan rantai keputusan hyperparameter yang lebih panjang. Ketika tim dan anggaran komputasi terbatas, model klasik yang berlatih dalam hitungan detik sering menjadi titik mulai yang tepat — kita akan susun kerangka keputusannya secara sistematis pada chapter penutup Section, Kapan Menggunakan Deep Learning.
Sebelum sampai pada kerangka itu, kita perlu membangun mesinnya terlebih dahulu: apa sebenarnya “neuron” di dalam jaringan tadi, dan bagaimana susunan matriks X @ W + b menggantikan perulangan eksplisit? Itulah materi Chapter Neuron Artificial dan Feedforward Network.
Referensi Resmi #
- Neural network supervised — scikit-learn User Guide
— gambaran resmi
MLPClassifierdanMLPRegressorbeserta batasnya. - Logistic regression — scikit-learn User Guide — model linier yang dipakai pada demo XOR.
- Random Generator — NumPy — pembangkit angka acak berseed yang menjamin determinisme seluruh contoh Section ini.