πŸ“ Artikel ini ditulis dalam Bahasa Indonesia
πŸ”₯ Seri Belajar PyTorch Part 2

Dataset, DataLoader & Klasifikasi Angka Tulisan Tangan

Part 2 membahas cara memuat data real, memproses dalam batch, dan membangun classifier yang bisa mengenali angka tulisan tangan (MNIST) dengan akurasi 97%+ β€” lengkap dengan visualisasi arsitektur network dan proses training.

πŸ“… Maret 2026 ⏱ 22 menit baca 🏷 PyTorch • Dataset • DataLoader • MNIST • Classification
πŸ“š Seri Belajar PyTorch:
1 2 3 4 5 6 7 8 9 10

πŸ“‘ Daftar Isi β€” Part 2

  1. Recap Part 1 β€” Apa yang sudah kita pelajari
  2. Dataset MNIST β€” 70.000 gambar angka tulisan tangan
  3. Transforms β€” Preprocessing gambar sebelum masuk model
  4. DataLoader β€” Batch, shuffle, parallel loading
  5. Membangun Classifier β€” Neural network untuk 10 kelas angka
  6. Training Loop Lengkap β€” Train + evaluate + visualisasi loss
  7. Evaluasi & Prediksi β€” Akurasi, confusion, dan prediksi real
  8. Ringkasan & Preview Part 3
πŸ”„

1. Recap Part 1

Fondasi yang sudah kita kuasai

Di Part 1, kita sudah belajar: Tensor (array multi-dimensi), Autograd (hitung gradient otomatis), dan membuat neural network pertama yang belajar y = 2x + 1 dari data. Sekarang kita naik level β€” dari data dummy ke data real: gambar tulisan tangan.

πŸ–ΌοΈ

2. Mengenal Dataset MNIST

70.000 gambar angka 0-9, masing-masing 28Γ—28 pixel grayscale

MNIST (Modified National Institute of Standards and Technology) adalah dataset benchmark paling terkenal di machine learning β€” berisi 70.000 gambar angka tulisan tangan (0-9), masing-masing berukuran 28Γ—28 pixel dalam grayscale.

πŸ–ΌοΈ Contoh Gambar MNIST β€” Angka 0 sampai 9

0 Label: 0 1 Label: 1 2 Label: 2 3 Label: 3 4 Label: 4 5 Label: 5 6 Label: 6 7 Label: 7 8 Label: 8 9 Label: 9 Setiap gambar = 28Γ—28 pixel grayscale = 784 angka (0-255)

πŸ“Š Training Set

60.000 gambar + label. Digunakan untuk melatih model. Data di-shuffle setiap epoch.

πŸ§ͺ Test Set

10.000 gambar + label. Tidak pernah dilihat model saat training. Mengukur akurasi sebenarnya.

πŸ”„

3. Transforms β€” Preprocessing Data

Konversi gambar β†’ tensor, normalisasi nilai pixel

Sebelum gambar bisa masuk ke neural network, kita perlu transform: mengubah gambar PIL menjadi tensor dan normalisasi agar training lebih stabil.

05_transforms.py β€” Preprocessing Pipeline
import torchvision.transforms as transforms # Pipeline preprocessing: jalankan berurutan transform = transforms.Compose([ transforms.ToTensor(), # PIL Image β†’ Tensor [0, 1] transforms.Normalize( (0.1307,), # mean MNIST (0.3081,) # std MNIST ) ]) # Apa yang terjadi: # 1. ToTensor(): gambar 28Γ—28 pixel (0-255) # β†’ Tensor shape [1, 28, 28] dengan nilai [0.0, 1.0] # 2. Normalize(): (pixel - mean) / std # β†’ Nilai terpusat di 0, spread Β±1 # β†’ Training lebih stabil & cepat converge

πŸŽ“ Mengapa Normalisasi?

Tanpa normalisasi, pixel bernilai 0-255 β€” range yang terlalu besar. Gradient bisa meledak atau menghilang. Normalisasi membuat semua input dalam range yang seragam (~-0.4 sampai ~2.8 untuk MNIST), sehingga optimizer bekerja jauh lebih efisien. Nilai 0.1307 dan 0.3081 adalah mean dan std yang sudah dihitung dari seluruh dataset MNIST.

πŸ“¦

4. Dataset & DataLoader

Muat data, bagi ke batch, shuffle, parallel loading

πŸ”„ DataLoader Pipeline β€” Dari Dataset ke Batch

Dataset 60.000 gambar + labels πŸ”€ Shuffle Acak urutan setiap epoch πŸ“¦ Batching batch_size = 64 [64, 1, 28, 28] 937 batches/epoch πŸ”„ Transform ToTensor() Normalize() 🧠 Model Forward Pass β†’ Prediction β†’ Loss β†’ βˆ‡ 60.000 Γ· 64 = 937 batches per epoch Γ— 10 epoch = 9.370 parameter updates DataLoader menghandle shuffle + batch + transform secara otomatis
06_dataset_dataloader.py β€” Muat MNIST
import torch from torch.utils.data import DataLoader import torchvision import torchvision.transforms as transforms # =========================== # 1. Transform pipeline # =========================== transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) # =========================== # 2. Download & muat dataset # =========================== train_dataset = torchvision.datasets.MNIST( root='./data', train=True, # Training set (60K) download=True, # Download jika belum ada transform=transform ) test_dataset = torchvision.datasets.MNIST( root='./data', train=False, # Test set (10K) download=True, transform=transform ) print(f"Training: {len(train_dataset)} gambar") print(f"Test: {len(test_dataset)} gambar") # Training: 60000 gambar # Test: 10000 gambar # =========================== # 3. Buat DataLoader # =========================== train_loader = DataLoader( train_dataset, batch_size=64, # 64 gambar per batch shuffle=True # Acak setiap epoch ) test_loader = DataLoader( test_dataset, batch_size=64, shuffle=False # Test: tidak perlu acak ) # =========================== # 4. Intip satu batch # =========================== images, labels = next(iter(train_loader)) print(f"Batch images: {images.shape}") print(f"Batch labels: {labels.shape}") # Batch images: torch.Size([64, 1, 28, 28]) # ↑batch ↑channel ↑height ↑width # Batch labels: torch.Size([64]) # ↑64 angka (0-9)

πŸŽ“ Kenapa Pakai Batch?

Melatih 60.000 gambar sekaligus = terlalu berat untuk memory. Satu gambar saja = terlalu noisy (gradient tidak stabil). Mini-batch (64) = sweet spot: cukup stabil untuk gradient yang baik, cukup kecil untuk muat di memory. Model melihat 64 gambar, hitung rata-rata error, update parameter sekali. Ulangi 937 kali = 1 epoch (semua data terlihat sekali).

🧠

5. Membangun Digit Classifier

Neural network dengan 2 hidden layers β€” input 784, output 10 kelas

🧠 Arsitektur Neural Network β€” MNIST Classifier

INPUT 784 neurons 28Γ—28 = 784 pixel values Flatten gambar 2D β†’ 1D Linear HIDDEN 1 256 neurons + ReLU max(0, x) 200,960 params Linear HIDDEN 2 128 neurons + ReLU 32,896 params Linear OUTPUT 10 kelas (0-9) Softmax 1,290 params 7 Prediksi! confidence: 98.2% Total: 235.146 parameters yang bisa belajar (trainable)
07_mnist_classifier.py β€” Full Classifier
import torch import torch.nn as nn class MNISTClassifier(nn.Module): def __init__(self): super().__init__() self.flatten = nn.Flatten() # [1,28,28] β†’ [784] self.layers = nn.Sequential( nn.Linear(784, 256), # Hidden 1 nn.ReLU(), # Aktivasi nn.Linear(256, 128), # Hidden 2 nn.ReLU(), # Aktivasi nn.Linear(128, 10) # Output (10 kelas) ) def forward(self, x): x = self.flatten(x) # Flatten gambar x = self.layers(x) # Forward through layers return x model = MNISTClassifier() # Hitung total parameters total = sum(p.numel() for p in model.parameters()) print(f"Total parameters: {total:,}") # Total parameters: 235,146

πŸŽ“ Apa Itu ReLU?

ReLU (Rectified Linear Unit) = max(0, x). Jika input positif β†’ loloskan. Jika negatif β†’ jadikan 0. Ini "fungsi aktivasi" yang membuat network bisa belajar pola non-linear (bukan cuma garis lurus). Tanpa ReLU, network sekompleks apapun hanya bisa menghitung fungsi linear β€” yang tidak cukup untuk mengenali gambar.

πŸ‹οΈ

6. Training Loop Lengkap

Train 10 epoch + evaluasi setiap epoch + track loss
08_training_loop.py β€” Train + Evaluate
import torch import torch.nn as nn from torch.utils.data import DataLoader import torchvision import torchvision.transforms as transforms # === Setup (dari section sebelumnya) === transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) train_dataset = torchvision.datasets.MNIST('./data', train=True, download=True, transform=transform) test_dataset = torchvision.datasets.MNIST('./data', train=False, transform=transform) train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True) test_loader = DataLoader(test_dataset, batch_size=64) # === Model, Loss, Optimizer === model = MNISTClassifier() # Model dari section 5 loss_fn = nn.CrossEntropyLoss() # Untuk klasifikasi multi-kelas optimizer = torch.optim.Adam(model.parameters(), lr=0.001) # === Training Function === def train_one_epoch(model, loader, loss_fn, optimizer): model.train() # Mode training total_loss = 0 for images, labels in loader: pred = model(images) # Forward pass loss = loss_fn(pred, labels) # Hitung loss optimizer.zero_grad() # Reset gradient loss.backward() # Hitung gradient optimizer.step() # Update parameter total_loss += loss.item() return total_loss / len(loader) # === Evaluation Function === def evaluate(model, loader): model.eval() # Mode evaluasi correct = 0 total = 0 with torch.no_grad(): # Matikan gradient (hemat memory) for images, labels in loader: pred = model(images) _, predicted = pred.max(1) # Ambil kelas dengan skor tertinggi total += labels.size(0) correct += (predicted == labels).sum().item() return 100.0 * correct / total # === TRAINING! πŸ”₯ === for epoch in range(10): loss = train_one_epoch(model, train_loader, loss_fn, optimizer) acc = evaluate(model, test_loader) print(f"Epoch {epoch+1:2d} | Loss: {loss:.4f} | Acc: {acc:.1f}%") # Output: # Epoch 1 | Loss: 0.3421 | Acc: 95.2% # Epoch 2 | Loss: 0.1543 | Acc: 96.4% # Epoch 3 | Loss: 0.1089 | Acc: 96.9% # Epoch 4 | Loss: 0.0842 | Acc: 97.2% # Epoch 5 | Loss: 0.0681 | Acc: 97.4% # Epoch 6 | Loss: 0.0554 | Acc: 97.5% # Epoch 7 | Loss: 0.0449 | Acc: 97.6% # Epoch 8 | Loss: 0.0378 | Acc: 97.7% # Epoch 9 | Loss: 0.0310 | Acc: 97.7% # Epoch 10 | Loss: 0.0254 | Acc: 97.8% πŸŽ‰

πŸ“‰ Training Loss & Accuracy β€” 10 Epochs

0.0 0.1 0.2 0.3 0.4 2 4 6 8 10 Epoch Loss: 0.025 Acc: 97.8% Loss (↓ baik) Accuracy (↑ baik)
πŸŽ‰ 97.8% Akurasi! Dari 10.000 gambar test, model kita benar mengenali 9.780 gambar. Hanya salah 220 β€” dan ini hanya dengan 2 hidden layers sederhana. Di Part 3 kita akan naik ke 99%+ dengan CNN (Convolutional Neural Network).
πŸ”

7. Evaluasi & Prediksi

Simpan model + prediksi gambar baru
09_evaluate_predict.py β€” Prediksi & Save
# =========================== # 1. Prediksi gambar individual # =========================== # Ambil satu gambar dari test set image, true_label = test_dataset[0] # Prediksi model.eval() with torch.no_grad(): output = model(image.unsqueeze(0)) # Tambah batch dimension probs = torch.nn.functional.softmax(output, dim=1) pred = output.argmax(dim=1).item() conf = probs[0][pred].item() * 100 print(f"True label: {true_label}") print(f"Predicted: {pred}") print(f"Confidence: {conf:.1f}%") # True label: 7 # Predicted: 7 # Confidence: 99.8% βœ… # =========================== # 2. Simpan model untuk nanti # =========================== torch.save(model.state_dict(), 'mnist_model.pth') print("Model tersimpan! πŸ’Ύ") # Muat kembali: loaded_model = MNISTClassifier() loaded_model.load_state_dict(torch.load('mnist_model.pth')) loaded_model.eval() print("Model dimuat kembali! βœ…")
πŸ“

8. Ringkasan Part 2

Konsep baru yang kita kuasai
KonsepApa ItuKode Kunci
DatasetKoleksi data + label. MNIST: 70K gambar angka.datasets.MNIST(root, train, transform)
TransformPipeline preprocessing: gambar β†’ tensor β†’ normalizetransforms.Compose([ToTensor(), Normalize()])
DataLoaderBagi data ke batch, shuffle, parallel loadingDataLoader(dataset, batch_size=64, shuffle=True)
FlattenUbah gambar 2D β†’ vektor 1D untuk masuk ke Linearnn.Flatten() # [1,28,28] β†’ [784]
CrossEntropyLossLoss untuk klasifikasi multi-kelas (10 angka)nn.CrossEntropyLoss()
Adam OptimizerOptimizer adaptif β€” lebih baik dari SGD untuk kebanyakan kasusoptim.Adam(model.parameters(), lr=0.001)
model.train()Aktifkan mode training (dropout, batchnorm aktif)model.train()
model.eval()Mode evaluasi (dropout off, batchnorm freeze)model.eval()
torch.save()Simpan model ke file untuk dipakai nantitorch.save(model.state_dict(), 'model.pth')
softmaxKonversi output raw β†’ probabilitas (jumlah = 1.0)F.softmax(output, dim=1)
πŸ”₯
Tech Review Desk β€” Seri Belajar PyTorch
Tutorial hands-on. Sumber: pytorch.org docs, PyTorch Foundation, DigitalOcean, GeeksforGeeks, Nextjournal. PyTorch v2.7+ (2026).
πŸ“§ rominur@gmail.com  β€’  ✈️ t.me/Jekardah_AI β€” For collaboration & discussion