Perulangan pelatihan yang benar belum tentu pelatihan yang berhasil, dan satu-satunya cara menilainya adalah mengamati kurva loss antar epoch. Topik ini membahas cara mencatat loss per epoch secara deterministik, cara menampilkan kemajuan pelatihan dengan tqdm, dan cara membaca laju penurunan loss sebagai alat diagnosis sebelum pelatihan penuh dijalankan. Setelah membaca topik ini, perulangan pelatihan bukan lagi kotak hitam, karena kemajuannya terbaca dari angka yang dicetaknya sendiri.
Satu epoch menelusuri seluruh data#
Satu epoch adalah satu penelusuran penuh atas seluruh data pelatihan, dan jumlah langkah pembaruan bobot dalam satu epoch sama dengan jumlah batch, kelompok sampel yang diproses bersama dalam satu langkah. Pelatihan sering membutuhkan puluhan hingga ratusan epoch, sehingga mencatat satu angka per epoch adalah kerangka pelacakan yang paling sederhana sekaligus paling mudah dibandingkan. Skrip berikut melatih regresi linear pada 128 sampel selama delapan epoch, lalu mencetak rata-rata loss setiap epoch beserta total penurunannya.
import torch
import torch.nn as nn
from torch.utils.data import TensorDataset, DataLoader
torch.manual_seed(0)
n = 128
x = torch.linspace(0.0, 1.0, n).unsqueeze(1)
y = 3.0 * x + 0.5 + 0.08 * torch.randn(n, 1)
loader = DataLoader(TensorDataset(x, y), batch_size=32, shuffle=True)
model = nn.Linear(1, 1)
optimizer = torch.optim.SGD(model.parameters(), lr=0.3)
loss_fn = nn.MSELoss()
riwayat = []
for epoch in range(8):
total = 0.0
for xb, yb in loader:
optimizer.zero_grad()
loss = loss_fn(model(xb), yb)
loss.backward()
optimizer.step()
total += loss.item()
rata = total / len(loader)
riwayat.append(rata)
print(f"epoch {epoch + 1}: loss {rata:.4f}")
penurunan = (riwayat[0] - riwayat[-1]) / riwayat[0] * 100
print(f"loss turun {penurunan:.1f}% dari epoch 1 ke epoch 8")epoch 1: loss 2.7917
epoch 2: loss 0.2501
epoch 3: loss 0.1818
epoch 4: loss 0.1331
epoch 5: loss 0.0989
epoch 6: loss 0.0741
epoch 7: loss 0.0530
epoch 8: loss 0.0420
loss turun 98.5% dari epoch 1 ke epoch 8Daftar riwayat menyimpan satu angka per epoch, dan angka itulah kurva loss dalam bentuknya yang paling sederhana. Lompatan terbesar terjadi antara epoch pertama dan kedua, yaitu dari 2.7917 ke 0.2501, kemudian penurunannya melambat secara konsisten dari epoch ke epoch. Bentuk penurunan yang melambat seperti ini adalah perilaku normal, karena perbaikan yang tersisa memang makin kecil seiring model menghampiri batas terbaiknya. Tanpa grafik sekalipun, kolom angka yang rapi sudah menunjukkan bentuk kurvanya.
Menampilkan kemajuan dengan tqdm#
Pencetakan per epoch bekerja baik untuk pelatihan pendek, tetapi pelatihan panjang membutuhkan umpan balik yang lebih sering. tqdm, pustaka Python untuk bar kemajuan, membungkus perulangan mana pun dan menampilkan berapa persen pekerjaan yang selesai beserta perkiraan waktu sisa. Bar tersebut memperbarui dirinya di tempat yang sama di terminal, sehingga keluarannya interaktif dan tidak dapat direkam sebagai teks statis, apalagi dibandingkan antara eksekusi. Berikut pola penggunaannya pada perulangan pelatihan, dengan bar luar untuk epoch dan bar dalam untuk batch.
# tidak-dijalankan: bar tqdm menimpa dirinya di terminal, sehingga keluarannya tidak dapat direkam sebagai teks statis
from tqdm import tqdm
for epoch in tqdm(range(8), desc="epoch"):
total = 0.0
for xb, yb in tqdm(loader, desc="batch", leave=False):
optimizer.zero_grad()
loss = loss_fn(model(xb), yb)
loss.backward()
optimizer.step()
total += loss.item()
print(f"epoch {epoch + 1}: loss {total / len(loader):.4f}")Argumen desc memberi nama pada bar, sedangkan leave=False menghapus bar batch setiap ganti epoch agar layar tidak penuh sisa bar. Bar yang berjalan menampilkan persentase pekerjaan yang selesai, jumlah iterasi, kecepatan iterasi per detik, dan perkiraan waktu sisa. Seluruh kolom itu bersifat tampilan semata, sehingga mengganti range(8) dengan tqdm(range(8)) tidak mengubah nilai apa pun yang dihitung di dalam perulangan. Aturan praktisnya, tetapkan satu sumber kebenaran untuk angka, yaitu print per epoch atau pencatat eksperimen, dan perlakukan tqdm sebagai layar kemajuan semata.
Laju penurunan sebagai alat diagnosis#
Kurva loss bukan sekadar laporan, melainkan alat diagnosis yang paling murah. Perbandingan berikut melatih model yang sama pada data yang sama dengan dua nilai learning rate yang berbeda, yakni besarnya langkah pembaruan bobot, lalu mencatat loss pada empat titik langkah yang sama.
import torch
import torch.nn as nn
torch.manual_seed(0)
x = torch.linspace(0.0, 1.0, 64).unsqueeze(1)
y = 2.0 * x + 1.0
def latih(lr):
torch.manual_seed(0)
model = nn.Linear(1, 1)
opt = torch.optim.SGD(model.parameters(), lr=lr)
loss_fn = nn.MSELoss()
riwayat = []
for langkah in range(40):
opt.zero_grad()
loss = loss_fn(model(x), y)
loss.backward()
opt.step()
riwayat.append(loss.item())
return riwayat
a = latih(0.3)
b = latih(0.001)
for i in (0, 9, 19, 39):
print(f"langkah {i + 1:>2}: lr=0.3 {a[i]:.4f} lr=0.001 {b[i]:.4f}")langkah 1: lr=0.3 2.4995 lr=0.001 2.4995
langkah 10: lr=0.3 0.0771 lr=0.001 2.3947
langkah 20: lr=0.3 0.0336 lr=0.001 2.2837
langkah 40: lr=0.3 0.0064 lr=0.001 2.0779Kedua jalur memulai dari loss yang identik, tetapi jalur dengan learning rate 0.3 turun ke 0.0064 dalam empat puluh langkah, sedangkan jalur dengan learning rate 0.001 nyaris tidak bergerak dan masih di 2.0779. Laju penurunan yang terlalu datar pada awal pelatihan, seperti kurva yang masih bertahan di dekat nilai awal setelah puluhan langkah, patut dicurigai sebelum pelatihan dibiarkan berjalan lama. Tiga bentuk kurva mencakup sebagian besar kasus.
- Kurva menurun lalu mendatar di dekat lantai derau, bentuk yang sehat dan menandakan pelatihan dapat dilanjutkan.
- Kurva mendatar terlalu cepat di dekat nilai awal, penanda learning rate terlalu kecil, baris pelatihan yang salah, atau model yang tidak cocok dengan data.
- Kurva berayun atau justru naik, penanda learning rate terlalu besar atau gradien yang tercemar, misalnya akibat
zero_grad()yang tidak dipanggil.
Membaca kurva pada tingkat ini membuat keputusan berikutnya terukur, apakah melanjutkan epoch, menaikkan learning rate, atau berhenti dan mencari bug terlebih dahulu.
Kesalahan umum#
Tiga kesalahan berikut paling sering muncul ketika kurva loss dicatat.
- Membagi akumulasi loss dengan jumlah sampel alih-alih jumlah batch. Variabel
totaladalah penjumlahan rata-rata loss per batch, sehingga pembagi yang benar adalahlen(loader). Pembagian denganlen(dataset)menghasilkan angka yang selalu terlihat kecil dan tidak sebanding antara konfigurasi batch. - Membandingkan kurva antara eksekusi yang berbeda seed, batch size, atau learning rate secara bersamaan. Perubahan yang bertumpuk membuat penyebab pergeseran kurva tidak dapat ditentukan. Ubah satu variabel dalam satu eksekusi, dan catat seed beserta konfigurasi bersama kurvanya.
- Menyimpan angka loss sebagai tensor yang masih terhubung graf komputasi. Riwayat yang memuat
lossmentah menarik seluruh grafnya, sehingga memori membengkak pada pelatihan panjang. Panggil.item()pada saat mencatat agar yang tersimpan berupa angka Python biasa.
Ringkasan#
- Satu epoch adalah satu penelusuran penuh data pelatihan, dan rata-rata loss per epoch diperoleh dengan membagi akumulasi loss batch oleh jumlah batch.
- tqdm menampilkan bar kemajuan yang interaktif di terminal, sehingga berguna untuk mengamati jalannya pelatihan tetapi tidak cocok sebagai catatan yang direkam dan dibandingkan.
- Laju penurunan loss antara epoch adalah alat diagnosis, dengan tiga bentuk utama, yaitu menurun lalu mendatar, mendatar di dekat nilai awal, dan berayun atau naik.
Referensi#
- PyTorch data documentation — PyTorch
— Dokumentasi
DataLoaderyang menjadi sumber batch dalam setiap epoch. - PyTorch documentation — PyTorch — Rujukan API pelatihan, optimizer, dan fungsi loss yang digunakan pada contoh.
- Deep Learning with PyTorch: A 60 Minute Blitz — PyTorch — Tutorial resmi yang menyertakan pelatihan berepoch pada perulangan pertamanya.