Referensi cepat PyTorch untuk tensor, autograd, DataLoader, model, training loop, GPU, checkpoint, dan debugging. Buat kamu yang belajar machine learning dan deep learning dengan Python.
PyTorch adalah library Python untuk komputasi tensor dan deep learning. Kamu akan paling sering bertemu tiga objek: Tensor untuk data, nn.Module untuk model, serta Optimizer untuk memperbarui bobot. Jangan mulai dengan notebook yang penuh kode dulu. Pastikan instalasi, perangkat komputasi, dan versi library jelas sejak awal.
Halaman selector resmi di pytorch.org memberi perintah instalasi sesuai OS, Python, CUDA, dan jenis package manager. Untuk CPU atau environment sederhana, ini cukup:
python -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
pip install torch torchvision torchaudioKalau mau memakai GPU NVIDIA, salin perintah dari selector resmi PyTorch yang cocok dengan driver dan CUDA pada mesinmu. Jangan asal memasang toolkit CUDA terpisah lalu berharap PyTorch otomatis memakainya. Wheel PyTorch membawa runtime yang dibutuhkan untuk konfigurasi yang didukung. Cek hasilnya:
import torch
print(torch.__version__)
print(torch.cuda.is_available())
print(torch.cuda.get_device_name(0) if torch.cuda.is_available() else "CPU")torch.cuda.is_available() bernilai True belum berarti semua kode aman dari masalah memori. Batch terlalu besar, sequence terlalu panjang, atau model yang dipindah dua kali tetap bisa memicu out-of-memory.
Simpan device dalam satu variabel. Kebiasaan kecil ini mencegah input tertinggal di CPU ketika model sudah ada di GPU.
import random
import numpy as np
import torch
seed = 42
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
if torch.cuda.is_available():
torch.cuda.manual_seed_all(seed)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device)Seed membantu eksperimen berulang, tapi tidak menjamin hasil bit demi bit pada semua GPU dan semua operasi. Untuk kebutuhan yang sangat ketat, aktifkan algoritme deterministik lalu terima bahwa beberapa operasi bisa lebih lambat atau menolak jalan.
torch.use_deterministic_algorithms(True)Tensor mirip ndarray dari NumPy, tetapi bisa dihitung di GPU dan ditautkan ke grafik autograd. Bentuk tensor dibaca dari kiri ke kanan. Tensor gambar umum punya bentuk [batch, channel, height, width]. Tensor teks setelah tokenisasi biasanya [batch, sequence_length].
import torch
x = torch.tensor([[1, 2], [3, 4]], dtype=torch.float32)
zeros = torch.zeros(3, 4)
ones = torch.ones_like(zeros)
noise = torch.randn(2, 5)
ids = torch.arange(0, 10, 2)
identity = torch.eye(3)
print(x.shape) # torch.Size([2, 2])
print(x.dtype) # torch.float32
print(noise.device) # cpuPilih tipe data dengan sengaja. float32 adalah default yang aman untuk training. float16 dan bfloat16 menghemat memori di hardware yang mendukungnya, tetapi biasanya dipakai lewat mixed precision, bukan dengan mengubah semua tensor secara membabi buta. Label kelas untuk CrossEntropyLoss harus bertipe torch.long.
x = torch.arange(24).reshape(2, 3, 4)
first_batch = x[0] # [3, 4]
last_column = x[:, :, -1] # [2, 3]
flat = x.flatten(start_dim=1) # [2, 12]
transposed = x.transpose(1, 2) # [2, 4, 3]
scores = torch.tensor([[1.0, 2.0, 3.0], [2.0, 1.0, 0.0]])
probabilities = torch.softmax(scores, dim=1)
predictions = scores.argmax(dim=1)
print(probabilities.sum(dim=1)) # tensor([1., 1.])view() dan reshape() sama-sama sering dipakai untuk mengubah bentuk. Bedanya, view() meminta data tersimpan kontigu di memori. Setelah transpose() atau permute(), pakai reshape() atau panggil .contiguous() sebelum view() agar tidak kena error yang membingungkan.
x = torch.randn(2, 3, 4)
y = x.transpose(1, 2)
flat = y.reshape(2, -1)Untuk menggabungkan data, bedakan cat dan stack. torch.cat menyambung pada dimensi yang sudah ada. torch.stack menambah dimensi baru.
a = torch.ones(2, 3)
b = torch.zeros(2, 3)
joined = torch.cat([a, b], dim=0) # [4, 3]
batched = torch.stack([a, b], dim=0) # [2, 2, 3]Model dan semua tensor yang ikut operasi harus berada di device yang sama. Pindahkan batch tepat sebelum forward pass.
features = torch.randn(8, 10)
features = features.to(device)
# Untuk input Tensor biasa
model_input = features.to(device, non_blocking=True)non_blocking=True baru memberi manfaat ketika sumber data memakai pinned memory dan transfer bisa overlap dengan kerja GPU. Jangan menganggap flag ini sebagai obat untuk training lambat.
Operasi dengan akhiran underscore, misalnya add_() atau relu_(), memodifikasi tensor di tempat. Ini hemat alokasi pada kasus tertentu, tetapi bisa merusak nilai yang dibutuhkan autograd untuk backward pass. Saat kamu masih belajar, pilih operasi biasa kecuali kamu paham tensor mana yang boleh dimutasi.
x = torch.randn(3, requires_grad=True)
y = x * 2
z = y.relu() # aman untuk contoh ini
loss = z.sum()
loss.backward()
print(x.grad)PyTorch membangun grafik komputasi saat operasi melibatkan tensor dengan requires_grad=True. Ketika memanggil loss.backward(), autograd menjalankan chain rule dan menaruh gradient pada .grad milik leaf tensor, biasanya parameter model.
w = torch.tensor(2.0, requires_grad=True)
x = torch.tensor(3.0)
target = torch.tensor(10.0)
prediction = w * x
loss = (prediction - target).pow(2)
loss.backward()
print(w.grad) # turunan loss terhadap wGradient terakumulasi. Ini sering bikin pemula bertanya kenapa loss makin aneh setelah beberapa iterasi. Jawabannya biasanya karena gradient lama belum dibersihkan. Panggil optimizer.zero_grad(set_to_none=True) sebelum backward. set_to_none=True mengurangi pekerjaan reset dan membantu mendeteksi parameter yang memang tidak mendapat gradient.
optimizer.zero_grad(set_to_none=True)
loss = criterion(model(inputs), targets)
loss.backward()
optimizer.step()Untuk validasi dan inference, pakai torch.inference_mode() bila kamu tidak butuh autograd. Mode ini mengurangi overhead lebih jauh daripada torch.no_grad() pada inference biasa. Jika kode di dalam blok perlu mengaktifkan gradient lagi atau memanipulasi tensor secara khusus, torch.no_grad() lebih fleksibel.
model.eval()
with torch.inference_mode():
logits = model(validation_inputs)
predicted_class = logits.argmax(dim=1)model.eval() tidak mematikan gradient. Ia mengubah perilaku layer seperti dropout dan batch normalization. Jadi untuk evaluasi yang benar, gunakan keduanya: model.eval() dan context manager tanpa gradient.
Transfer learning sering dimulai dengan backbone yang dibekukan, lalu hanya classifier head yang dilatih. Pastikan optimizer hanya menerima parameter yang masih dapat dioptimasi.
for parameter in model.features.parameters():
parameter.requires_grad = False
optimizer = torch.optim.AdamW(
(p for p in model.parameters() if p.requires_grad),
lr=1e-3,
weight_decay=1e-2,
)Kalau nanti kamu membuka kembali backbone, buat ulang optimizer atau tambahkan parameter group. Parameter yang tidak ada di optimizer tidak akan berubah walaupun gradient-nya tersedia.
Dataset mendefinisikan cara mengambil satu sampel. DataLoader mengelompokkan sampel menjadi batch, bisa mengacak urutan, dan bisa menjalankan worker untuk menyiapkan data di belakang layar.
from torch.utils.data import DataLoader, TensorDataset
features = torch.randn(1_000, 20)
labels = torch.randint(0, 3, (1_000,), dtype=torch.long)
dataset = TensorDataset(features, labels)
loader = DataLoader(
dataset,
batch_size=32,
shuffle=True,
num_workers=0,
pin_memory=torch.cuda.is_available(),
)
for batch_features, batch_labels in loader:
print(batch_features.shape, batch_labels.shape)
breakshuffle=True lazim untuk training agar urutan data tidak ikut membentuk bias batch. Untuk validation dan test, pakai shuffle=False supaya debugging dan reproduksi lebih mudah. num_workers=0 adalah titik awal yang aman, terutama di notebook dan Windows. Tambah worker hanya setelah kamu mengukur bottleneck input. Terlalu banyak worker juga bisa lebih lambat.
from torch.utils.data import Dataset
class ReviewDataset(Dataset):
def __init__(self, texts, labels, tokenizer):
self.texts = texts
self.labels = labels
self.tokenizer = tokenizer
def __len__(self):
return len(self.texts)
def __getitem__(self, index):
encoded = self.tokenizer(
self.texts[index],
truncation=True,
max_length=128,
padding="max_length",
return_tensors="pt",
)
return {
"input_ids": encoded["input_ids"].squeeze(0),
"attention_mask": encoded["attention_mask"].squeeze(0),
"labels": torch.tensor(self.labels[index], dtype=torch.long),
}Untuk teks panjang, padding setiap contoh sampai max_length bisa boros. Pakai data collator dengan dynamic padding bila tokenizer dan model kamu mendukungnya. Itu membuat panjang batch mengikuti contoh terpanjang dalam batch tersebut.
Turunkan model dari nn.Module, daftarkan layer di __init__, lalu tulis aliran data di forward. Jangan panggil forward() secara langsung saat memakai model. Gunakan model(inputs) supaya hook dan mekanisme nn.Module tetap berjalan.
import torch.nn as nn
class TabularClassifier(nn.Module):
def __init__(self, input_features: int, num_classes: int):
super().__init__()
self.network = nn.Sequential(
nn.Linear(input_features, 128),
nn.ReLU(),
nn.Dropout(p=0.2),
nn.Linear(128, num_classes),
)
def forward(self, x):
return self.network(x)
model = TabularClassifier(input_features=20, num_classes=3).to(device)
print(sum(p.numel() for p in model.parameters()))Output layer untuk klasifikasi multiclass harus memberi logits mentah. Jangan masukkan Softmax sebelum nn.CrossEntropyLoss, karena loss itu sudah melakukan log-softmax secara numerik stabil. Untuk binary classification satu logit per sampel, pakai nn.BCEWithLogitsLoss dan jangan tambahkan Sigmoid sebelum loss.
| Tugas | Output model | Loss umum | Target |
|---|---|---|---|
| Regresi | Nilai kontinu | nn.MSELoss atau nn.L1Loss | float |
| Binary classification | Satu logit | nn.BCEWithLogitsLoss | float 0 atau 1 |
| Multiclass classification | Satu logit per kelas | nn.CrossEntropyLoss | indeks kelas long |
| Segmentasi multiclass | Logit per kelas per piksel | nn.CrossEntropyLoss | indeks kelas per piksel |
Layer standar PyTorch sudah punya inisialisasi default yang layak. Jangan mengganti semuanya tanpa alasan. Untuk eksperimen tertentu, kamu bisa menerapkan inisialisasi secara eksplisit.
def init_linear(module):
if isinstance(module, nn.Linear):
nn.init.kaiming_normal_(module.weight, nonlinearity="relu")
if module.bias is not None:
nn.init.zeros_(module.bias)
model.apply(init_linear)
for name, parameter in model.named_parameters():
print(name, parameter.shape, parameter.requires_grad)Training loop inti punya urutan yang tetap: mode train, ambil batch, pindah device, nolkan gradient, forward, hitung loss, backward, update optimizer. Kalau satu langkah hilang, hasilnya hampir pasti salah.
import torch
import torch.nn as nn
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.AdamW(model.parameters(), lr=3e-4, weight_decay=1e-2)
for epoch in range(10):
model.train()
total_loss = 0.0
total_correct = 0
total_examples = 0
for inputs, targets in loader:
inputs = inputs.to(device, non_blocking=True)
targets = targets.to(device, non_blocking=True)
optimizer.zero_grad(set_to_none=True)
logits = model(inputs)
loss = criterion(logits, targets)
loss.backward()
optimizer.step()
total_loss += loss.item() * inputs.size(0)
total_correct += (logits.argmax(dim=1) == targets).sum().item()
total_examples += inputs.size(0)
print(
f"epoch={epoch + 1} "
f"loss={total_loss / total_examples:.4f} "
f"accuracy={total_correct / total_examples:.3f}"
)Jangan menjumlahkan loss.item() lalu membaginya dengan jumlah batch ketika batch terakhir ukurannya berbeda. Mengalikan loss dengan ukuran batch, seperti kode di atas, memberi rata-rata per contoh yang benar.
Validation loader tidak boleh ikut memperbarui bobot. Jalankan setelah satu epoch agar kamu bisa membandingkan train loss dengan validation loss dan melihat gejala overfitting lebih awal.
def evaluate(model, dataloader, criterion, device):
model.eval()
total_loss = 0.0
total_correct = 0
total_examples = 0
with torch.inference_mode():
for inputs, targets in dataloader:
inputs = inputs.to(device, non_blocking=True)
targets = targets.to(device, non_blocking=True)
logits = model(inputs)
loss = criterion(logits, targets)
total_loss += loss.item() * inputs.size(0)
total_correct += (logits.argmax(dim=1) == targets).sum().item()
total_examples += inputs.size(0)
return total_loss / total_examples, total_correct / total_examplesAccuracy kadang menipu, misalnya kelas positif hanya dua persen dari data. Untuk kasus seperti fraud, penyakit, atau moderation, hitung precision, recall, F1, PR-AUC, lalu lihat confusion matrix. Metrik harus mengikuti biaya kesalahan pada produkmu.
Gradient clipping berguna saat training tidak stabil, khususnya RNN, transformer, atau loss yang sesekali melonjak. Scheduler mengubah learning rate selama training. Keduanya bukan hiasan wajib. Tambahkan setelah baseline loop sudah berjalan dan kamu punya alasan dari log training.
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(
optimizer,
T_max=10,
)
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
optimizer.step()
scheduler.step()Untuk scheduler berbasis validation metric seperti ReduceLROnPlateau, panggil scheduler.step(validation_loss), bukan tanpa argumen.
GPU cepat ketika data dan operasi cukup besar, tetapi transfer CPU ke GPU, preprocessing Python, dan batch terlalu kecil bisa menahan seluruh pipeline. Ukur sebelum mengubah banyak setting.
Di GPU CUDA, automatic mixed precision memakai torch.autocast dan torch.amp.GradScaler. GradScaler membantu gradient kecil tetap terwakili saat memakai float16.
scaler = torch.amp.GradScaler("cuda", enabled=device.type == "cuda")
for inputs, targets in loader:
inputs, targets = inputs.to(device), targets.to(device)
optimizer.zero_grad(set_to_none=True)
with torch.autocast(
device_type=device.type,
dtype=torch.float16,
enabled=device.type == "cuda",
):
logits = model(inputs)
loss = criterion(logits, targets)
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()Jangan langsung menganggap mixed precision pasti lebih cepat. Di CPU ia tidak cocok dengan contoh ini, dan beberapa GPU atau model lebih sesuai dengan bfloat16. Cek dokumentasi hardware sebelum memilih dtype.
torch.compile dapat mengompilasi bagian model untuk mengurangi overhead Python dan mempercepat beberapa beban kerja. Gunakan setelah model benar secara fungsional. Error dari graph break atau shape yang sering berubah lebih susah dibaca ketika kamu belum punya baseline.
if hasattr(torch, "compile"):
model = torch.compile(model)Profiling lebih berguna daripada tebakan. torch.profiler bisa menunjukkan apakah waktu habis di DataLoader, operasi tertentu, atau transfer memory.
with torch.profiler.profile(activities=[
torch.profiler.ProfilerActivity.CPU,
torch.profiler.ProfilerActivity.CUDA,
]) as prof:
logits = model(torch.randn(32, 20, device=device))
print(prof.key_averages().table(sort_by="self_cuda_time_total", row_limit=10))Jalankan blok profiler hanya jika CUDA tersedia. Untuk diagnosis penggunaan memori GPU, cek torch.cuda.memory_allocated() dan torch.cuda.memory_reserved(). Memanggil torch.cuda.empty_cache() setiap batch biasanya memperlambat training dan tidak memperbaiki tensor yang masih direferensikan.
Simpan state_dict, bukan seluruh objek model Python. Cara ini lebih portabel dan tidak bergantung pada lokasi class saat file dimuat kembali.
checkpoint = {
"epoch": epoch,
"model_state": model.state_dict(),
"optimizer_state": optimizer.state_dict(),
"validation_loss": validation_loss,
}
torch.save(checkpoint, "checkpoint.pt")Muat checkpoint dengan class model yang sama. Untuk checkpoint dari sumber yang tidak kamu percaya, gunakan weights_only=True pada PyTorch modern agar proses load tidak mendeserialisasi objek Python sembarangan.
model = TabularClassifier(input_features=20, num_classes=3).to(device)
checkpoint = torch.load("checkpoint.pt", map_location=device, weights_only=True)
model.load_state_dict(checkpoint["model_state"])
model.eval()Jika menyimpan model yang sudah dibungkus torch.compile, simpan state dict dari model asli bila struktur key tidak sesuai saat load. Jangan menganggap checkpoint lintas versi selalu kompatibel. Catat versi PyTorch, dataset revision, commit kode, hyperparameter, dan metrik pada setiap run yang kamu anggap penting.
classes = ["negatif", "netral", "positif"]
model.eval()
inputs = torch.randn(4, 20, device=device)
with torch.inference_mode():
logits = model(inputs)
probabilities = torch.softmax(logits, dim=1)
indices = probabilities.argmax(dim=1)
for index, probability in zip(indices.cpu().tolist(), probabilities.cpu()):
print(classes[index], float(probability[index]))Probabilitas tinggi bukan jaminan model benar. Neural network sering overconfident. Jika keputusan terkait uang, keamanan, atau kesehatan, evaluasi calibration dan tetapkan aturan fallback di luar model.
Masalah PyTorch paling sering jatuh ke tiga kelompok: shape salah, device atau dtype tidak sama, lalu mode train/eval keliru. Cetak nilai dan bentuk pada batch pertama sebelum menjalankan training berjam-jam.
inputs, targets = next(iter(loader))
print(inputs.shape, inputs.dtype, inputs.device)
print(targets.shape, targets.dtype, targets.min().item(), targets.max().item())
print(next(model.parameters()).device)| Gejala | Cek pertama | Perbaikan umum |
|---|---|---|
Loss nan | learning rate, input, gradient | turunkan learning rate, cek torch.isfinite, pakai clipping |
| Error device | parameter dan batch | pindahkan keduanya ke device yang sama |
| Error target CrossEntropy | shape dan dtype label | gunakan kelas indeks long, bukan one-hot float |
| Validation buruk, train bagus | split dan augmentasi | cek data leakage, regularisasi, early stopping |
| GPU tidak penuh | DataLoader dan transfer | ukur input pipeline, coba pinned memory atau worker lebih banyak |
| Loss tidak turun | label, loss, mode | pastikan target benar, model train(), optimizer memegang parameter |
Saat loss menjadi nan, jangan lanjut training sambil berharap pulih. Hentikan pada batch pertama yang gagal, lalu cek input dan output.
if not torch.isfinite(loss):
raise RuntimeError(f"Loss tidak valid: {loss.item()}")| Istilah | Arti singkat |
|---|---|
| Tensor | Array multidimensi yang membawa dtype, shape, device, dan bisa ikut autograd. |
| Batch | Sekelompok contoh yang diproses dalam satu forward dan backward pass. |
| Epoch | Satu putaran penuh melewati data training. |
| Logit | Output mentah model sebelum sigmoid atau softmax. |
| Loss | Angka yang mengukur seberapa jauh prediksi dari target dan dipakai untuk training. |
| Gradient | Turunan loss terhadap parameter. Optimizer memakai nilainya untuk mengubah bobot. |
| Backward pass | Proses menghitung gradient melalui grafik komputasi. |
| Autograd | Mesin diferensiasi otomatis PyTorch. |
| State dict | Dictionary berisi parameter dan buffer model atau optimizer. |
| Overfitting | Model sangat cocok pada data training, lalu buruk pada data yang belum pernah dilihat. |
| Mixed precision | Training atau inference dengan gabungan dtype presisi berbeda demi memori dan kecepatan. |
| Data leakage | Informasi dari validation atau test masuk ke training, sehingga metrik terlihat palsu. |
Login atau daftar akun gratis untuk membaca cheat sheet ini.