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.

python
# 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:

text
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.00

Mari kita bedah hasilnya:

  1. Bagian 1 (baris 13–25): LogisticRegression pada 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.
  2. Bagian 2 (baris 27–41): satu fitur baru x1*x2 mengubah segalanya. Nilai interaksi 0 pada label 0 dan 1 pada label 1, sehingga kini masalahnya linier lagi; dengan C=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:

python
# 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:

text
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.9770

Tabel di akhir output adalah inti chapter ini — mari kita baca dengan teliti:

  1. Baris 19–24 (inisialisasi): bobot W1, W2 diundi dari rng.normal berseed 42; rng ini memastikan hasil identik setiap eksekusi.
  2. 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.
  3. Baris 57–60 (representasi tersembunyi): dua neuron tersembunyi ternyata membangun fitur pembanding sendiri — neuron1 bernilai tinggi ketika x1 >= x2 (titik (1,0) dan (0,0)) dan neuron2 bernilai tinggi ketika x2 >= 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 fitur x1 >= 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.

SkenarioPilihan Awal yang WajarAlasan Utama
Data tabular, < 100 ribu barisGradient boosting / random forestKuat pada tabel, minim pra-pemrosesan
Data tabular, sangat besarGradient boosting, pertimbangkan DL pada pola kompleksBoosting tetap efisien; DL butuh data masif
Gambar, audio, videoDeep learning (CNN, transformer)Fitur piksel/sample terlalu rumit dirancang manual
Teks dan bahasaDeep learning (transformer)Representasi kata/urutan harus dipelajari
Data sangat kecil (< ratusan baris)Model linier / tree sederhanaSemua model kompleks akan overfitting
Butuh penjelasan per fiturModel linier / tree + SHAPJaringan 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 #