Model tidak mempelajari seluruh dataset sekaligus; dataset dipotong menjadi batch yang digilirkan melalui forward, backward, dan pembaruan parameter. Tiga istilah pada proses itu — batch, iterasi, epoch — sering tertukar padahal maknanya berbeda, dan ukuran batch menyimpan trade-off yang memengaruhi kecepatan, memori, hingga kualitas gradien. Chapter ini memetakan ketiganya, membedah trade-offnya, lalu membuktikannya dengan training identik pada batch 8, 32, dan 128.

Batch, Iterasi, dan Epoch #

  • Batch adalah potongan data yang diproses model sebelum satu pembaruan parameter; ukurannya disebut batch size.
  • Iterasi adalah satu putaran forward–backward–step() pada satu batch.
  • Epoch adalah satu putaran penuh menelusuri seluruh data latih.

Aritmetikanya lurus: dataset 600 sampel dengan batch size 32 menghasilkan 19 iterasi per epoch — 18 batch penuh ditambah satu batch sisa berisi 24 sampel, karena DataLoader tidak membuang sisa yang tidak genap. Lima epoch berarti model telah melihat setiap sampel tepat lima kali.

Full Batch, Mini-Batch, dan Stochastic #

ModeUkuran batchKarakteristik
Full batchseluruh data latihgradien paling halus (eksak), satu pembaruan per epoch, memori besar
Mini-batch16–512 (umumnya)kompromi industri: gradien cukup stabil, pembaruan cukup sering
Stochastic (online)1 sampelpembaruan paling sering, gradien paling bergejolak, tidak efisien pada komputasi paralel

Full-batch gradient descent yang kita hitung manual di chapter Gradient Descent memakai gradien eksak seluruh data; mini-batch menggantinya dengan perkiraan dari potongan data, dan variasi antar batch itu justru berguna — membantu model lolos dari titik jenuh dan kerap menghasilkan generalisasi lebih baik daripada gradien eksak.

Trade-off Ukuran Batch #

AspekBatch kecilBatch besar
Kualitas gradienbergejolak antar iterasisemakin halus dan mendekati gradien eksak
Pembaruan per epochbanyak (600/8 = 75)sedikit (600/128 = 5)
Memori per iterasikecilbesar, bisa melebihi kapasitas GPU
Waktu per epochlebih lama (iterasi banyak)lebih cepat (paralelisme efektif)
Kemajuan per epochcenderung lebih jauhcenderung lebih pendek pada epoch sama

Dua baris terakhir tabel tampak saling bertentangan dan memang demikian adanya: batch kecil mendapat lebih banyak kesempatan memperbaiki bobot dalam satu epoch, sedangkan batch besar menikmati gradien yang lebih dapat dipercaya. Tidak ada pemenang mutlak — perlu diukur.

Contoh: Batch 8, 32, dan 128 #

Berkas banding_batch.py melatih model yang identik tiga kali: dataset make_moons 600 sampel, MLP [2, 16, 2], SGD lr=0.1, 30 epoch, inisialisasi bobot sama. Satu-satunya variabel yang berubah adalah batch size:

python
"""Perbandingan batch size 8, 32, dan 128 pada training yang sama.

Dataset make_moons 600 sampel, MLP [2, 16, 2], SGD lr=0.1, 30 epoch.
Hanya batch size yang berubah. Diamati: jumlah iterasi per epoch, laju
penurunan loss, getaran loss antar batch, akurasi akhir, dan waktu.
Semua contoh diuji pada CPU (torch 2.14.1+cpu).
"""

import time

import torch
import torch.nn as nn
from sklearn.datasets import make_moons
from torch.utils.data import DataLoader, TensorDataset

torch.manual_seed(42)

# ---------- 1. Dataset dan komponen yang disamakan ----------
X, y = make_moons(n_samples=600, noise=0.20, random_state=42)
X_t = torch.tensor(X, dtype=torch.float32)
y_t = torch.tensor(y, dtype=torch.long)

JUMLAH_EPOCH = 30
EPOCH_CETAK = (1, 10, 20, 30)


def buat_model():
    """Model dengan inisialisasi bobot identik untuk tiap batch size."""
    torch.manual_seed(7)
    return nn.Sequential(
        nn.Linear(2, 16),
        nn.ReLU(),
        nn.Linear(16, 2),
    )


def latih(batch_size):
    """Latih model dengan batch size tertentu, kembalikan ringkasan."""
    model = buat_model()
    loss_fn = nn.CrossEntropyLoss()
    optimizer = torch.optim.SGD(model.parameters(), lr=0.1)

    dataset = TensorDataset(X_t, y_t)
    loader = DataLoader(
        dataset,
        batch_size=batch_size,
        shuffle=True,
        generator=torch.Generator().manual_seed(42),
    )

    mulai = time.perf_counter()
    jejak_loss = {}
    batch_terakhir = None
    loss_per_batch = None
    for epoch in range(1, JUMLAH_EPOCH + 1):
        if epoch == JUMLAH_EPOCH:
            loss_per_batch = []          # getaran batch pada epoch terakhir
        total_loss = 0.0
        for X_batch, y_batch in loader:
            optimizer.zero_grad()
            loss = loss_fn(model(X_batch), y_batch)
            loss.backward()
            optimizer.step()
            total_loss += loss.item()
            batch_terakhir = len(y_batch)
            if loss_per_batch is not None:
                loss_per_batch.append(loss.item())
        if epoch in EPOCH_CETAK:
            jejak_loss[epoch] = total_loss / len(loader)
    waktu = time.perf_counter() - mulai

    with torch.no_grad():
        akurasi = (model(X_t).argmax(dim=1) == y_t).float().mean().item()

    getaran = torch.tensor(loss_per_batch).std().item()
    return {
        "iterasi": len(loader),
        "terakhir": batch_terakhir,
        "jejak": jejak_loss,
        "akhir": jejak_loss[JUMLAH_EPOCH],
        "akurasi": akurasi,
        "getaran": getaran,
        "waktu": waktu,
    }


# ---------- 2. Jalankan tiga konfigurasi ----------
print("data      : make_moons 600 sampel | MLP [2, 16, 2] | SGD lr=0.1")
print("epoch     :", JUMLAH_EPOCH, "| satu-satunya variabel: batch size")
print()
for batch_size in (8, 32, 128):
    h = latih(batch_size)
    jejak = " -> ".join(f"{h['jejak'][e]:.4f}" for e in EPOCH_CETAK)
    print(f"batch {batch_size:3d} | {h['iterasi']:2d} iterasi/epoch "
          f"| batch terakhir {h['terakhir']:3d}")
    print(f"  loss epoch 1/10/20/30 : {jejak}")
    print(f"  loss akhir {h['akhir']:.4f} | akurasi {h['akurasi']:.3f} | "
          f"getaran loss antar batch {h['getaran']:.4f} | "
          f"waktu {h['waktu']:.2f} s")
    print()
print("catatan: angka waktu bervariasi antar mesin dan eksekusi;")
print("getaran = simpangan baku loss antar batch pada epoch terakhir.")

Output yang Dihasilkan:

text
data      : make_moons 600 sampel | MLP [2, 16, 2] | SGD lr=0.1
epoch     : 30 | satu-satunya variabel: batch size

batch   8 | 75 iterasi/epoch | batch terakhir   8
  loss epoch 1/10/20/30 : 0.4410 -> 0.2486 -> 0.1627 -> 0.1290
  loss akhir 0.1290 | akurasi 0.963 | getaran loss antar batch 0.1424 | waktu 8.52 s

batch  32 | 19 iterasi/epoch | batch terakhir  24
  loss epoch 1/10/20/30 : 0.5479 -> 0.2921 -> 0.2722 -> 0.2588
  loss akhir 0.2588 | akurasi 0.895 | getaran loss antar batch 0.0902 | waktu 4.49 s

batch 128 |  5 iterasi/epoch | batch terakhir  88
  loss epoch 1/10/20/30 : 0.6132 -> 0.3809 -> 0.3155 -> 0.2988
  loss akhir 0.2988 | akurasi 0.875 | getaran loss antar batch 0.0612 | waktu 1.52 s

catatan: angka waktu bervariasi antar mesin dan eksekusi;
getaran = simpangan baku loss antar batch pada epoch terakhir.

Contoh di atas diuji nyata dengan PyTorch 2.14.1+cpu pada CPU. Mari kita telaah kode di atas baris per baris:

  1. Baris 19-21 (data): 600 sampel make_moons dikonversi sekali ke tensor dan dipakai bersama oleh ketiga pelatihan.
  2. Baris 27-34 (buat_model): seed 7 dipanggil ulang tiap pelatihan sehingga ketiga konfigurasi memulai dari bobot identik.
  3. Baris 43-49 (loader): shuffle=True dengan generator ber-seed memberi urutan data acak yang sama untuk ketiganya.
  4. Baris 55-69 (loop): pelatihan standar; jejak_loss mencatat rata-rata loss per epoch, dan pada epoch terakhir setiap loss batch disimpan untuk mengukur getaran.
  5. Baris 70-75 (metrik): waktu diukur dengan time.perf_counter(); getaran = simpangan baku loss antar batch pada epoch terakhir, ukuran kebisingan gradien mini-batch.
  6. Baris 91-100 (perbandingan): hasil ketiga konfigurasi dicetak berdampingan.

Keluaran mengubah tabel menjadi angka. Batch 8 menghasilkan 75 iterasi per epoch dan menuntaskan loss sampai 0.1290 (akurasi 0.963); batch 128 hanya mendapat 5 iterasi dan berhenti di 0.2988 (0.875) — pada 30 epoch yang sama, frekuensi pembaruan menentukan seberapa jauh model maju. Arah sebaliknya tampak pada getaran: simpangan baku loss antar batch turun 0.1424 → 0.0902 → 0.0612 seiring batch membesar — gradien semakin halus. Waktu per epoch memihak batch besar (8.52 s → 1.52 s) karena 75 iterasi kecil menanggung biaya tetap Python jauh lebih sering daripada 5 iterasi besar.

Jumlah iterasi per epoch kini jelas; besaran berikutnya menentukan seberapa jauh tiap iterasi menggeser parameter. Learning rate — pengatur tunggal yang paling sering memisahkan training mulus dari training gagal — adalah topik chapter berikutnya.