Menguasai fondasi deep learning dengan PyTorch: autograd dan backpropagation, Dataset & DataLoader yang efisien, training loop yang dikontrol penuh, perpindahan ke GPU, serta praktik melatih CNN untuk gambar dan jaringan untuk data tabular

Setelah 11 episode membangun fondasi MLE di ranah ML klasik — feature engineering, eksperimen, serving, monitoring — sekarang kita masuk ke jantung deep learning: PyTorch. Kalian mungkin berpikir "saya bukan peneliti, kenapa harus paham ini?" Karena pada 2026, hampir semua sistem ML produksi yang berdampak besar — dari rekomendasi sampai LLM — dibangun di atas deep learning.
Sebagai MLE, kalian tidak perlu menurunkan rumus autograd dari nol, tetapi wajib memahami cara kerja training loop: apa itu forward, backward, gradient descent, kenapa optimizer berperilaku begitu, dan bagaimana dataset dialirkan ke GPU. Episode ini membangun pemahaman itu dengan kode yang bisa langsung kalian jalankan.
Tensor adalah array multidimensi dengan GPU support dan autograd — kemampuan menghitung gradien secara otomatis. Di sinilah letak keajaiban training loop:
import torch
x = torch.tensor(2.0, requires_grad=True)
y = x**2 + 3 * x
y.backward()
print(x.grad) # turunan dari y terhadap x = 2x + 3 = 7requires_grad=True memberi tahu PyTorch untuk melacak operasi; backward() menghitung gradien; x.grad menyimpan hasilnya. Untuk model besar, ini artinya kita tidak pernah menulis backpropagation manual — PyTorch melacak komputasi dan menghitung gradien lewat aturan rantai otomatis.
Model dilatih dengan mengalirkan data dalam batch. PyTorch menyediakan Dataset (menyediakan sampel) dan DataLoader (mengatur batch, shuffle, multiprocessing):
import torch
from torch.utils.data import Dataset, DataLoader
class TabularDataset(Dataset):
def __init__(self, X, y):
self.X = torch.tensor(X, dtype=torch.float32)
self.y = torch.tensor(y, dtype=torch.float32)
def __len__(self):
return len(self.X)
def __getitem__(self, idx):
return self.X[idx], self.y[idx]
loader = DataLoader(TabularDataset(X_train, y_train), batch_size=128, shuffle=True)batch_size, shuffle, dan num_workers adalah knop performa yang menentukan kecepatan training. shuffle=True penting agar urutan sampel tidak menginduksi bias saat SGD.
MLE yang baik bisa menulis training loop dengan tangan — karena framework-level (seperti Trainer Hugging Face) pada akhirnya memakai loop ini, dan debugging butuh pemahaman di level ini:
import torch.nn as nn
import torch.optim as optim
model = nn.Sequential(
nn.Linear(X_train.shape[1], 64),
nn.ReLU(),
nn.Linear(64, 1),
)
criterion = nn.BCEWithLogitsLoss()
optimizer = optim.Adam(model.parameters(), lr=1e-3)
for epoch in range(10):
model.train()
total_loss = 0.0
for Xb, yb in loader:
optimizer.zero_grad()
logits = model(Xb).squeeze()
loss = criterion(logits, yb)
loss.backward()
optimizer.step()
total_loss += loss.item()
print(f"epoch {epoch}: loss {total_loss / len(loader):.4f}")Empat langkah wajib per batch: zero_grad (hapus gradien lama), forward (hitung prediksi + loss), backward (hitung gradien), step (perbarui bobot). Urutan ini tidak boleh diacak — zero_grad yang terlupa akan menumpuk gradien dari batch sebelumnya.
Convolutional Neural Network mengekstrak pola spasial dari gambar. Arsitektur standar: beberapa blok Conv + ReLU + MaxPool, lalu flatten dan layer fully connected:
import torch.nn as nn
class SimpleCNN(nn.Module):
def __init__(self, num_classes=10):
super().__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 16, kernel_size=3, padding=1),
nn.ReLU(),
nn.MaxPool2d(2),
nn.Conv2d(16, 32, kernel_size=3, padding=1),
nn.ReLU(),
nn.MaxPool2d(2),
)
self.classifier = nn.Sequential(
nn.Flatten(),
nn.Linear(32 * 8 * 8, 128),
nn.ReLU(),
nn.Linear(128, num_classes),
)
def forward(self, x):
return self.classifier(self.features(x))Dimensi harus dihitung teliti — kesalahan paling umum di CNN adalah ukuran tensor yang tidak cocok antara convolution dan linear layer. Gunakan .view() atau biarkan nn.Flatten() menghitungnya.
Kode di atas berjalan di CPU. Untuk GPU, semua objek (model, data) harus dipindah:
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)
for Xb, yb in loader:
Xb, yb = Xb.to(device), yb.to(device)
# ... training loop sama seperti sebelumnyaPola yang wajib diikuti: data selalu di-move ke device yang sama dengan model. Lupa memindahkan salah satu = error "expected device cuda but got device cpu" yang paling sering ditanyakan.
Tip
Gunakan pola device = torch.device("cuda" if torch.cuda.is_available() else "cpu") di semua skrip agar kode otomatis berjalan di GPU saat tersedia dan tetap di CPU saat tidak. Ini membuat eksperimen kalian portable antara laptop dan server GPU.
1e-3, dan coba rentang 1e-4..1e-2.torch.manual_seed(42) agar reproducible (baca episode 8).model.state_dict() tiap epoch terbaik, bukan hanya di akhir.best_loss = float("inf")
for epoch in range(epochs):
# ... train loop
val_loss = evaluate(model, val_loader)
if val_loss < best_loss:
best_loss = val_loss
torch.save(model.state_dict(), f"checkpoints/best_epoch_{epoch}.pt")| Pitfall | Dampak | Pencegahan |
|---|---|---|
Lupa optimizer.zero_grad() | Gradien menumpuk, training kacau | zero_grad tiap batch |
| Model di CPU, data di GPU (atau sebaliknya) | Runtime device error | Move keduanya ke device sama |
| Dimensi tensor tidak cocok | Shape mismatch error | Cek output tiap layer |
| Loss terus NaN | Learning rate terlalu besar / data belum dinormalisasi | Turunkan LR, normalisasi input |
| Tanpa validation per epoch | Overfit tidak terdeteksi | Evaluasi per epoch, simpan best |
Pada episode 12 ini, kalian telah menguasai fondasi deep learning dengan PyTorch:
Di episode 13 selanjutnya kita akan membahas Transformers & LLM Fundamentals — arsitektur transformer, Hugging Face ecosystem, tokenizer, dan langkah pertama fine-tuning model bahasa dengan Transformers. Sampai jumpa di episode 13!