Augmentasi data memperluas data latih tanpa mengumpulkan satu sampel baru: satu gambar dirotasi dan digeser, satu kalimat diparafrasekan, satu sinyal diberi derau — dan model berlatih pada variasi yang sebelumnya tidak ada. Namun transformasi yang tepat untuk citra medis bisa jadi salah untuk citra plat nomor, dan transformasi yang salah justru menurunkan performa. Chapter ini membahas augmentasi sebagai keputusan desain per domain, lalu menguji policy gabungan secara terkendali pada dataset digits.

Strategi per Domain #

DomainTransformasi umumTransformasi yang mengubah label
Gambarflip horizontal, rotasi kecil, geser, zoom, perubahan brightness, derauflip vertikal (foto objek), rotasi 180° (digit 6 menjadi 9)
Tekssinonim, hapus/tukar kata acak, back-translationnegasi yang terhapus (“tidak layak” menjadi “layak”)
Audioubah kecepatan, geser pitch, tambah derau latarpotongan yang memotong kata kunci
Tabularinterpolasi antar sampel sekelas (SMOTE), jitter numerik keciljitter pada kolom yang justru menjadi label

Kolom ketiga adalah inti tabel tersebut: augmentasi hanya sah bila hasil transformasi mempertahankan label. Batasnya berbeda per tugas — flip horizontal aman untuk foto kucing, tetapi membatalkan makna huruf pada citra dokumen.

Aturan Desain Policy #

Tiga aturan menjaga policy tetap sehat. Pertama, train-only: augmentasi diterapkan hanya pada data latih; validasi dan test tetap bersih sebagai pengukur yang jujur. Kedua, magnitude terukur: geser 1-2 piksel dan derau kecil lebih aman daripada transformasi ekstrem; magnitudo adalah hyperparameter yang divalidasi, bukan tebakan. Ketiga, kombinasi sadar: riset seperti RandAugment menunjukkan memilih sejumlah kecil transformasi secara acak per sampel bekerja sebaik pencarian grid yang mahal — prinsipnya, keberagaman mengalahkan kompleksitas.

Teori mengapa augmentasi mengekang overfitting dibahas di Data Augmentation , sedangkan katalog operasi citra lengkap dengan implementasinya ada di Image Augmentation . Chapter ini berfokus pada lapisan di atasnya: bagaimana menyusun transformasi menjadi satu policy.

Demo: Policy Gabungan pada Data Langka #

Skenario paling umum augmentasi membantu: data sedikit. Berkas policy_augmentasi.py membatasi data latih digits ke 80 sampel, lalu membandingkan baseline dengan policy gabungan derau + geser yang diterapkan bergantian dengan epoch bersih:

python
"""Strategi augmentasi: policy gabungan noise + geser pada digits.

Data latih dibatasi 80 sampel (skenario data langka) supaya efek
augmentasi terlihat jelas. Baseline dilatih tanpa augmentasi; varian
policy mendapat noise gaussian + geser 1 piksel yang diterapkan hanya
pada data latih, bergantian dengan epoch bersih. Validasi dan test
tetap bersih (train-only).
"""

import numpy as np
import torch
from sklearn.datasets import load_digits
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split

torch.manual_seed(42)
rng = np.random.default_rng(42)

# ---------- 1. Dataset digits + subset latih kecil ----------
digits = load_digits()
X, y = digits.data / 16.0, digits.target
X_latih, X_sisa, y_latih, y_sisa = train_test_split(
    X, y, train_size=80, random_state=42, stratify=y
)
X_val, X_test, y_val, y_test = train_test_split(
    X_sisa, y_sisa, test_size=0.5, random_state=42, stratify=y_sisa
)
print("===== DATA (skenario data langka) =====")
print("latih:", X_latih.shape, "| validasi:", X_val.shape,
      "| test:", X_test.shape)

# ---------- 2. Policy augmentasi: noise + geser 1 piksel ----------
def augmentasi(gambar_8x8):
    """Satu kebijakan: geser acak +-1 px lalu derau gaussian ringan."""
    arah = rng.integers(-1, 2, size=2)          # (dy, dx) acak
    geser = np.roll(np.roll(gambar_8x8, arah[0], axis=0), arah[1], axis=1)
    derau = rng.normal(0, 0.05, geser.shape)
    return np.clip(geser + derau, 0, 1)


contoh = X_latih[0].reshape(8, 8)
aug = augmentasi(contoh)
print()
print("===== CONTOH SATU AUGMENTASI =====")
print("piksel berubah :", int((aug != contoh).sum()), "dari 64")
print("selisih maks   :", round(float(np.abs(aug - contoh).max()), 3))

# ---------- 3. Model dan training loop ----------
def latih(dengan_policy, epoch=300):
    torch.manual_seed(42)  # bobot awal identik untuk kedua run
    model = torch.nn.Sequential(
        torch.nn.Linear(64, 128), torch.nn.ReLU(), torch.nn.Linear(128, 10)
    )
    opt = torch.optim.Adam(model.parameters(), lr=2e-3)
    loss_fn = torch.nn.CrossEntropyLoss()
    for ep in range(epoch):
        batch = X_latih
        if dengan_policy and ep % 2 == 1:
            # epoch ganjil: batch ter-augmentasi; epoch genap: batch asli
            batch = np.array([augmentasi(g.reshape(8, 8)).ravel()
                              for g in X_latih])
        xt = torch.from_numpy(np.ascontiguousarray(batch, dtype=np.float32))
        yb = torch.from_numpy(y_latih.astype(np.int64))
        opt.zero_grad()
        loss = loss_fn(model(xt), yb)
        loss.backward()
        opt.step()
    with torch.no_grad():
        pv = model(torch.from_numpy(X_val.astype(np.float32))).argmax(1).numpy()
        pt = model(torch.from_numpy(X_test.astype(np.float32))).argmax(1).numpy()
    return accuracy_score(y_val, pv), accuracy_score(y_test, pt)


ak_val_base, ak_test_base = latih(dengan_policy=False)
ak_val_pol, ak_test_pol = latih(dengan_policy=True)

print()
print("===== HASIL: BASELINE vs POLICY (noise + geser) =====")
print(f"{'':16s}{'validasi':>10s}{'test':>8s}")
print(f"{'baseline':16s}{ak_val_base:10.3f}{ak_test_base:8.3f}")
print(f"{'policy':16s}{ak_val_pol:10.3f}{ak_test_pol:8.3f}")
print()
print("kenaikan validasi :", round(ak_val_pol - ak_val_base, 3))
print("kenaikan test     :", round(ak_test_pol - ak_test_base, 3))
print("validasi/test tidak pernah diaugmentasi (train-only).")

Output yang Dihasilkan:

text
===== DATA (skenario data langka) =====
latih: (80, 64) | validasi: (858, 64) | test: (859, 64)

===== CONTOH SATU AUGMENTASI =====
piksel berubah : 56 dari 64
selisih maks   : 0.946

===== HASIL: BASELINE vs POLICY (noise + geser) =====
                  validasi    test
baseline             0.882   0.856
policy               0.915   0.884

kenaikan validasi : 0.033
kenaikan test     : 0.028
validasi/test tidak pernah diaugmentasi (train-only).

Contoh di atas diuji nyata dengan PyTorch 2.14.1+cpu pada mesin CPU dan selesai dalam waktu sekitar delapan detik. Mari kita telaah kode di atas baris per baris:

  1. Baris 19-27 (subset langka): train_size=80 dengan stratify=y menyisakan 80 sampel latih yang tetap seimbang antar kelas; validasi dan test masing-masing berisi lebih dari 850 sampel sehingga pengukuran stabil.
  2. Baris 32-39 (policy): dua transformasi digabung dalam satu fungsi — np.roll menggeser gambar ±1 piksel pada kedua sumbu, lalu derau gaussian 0.05 ditambahkan; np.clip(..., 0, 1) menjaga rentang piksel tetap valid.
  3. Baris 41-46 (inspeksi): satu contoh augmentasi diperiksa sebelum dipakai — 56 dari 64 piksel berubah dengan selisih maksimum 0.946 (efek utama dari geser, bukan derau); memeriksa hasil transformasi adalah kebiasaan yang murah tetapi mencegah policy rusak masuk ke training.
  4. Baris 49-75 (latih): kedua run memulai dari bobot awal identik berkat torch.manual_seed(42) di dalam fungsi; pada epoch ganjil batch diganti versi ter-augmentasi, pada epoch genap model kembali melihat data asli — bergantian ini menjaga model tetap berlabuh pada distribusi bersih.
  5. Baris 77-85 (laporan): akurasi dihitung pada validasi dan test yang tidak pernah diaugmentasi; kenaikan 0.033 pada validasi dan 0.028 pada test menegaskan peningkatan berasal dari generalisasi, bukan dari membuat ujian lebih mudah.

Berapa Banyak Augmentasi yang Tepat #

Tidak ada angka universal, tetapi ada protokol: mulai dari satu transformasi ringan, ukur validasi, tambah satu lagi, ukur lagi. Bila validasi turun saat magnitude dinaikkan, policy sudah melewati batas. Eksperimen kecil seperti demo di atas — dua run, satu variabel — adalah pola kerja yang tepat untuk menyetel policy; kerangka eksperimen terkontrolnya dibahas di Reproducible Experiments .

Augmentasi mengolah data yang sudah ada. Untuk kebutuhan yang lebih jauh — melindungi privasi pemilik data, mengisi kelas yang sangat langka, atau menguji kasus batas — kita bergerak ke ranah data sintetis: data baru yang utuh, lengkap dengan peluang dan risikonya.