Belajar AI #6: Melatih Model — Loss Function, Gradient Descent, Epoch, dan Learning Rate

Episode ini adalah yang paling “matematika” sejauh seri ini, dan saya menulisnya sambil sedikit gemetar. Tapi ternyata inti dari melatih model bisa dijelaskan tanpa satu pun turunan yang ditulis lengkap — cukup dengan analogi yang familiar buat programmer: training itu optimasi parameter, dan semua istilah menakutkannya cuma bagian dari satu loop yang sama.

Loss function: mengukur seberapa salah

Sebelum bisa belajar, model butuh cara mengukur kesalahannya. Itu tugas loss function: fungsi yang menerima prediksi dan jawaban benar, lalu mengembalikan angka “seberapa buruk”. Prediksi pas dengan kunci jawaban? Loss mendekati nol. Melenceng jauh? Loss membengkak.

Dua contoh yang paling umum:

  • MSE (Mean Squared Error) — untuk regresi. Rata-rata kuadrat selisih prediksi vs kenyataan. Dikuadratkan supaya error besar dihukum jauh lebih berat.
  • Cross-entropy — untuk klasifikasi. Menghukum prediksi yang percaya diri tapi salah.

Analogi programmer: loss function itu seperti assertion di test suite — dia yang menentukan lulus atau tidaknya. Bedanya, kita tidak berhenti di lulus/gagal, tapi menghitung seberapa jauh dari lulus.

Gradient descent: turun gunung dalam gelap

Sekarang pertanyaan besarnya: kalau loss mengukur kesalahan, siapa yang memperbaiki parameter? Jawabannya gradient descent, dan analogi klasiknya masih terbaik yang pernah saya baca: kita berdiri di lereng gunung berkabut, ingin mencapai lembah (loss minimum). Kabut tebal — tidak bisa lihat peta. Yang bisa dilakukan: meraba kemiringan tanah di bawah kaki, lalu melangkah ke arah yang paling menurun. Ulangi ribuan kali.

“Meraba kemiringan” itulah gradien — arah penurunan loss terhadap tiap parameter. Framework seperti PyTorch menghitungnya otomatis lewat diferensiasi otomatis; kita tidak perlu menurunkan rumus manual. Loop pelatihannya secara konsep sesederhana ini:

for epoch in range(epochs):
    y_pred = model(X_train)          # 1. forward pass
    loss = loss_fn(y_pred, y_train)  # 2. hitung loss
    optimizer.zero_grad()            # 3. reset gradien
    loss.backward()                  # 4. hitung gradien
    optimizer.step()                 # 5. update bobot

Lima baris ini adalah seluruh deep learning pada dasarnya. Forward, ukur, mundur, geser. Ulangi.

Epoch: berapa kali membaca buku yang sama

Satu epoch = satu putaran penuh model melewati seluruh data latih. Seperti membaca ulang buku yang sama: sekali belum tentu hafal, sepuluh kali mulai nempel, seratus kali malah hafal kata per kata tanpa paham (ini overfitting — bahas tersendiri nanti). Terlalu sedikit epoch, model belum belajar; terlalu banyak, dia menghafal.

Learning rate: seberapa besar langkah

Learning rate menentukan panjang langkah tiap update bobot. Ini hyperparameter paling sensitif yang saya temui sejauh ini:

  • Terlalu besar — langkah melompati lembah, loss naik-turun liar atau malah divergen.
  • Terlalu kecil — progres nyaris tidak terasa, training makan waktu berjam-jam untuk hasil biasa saja.
  • Pas — loss turun stabil lalu melandai.
IstilahAnalogiRisiko salah setting
Loss functionAssertion di test suiteMengukur hal yang salah
Gradient descentTurun gunung berkabutTerjebak di titik tinggi
EpochBaca ulang bukuUnderfit / overfit
Learning ratePanjang langkahDivergen / lambat sekali

Visualisasi loss curve: mata ke dalam training

Saran paling practical yang bisa saya berikan: selalu plot loss curve. Tanpa grafik, training seperti mengemudi menutup mata. Kode berikut menghasilkan kurva training loss vs validation loss — dua garis ini menceritakan hampir segalanya tentang kondisi model Anda:

import matplotlib.pyplot as plt

train_losses = [0.92, 0.61, 0.45, 0.33, 0.25, 0.20, 0.17, 0.15, 0.14, 0.13]
val_losses   = [0.89, 0.58, 0.44, 0.36, 0.32, 0.30, 0.31, 0.33, 0.35, 0.38]

plt.plot(train_losses, label="Training Loss")
plt.plot(val_losses, label="Validation Loss")
plt.xlabel("Epoch")
plt.ylabel("Loss")
plt.legend()
plt.title("Training vs Validation Loss")
plt.show()

Jika training loss turun tapi validation loss mulai naik dari epoch ke-6, itu tanda overfitting — model mulai menghafal data latih. Jika kedua-duanya masih tinggi, model belum cukup belajar atau arsitekturnya terlalu sederhana. Kurva ini adalah diagnosis yang jauh lebih informatif daripada sekadar melihat angka akhir.

Batch vs mini-batch vs stochastic gradient descent

Dalam loop gradient descent di atas, pertanyaannya: berapa banyak data yang dilihat sebelum satu langkah update?

Stochastic Gradient Descent (SGD): update bobot setelah satu sampel. Cepat per langkah, tapi gradien sangat noise — seperti menuruni gunung dengan langkah kecil-kecil acak. Batch Gradient Descent: update setelah melihat seluruh dataset. Gradien stabil, tapi menghitungnya sekaligus untuk jutaan sampel membutuhkan memori besar. Mini-batch: kompromi — update setelah sekelompok kecil data (misalnya 32, 64, atau 256 sampel). Ini yang paling umum dipakai di dunia nyata.

MetodeSampel per updateKecepatan langkahStabilitas gradienMemori
Stochastic (SGD)1Sangat cepatNoise tinggiSangat kecil
Mini-batch32–256CepatSedangSedang
BatchSeluruh datasetLambatSangat stabilBesar

Dalam praktiknya, library seperti PyTorch menyediakan DataLoader yang memecah dataset menjadi mini-batch secara otomatis. Cukup tentukan batch_size, sisanya diurus framework.

Learning rate scheduling: langkah yang cerdas

Learning rate tetap (constant) berfungsi, tapi ada strategi yang lebih baik: mulai dengan langkah besar untuk eksplorasi cepat, lalu perkecil seiring model mendekati titik optimal. Beberapa teknik populer:

Step decay: kurangi learning rate secara berkala (misalnya dibagi 10 setiap 30 epoch). Sederhana dan sering efektif. Cosine annealing: kurangi learning rate mengikuti kurva cosinus — pelan di awal, cepat di tengah, pelan lagi di akhir. Hasilnya lebih halus. Warmup: mulai dengan learning rate sangat kecil (misalnya 1e-7), naikkan perlahan ke target, lalu turunkan. Warmup mencegah gradien liar di awal training saat bobot masih acak.

from torch.optim.lr_scheduler import StepLR, CosineAnnealingLR, OneCycleLR

# Step decay: kurangi lr setiap 30 epoch
scheduler = StepLR(optimizer, step_size=30, gamma=0.1)

# Cosine annealing: kurangi lr mengikuti kurva cosinus
scheduler = CosineAnnealingLR(optimizer, T_max=100)

# Warmup + cosine (OneCycleLR): kombinasi umum di practice
scheduler = OneCycleLR(optimizer, max_lr=0.01, total_steps=epochs)

Pengalaman saya: switching dari constant LR ke OneCycleLR sering memberikan peningkatan akurasi 2–5% tanpa mengubah arsitektur model sama sekali. Hyperparameter kecil, dampak besar.

Optimizer: lebih dari vanilla SGD

Vanilla SGD memperbarui bobot dengan rumus sederhana: w = w - lr * gradient. Tapi ada optimizer yang lebih canggih:

OptimizerIde utamaKapan pakai
SGD + momentumAkumulasi gradien sebelumnya seperti bola menggelindingCV tasks, robust & well-tuned
RMSpropAdaptif learning rate per-parameterRNN, data sequence
AdamMomentum + adaptif LR (kombinasi dua ide di atas)Default paling aman untuk hampir semua kasus
AdamWAdam + weight decay yang benarTransformer, NLP, fine-tuning

Saran praktis: mulai dengan Adam atau AdamW — keduanya memiliki default yang bekerja dengan baik di banyak task. Jika Anda perlu squeeze performa ekstra, barulah beralih ke SGD + momentum dengan learning rate yang di-tuning manual. AdamW khususnya menjadi standar di training model transformer (GPT, BERT, dsb.) karena weight decay yang lebih benar dibandingkan Adam biasa.

Early stopping: berhenti sebelum terlambat

Jika validation loss mulai naik sementara training loss terus turun, itu tanda overfitting. Early stopping mengatasi masalah ini dengan memantau validation loss dan menghentikan training jika tidak ada perbaikan selama beberapa epoch berturut-turut (disebut patience).

best_val_loss = float('inf')
patience = 5
patience_counter = 0

for epoch in range(max_epochs):
    train_loss = train_one_epoch(model, train_loader)
    val_loss = evaluate(model, val_loader)

    if val_loss < best_val_loss:
        best_val_loss = val_loss
        patience_counter = 0
        torch.save(model.state_dict(), "best_model.pt")
    else:
        patience_counter += 1
        if patience_counter >= patience:
            print(f"Early stopping di epoch {epoch}")
            break

Kode di atas hanya 15 baris, tapi menyelamatkan banyak waktu pelatihan dan mencegah model terlalu menghafal data latih. Ini seperti code review yang tahu kapan harus bilang “cukup, jangan tambah fitur lagi.”

Vanishing & exploding gradient: musuh dalam jaringan dalam

Ketika jaringan memiliki banyak layer, gradien harus dikalikan melewati semua layer selama backpropagation. Masalahnya:

  • Vanishing gradient: jika nilai gradien < 1, perkalian berulang membuatnya mendekati nol. Layer awal hampir tidak pernah diperbarui. Model di layer dangkal belajar, yang dalam tidak.
  • Exploding gradient: kebalikannya — gradien membengkak menjadi angka sangat besar, bobot berubah drastis, training divergen. Loss tiba-tiba menjadi NaN.

Penyebab utama: fungsi aktivasi sigmoid/tanh yang mempersempit gradien (sigmoid memotong gradien di 0.25 maksimal per layer), dan inisialisasi bobot yang kurang tepat.

Solusi: Batch normalization menormalisasi output setiap layer sehingga gradien tetap di skala wajar. Residual connection (skip connection) seperti di ResNet memberi jalur alternatif bagi gradien, memotong jalur panjang. Dan penggunaan fungsi aktivasi ReLU (atau varian seperti LeakyReLU) menghindari saturasi negatif yang menyebabkan vanishing gradient. Ketiga teknik ini adalah alasan arsitektur dalam ratusan layer bisa dilatih secara stabil.

Analogi untuk programmer: training loop = CI/CD pipeline

Jika Anda terbiasa dengan CI/CD, training loop terasa mirip: data latih adalah “kode”, model adalah “build artifact”, loss function adalah “test suite”, dan epoch adalah “pipeline run” yang berulang. Early stopping seperti conditional deployment — hentikan jika metrik memburuk. Learning rate scheduling seperti progressive rollout: mulai agresif, perlahan stabilkan. Batch size seperti parallelism dalam pipeline: lebih besar lebih cepat tapi butuh lebih banyak resources. Analologi ini membantu saya melatih mentalitas: training bukan sekali jalan lalu selesai, tapi proses iteratif yang membutuhkan monitoring dan tuning berkelanjutan.

Pitfall: membaca kurva training yang salah

Kesalahan umum yang saya buat di awal:

  • Learning rate terlalu besar: loss naik-turun liar, seperti grafik yang “bergetar”. Saya awalnya mengira model sedang “belajar dengan semangat” — ternyata justru melompati minimum dan tidak pernah konvergen.
  • Learning rate terlalu kecil: loss turun tapi nyaris horizontal. “Perlahan pasti sampai” tidak berlaku di sini — yang terjadi adalah buang waktu komputasi tanpa hasil yang layak.
  • Overfitting terlihat dari kurva: training loss mendekati nol, validation loss naik. Model sudah “menghafal jawaban ujian” bukan memahami polanya.
  • Underfitting: kedua loss masih tinggi. Modelnya kurang kompleks untuk menangkap pola data, atau epoch belum cukup.

Catatan penting: jangan hanya melihat angka loss akhir. Perhatikan shape kurva — apakah turun monoton, naik-turun liar, atau sudah plateau. Bentuk kurva memberi petunjuk diagnostik yang jauh lebih kaya daripada satu angka terakhir.

Catatan untuk diri sendiri

Kesulitan terbesar saya: dulu menganggap training sebagai kotak hitam ajaib. Setelah memecahnya jadi lima langkah di loop di atas, rasa takutnya hilang. Saran praktis kalau baru mulai: plot kurva loss per epoch. Grafik itu adalah “kamera” satu-satunya ke dalam proses training — naik berarti learning rate kebesaran, datar berarti terlalu kecil atau model salah arsitektur.

Pengalaman pertama yang tidak akan saya lupakan: ketika training loop berjalan dan saya melihat angka loss turun dari 2.3 menjadi 0.8 dalam beberapa epoch pertama — perasaan itu seperti melihat “Hello World!” pertama kali. Angka abstrak tiba-tiba bermakna. Model sedang belajar. Bukan sihir, bukan kotak hitam — hanya gradien, loss, dan iterasi yang bekerja persis seperti yang seharusnya. Sejak saat itu saya percaya: siapa pun yang bisa menulis for-loop bisa memahami deep learning.

Sumber

Tinggalkan Balasan

Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai *

Situs ini menggunakan Akismet untuk mengurangi spam. Pelajari bagaimana data komentar Anda diproses