Seluruh komponen kini tersedia: model dari nn.Module, fungsi loss dan optimizer, serta pasokan batch dari DataLoader. Yang belum ada adalah mesin yang memutar semuanya secara bergilir — training loop. Bentuknya nyaris sama di setiap proyek PyTorch, dari model mainan sampai riset skala besar, sehingga memahami setiap barisnya bernilai jangka panjang: sekali kuasai, kita bisa membaca kode pelatihan proyek orang lain dalam hitungan detik. Chapter ini membedah cetakan tersebut sampai ke detail urutan zero_grad, lalu menjalankannya pada dataset klasifikasi 512 sampel dengan validasi tiap epoch.
Anatomi Satu Putaran Training #
Satu epoch berarti satu putaran penuh menelusuri seluruh data latih. Di dalamnya, setiap batch melewati empat langkah yang sudah kita kenal secara matematis pada section Dasar Neural Network — kini dikerjakan oleh autograd dan optimizer:
for X_batch, y_batch in loader:
optimizer.zero_grad()
loss = criterion(model(X_batch), y_batch)
loss.backward()
optimizer.step()Urutannya ketat: gradien lama dibersihkan lebih dulu, forward pass menghasilkan prediksi, loss mengukur kesalahannya, backward pass mengisi atribut .grad tiap parameter, dan step() menggeser parameter melawan arah gradien. Diagram berikut menempatkan siklus batch tersebut di dalam kerangka epoch yang lebih besar:
Cetakan Utuh dengan Validasi #
Berkas training_loop.py menerapkan cetakan tersebut pada make_moons
— 512 sampel dua fitur, dibagi 384 sampel latih dan 128 sampel validasi. Modelnya MLP [2, 16, 2] dengan CrossEntropyLoss dan SGD, dilatih 30 epoch:
"""Cetakan training loop PyTorch dengan validasi tiap epoch.
Alur satu epoch: model.train() -> iterasi batch (zero_grad -> forward ->
loss -> backward -> step) -> model.eval() + torch.no_grad() untuk
validasi -> log. Dataset make_moons 512 sampel, MLP [2, 16, 2],
SGD lr=0.5, batch 32, 30 epoch.
"""
import torch
import torch.nn as nn
from sklearn.datasets import make_moons
from sklearn.model_selection import train_test_split
from torch.utils.data import DataLoader, TensorDataset
torch.manual_seed(42)
# ---------- 1. Data: make_moons, pembagian latih/validasi ----------
X, y = make_moons(n_samples=512, noise=0.20, random_state=42)
X_latih, X_val, y_latih, y_val = train_test_split(
X, y, test_size=0.25, random_state=42
)
# Standardisasi dihitung dari data latih saja, lalu diterapkan keduanya
rata2 = X_latih.mean(axis=0)
simpangan = X_latih.std(axis=0)
X_latih = (X_latih - rata2) / simpangan
X_val = (X_val - rata2) / simpangan
dataset_latih = TensorDataset(
torch.tensor(X_latih, dtype=torch.float32),
torch.tensor(y_latih, dtype=torch.long),
)
X_val_t = torch.tensor(X_val, dtype=torch.float32)
y_val_t = torch.tensor(y_val, dtype=torch.long)
loader = DataLoader(
dataset_latih,
batch_size=32,
shuffle=True,
num_workers=0,
generator=torch.Generator().manual_seed(42),
)
# ---------- 2. Model, fungsi loss, optimizer ----------
model = nn.Sequential(
nn.Linear(2, 16),
nn.ReLU(),
nn.Linear(16, 2),
)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.5)
print("dataset : make_moons 512 sampel | latih", len(dataset_latih),
"| validasi", len(y_val_t))
print("model : [2, 16, 2] | CrossEntropyLoss | SGD lr=0.5 | batch 32")
print()
# ---------- 3. Training loop: latih + validasi tiap epoch ----------
JUMLAH_EPOCH = 30
for epoch in range(1, JUMLAH_EPOCH + 1):
model.train() # aktifkan mode latih
total_loss = 0.0
for X_batch, y_batch in loader:
optimizer.zero_grad() # hapus gradien epoch sebelumnya
logits = model(X_batch) # 1) forward pass
loss = criterion(logits, y_batch) # 2) hitung loss
loss.backward() # 3) backward pass
optimizer.step() # 4) update parameter
total_loss += loss.item()
loss_latih = total_loss / len(loader)
model.eval() # nonaktifkan perilaku khusus latih
with torch.no_grad(): # validasi tanpa graf gradien
logits_val = model(X_val_t)
loss_val = criterion(logits_val, y_val_t).item()
benar = (logits_val.argmax(dim=1) == y_val_t).sum().item()
akurasi_val = benar / len(y_val_t)
if epoch == 1 or epoch % 5 == 0:
print(f"epoch {epoch:2d} | loss latih {loss_latih:.4f} | "
f"loss val {loss_val:.4f} | akurasi val {akurasi_val:.3f}")
# ---------- 4. Akurasi akhir pada kedua subset ----------
model.eval()
with torch.no_grad():
pred_latih = model(dataset_latih.tensors[0]).argmax(dim=1)
akurasi_latih = (pred_latih == dataset_latih.tensors[1]).float().mean().item()
akurasi_val_akhir = akurasi_val
print(f"\nakurasi akhir: latih {akurasi_latih:.3f} | "
f"validasi {akurasi_val_akhir:.3f}")Output yang Dihasilkan:
dataset : make_moons 512 sampel | latih 384 | validasi 128
model : [2, 16, 2] | CrossEntropyLoss | SGD lr=0.5 | batch 32
epoch 1 | loss latih 0.3984 | loss val 0.3247 | akurasi val 0.883
epoch 5 | loss latih 0.2869 | loss val 0.2903 | akurasi val 0.875
epoch 10 | loss latih 0.2395 | loss val 0.2416 | akurasi val 0.883
epoch 15 | loss latih 0.1739 | loss val 0.1779 | akurasi val 0.914
epoch 20 | loss latih 0.1283 | loss val 0.1356 | akurasi val 0.938
epoch 25 | loss latih 0.1021 | loss val 0.1081 | akurasi val 0.945
epoch 30 | loss latih 0.0859 | loss val 0.0958 | akurasi val 0.961
akurasi akhir: latih 0.977 | validasi 0.961Mari kita telaah kode di atas baris per baris:
- Baris 15 (
torch.manual_seed(42)): mengunci inisialisasi bobot dan urutan shuffle ke satu nilai seed, sehingga keluaran di atas muncul identik pada setiap eksekusi — nanti dibahas tuntas di chapter Reproducibility . - Baris 18-27 (data): pembagian latih/validasi memakai
random_state=42, dan standardisasi dihitung dari data latih saja — rata-rata dan simpangan data validasi tidak boleh menyusup ke preprocessing agar evaluasi tetap jujur. - Baris 36-42 (
DataLoader): pola dari chapter Dataset dan DataLoader : batch 32,shuffle=True, generator ber-seed — 384 sampel menghasilkan 12 batch per epoch. - Baris 45-51 (komponen):
nn.Sequentialmenyusun model;CrossEntropyLossmenerima logits mentah dan mengembalikan loss rata-rata batch;SGD(lr=0.5)menggeser tiap parameter. Pemilihan loss dan optimizer dibahas pada chapter Loss dan Optimizer . - Baris 61 (
model.train()): mengaktifkan mode latih — untuk MLP Linear+ReLU ini tidak mengubah apa pun, tetapi kebiasaannya dipasang sejak awal karena dropout dan batchnorm berperilaku berbeda antar mode. - Baris 64 (
optimizer.zero_grad()): gradien tidak terhapus sendiri. Autograd menambahkan gradien baru ke.gradyang lama, sehingga tanpa pembersihan ini gradien terakumulasi lintas batch dan update melenceng. - Baris 65-68 (empat langkah inti): forward menghasilkan logits
(32, 2); loss skalar;backward()mengisi.grad;step()memindahkan parameter. - Baris 69-70 (log loss latih):
loss.item()menyalin angka skalar keluar dari tensor agar bisa dijumlahkan tanpa menyimpan graf komputasi. - Baris 72-77 (validasi):
model.eval()lalutorch.no_grad()— prediksi validasi tidak perlu gradien, jadi pencatatan graf dimatikan agar cepat dan hemat memori; akurasi dihitung dariargmax(dim=1)dibanding label. - Baris 79-81 (log berkala): mencetak tiap epoch ke-5 mencegah terminal kebanjiran teks pada training panjang.
- Baris 83-91 (akurasi akhir): model dievaluasi sekali lagi dalam mode
evalpada kedua subset untuk perbandingan akhir.
Membaca Hasil Training #
Dua sinyal pada keluaran patut dicermati. Pertama, loss latih turun 0,3984 → 0,0859 dan loss validasi menurun seiring — model benar-benar belajar, bukan sekadar menghafal, karena data validasi tidak pernah dipakai mengubah bobot. Kedua, akurasi akhir latih 0,977 berbanding validasi 0,961 — selisih kecil yang menandakan model belum terjebak overfitting; selisih yang melebar justru menjadi alarm, topik yang dibahas mendalam di section Regularisasi dan Generalisasi .
Rangkaian langkah pada tabel berikut merangkum akibat tiap baris kalau sampai terlewat:
| Baris kunci | Peran | Jika terlewat |
|---|---|---|
optimizer.zero_grad() | kosongkan .grad lama | gradien terakumulasi antar batch, update melenceng |
loss.backward() | isi .grad lewat autograd | parameter tak pernah berubah, loss diam |
optimizer.step() | geser parameter | model tidak belajar meski gradien tersedia |
model.train() / model.eval() | atur mode layer | dropout dan batchnorm salah perilaku |
torch.no_grad() | matikan pencatatan graf | validasi lambat, memori membengkak |
Rujukan resmi untuk siklus optimisasi — zero_grad, step, dan scheduler learning rate — tersedia di docs.pytorch.org/docs/stable/optim.html
. Model yang telah terlatih masih menyimpan satu pertanyaan: seberapa baik sebenarnya angka 0,961 itu? Chapter berikutnya membedah evaluasi secara menyeluruh — dari mode eval, akurasi, F1, hingga confusion matrix.