Topik ini membahas nn.BatchNorm2d, yaitu layer yang menstabilkan distribusi aktivasi antara layer jaringan CNN. Fokus utamanya adalah perbedaan perilaku mode train dan mode eval, sebab di situlah kesalahan yang paling sering terjadi.
Apa yang distabilkan#
Distribusi aktivasi yang masuk ke sebuah layer tidak diam selama pelatihan. Setiap pembaruan bobot pada layer-layer sebelumnya mengubah nilai yang tiba di layer berikutnya, sehingga layer tersebut bekerja dengan masukan yang terus bergeser. Batch normalization meredam pergeseran ini dengan menormalkan aktivasi per kanal, yaitu menghitung rata-rata dan varians dari batch, lalu menggeser dan menskalakan hasilnya.
Untuk aktivasi pada satu kanal, normalisasinya adalah dengan dan berturut-turut rata-rata serta varians batch, kemudian keluarannya adalah . Nilai , yang bawaannya , menjaga pembagian tetap aman. Pasangan dan adalah parameter yang dipelajari, sehingga layer tetap dapat membentuk distribusi keluaran sesuai kebutuhannya, termasuk mengembalikannya seperti semula bila itu yang paling menguntungkan.
Dengan masukan yang distribusinya stabil, layer berikutnya tidak perlu menyesuaikan diri terhadap pergeseran besar, dan pelatihan menjadi lebih tahan terhadap pilihan learning rate. Pada jaringan yang dalam, efek yang sering terlihat adalah konvergensi yang lebih cepat dibandingkan dengan arsitektur yang sama tanpa normalisasi.
Mode train dan mode eval#
Perilaku nn.BatchNorm2d bergantung pada mode model. Pada mode train, normalisasi menggunakan statistik batch yang sedang diproses, dan dua buffer bernama running_mean serta running_var diperbarui sebagai rata-rata bergerak. Dengan nilai bawaan momentum 0.1, statistik berjalan yang baru adalah 0.9 kali statistik lama ditambah 0.1 kali statistik batch saat itu. Pada mode eval, keluaran dihitung dari statistik berjalan itu, dan tidak ada buffer yang diperbarui.
Konsekuensinya langsung terasa, yaitu keluaran model untuk masukan yang sama berbeda antara kedua mode. Inilah alasan teknis pemanggilan model.train() dan model.eval() di dalam perulangan pelatihan. Evaluasi memanggil model.eval() agar setiap batch dihitung dengan statistik yang sama, bukan dengan statistik batch masing-masing.
Demonstrasi statistik berjalan#
Perbedaan kedua mode dapat dibuktikan dengan program kecil. Satu batch tetap diberikan lima kali kepada layer batch normalization dalam mode train agar statistik berjalannya terisi, kemudian keluaran kedua mode dibandingkan untuk masukan yang sama.
import torch
import torch.nn as nn
torch.manual_seed(0)
# pasangan layer yang akan diperiksa: konvolusi, lalu batch normalization
conv = nn.Conv2d(1, 4, kernel_size=3, padding=1)
bn = nn.BatchNorm2d(4)
# satu batch tetap yang diberikan berulang kali
torch.manual_seed(1)
x = torch.rand(8, 1, 8, 8)
akt = conv(x) # aktivasi masukan bagi batch normalization
with torch.no_grad():
print("awal running_mean:", bn.running_mean)
print("awal running_var :", bn.running_var)
for _ in range(5):
y_train = bn(akt) # mode train: statistik batch, statistik berjalan diperbarui
print("sesudah 5 forward running_mean:", bn.running_mean)
print("sesudah 5 forward running_var :", bn.running_var)
# mode eval: statistik berjalan yang dipakai, bukan statistik batch
bn.eval()
y_eval = bn(akt)
selisih = (y_train - y_eval).abs().max().item()
print(f"selisih maksimum train vs eval: {selisih:.4f}")
print("y_train[0, 0, 0, :4]:", y_train[0, 0, 0, :4])
print("y_eval [0, 0, 0, :4]:", y_eval[0, 0, 0, :4])awal running_mean: tensor([0., 0., 0., 0.])
awal running_var : tensor([1., 1., 1., 1.])
sesudah 5 forward running_mean: tensor([-0.1039, 0.0312, 0.1444, -0.1519])
sesudah 5 forward running_var : tensor([0.6001, 0.5996, 0.6067, 0.6167])
selisih maksimum train vs eval: 2.6881
y_train[0, 0, 0, :4]: tensor([ 0.8151, -0.2172, 1.0152, -0.1121])
y_eval [0, 0, 0, :4]: tensor([-0.0328, -0.2363, 0.0066, -0.2156])Keluarannya memperlihatkan tiga hal. Buffer yang mula-mula berisi nol dan satu bergeser mendekati statistik batch setelah lima forward, karena momentumnya 0.1. Keluaran mode train dinormalisasi dengan statistik batch, sedangkan keluaran mode eval menggunakan statistik berjalan, sehingga selisih maksimumnya mencapai 2.6881 untuk masukan yang sama. Empat elemen pertama pada kedua keluaran menegaskan bahwa perbedaannya nyata, bukan sekadar selisih presisi.
Kesalahan umum#
Batch yang terlalu kecil membuat statistik batch tidak stabil, karena rata-rata dan variansnya dihitung dari sedikit sampel. Pada kasus ekstrem satu nilai per kanal, mode train menolak menghitungnya sama sekali. Contoh berikut memicu error tersebut dengan batch berukuran satu dan ukuran spasial 1x1.
import torch
import torch.nn as nn
bn = nn.BatchNorm2d(4)
x = torch.rand(1, 4, 1, 1) # satu sampel, spasial 1x1
try:
bn(x) # masih mode train
except ValueError as e:
print("ValueError:", e)ValueError: Expected more than 1 value per channel when training, got input size torch.Size([1, 4, 1, 1])Perbaikannya adalah memperbesar batch, atau berpindah ke mode eval apabila perhitungan memang berupa inferensi. Untuk arsitektur yang memang bekerja dengan batch kecil, layer alternatif seperti nn.GroupNorm menormalkan aktivasi tanpa bergantung pada batch sama sekali. Kesalahan kedua yang sering terjadi adalah lupa memanggil model.train() kembali setelah evaluasi, sehingga pelatihan berlanjut dengan statistik beku dan loss tidak turun. Pembagian mode sebaiknya ditulis sebagai pola baku di awal dan akhir setiap perulangan, bukan diletakkan sesuai keinginan.
Ringkasan#
- Batch normalization menormalkan aktivasi per kanal menggunakan statistik batch, lalu menskalakan dan menggesernya dengan parameter yang dipelajari.
- Mode train menggunakan statistik batch dan memperbarui
running_meansertarunning_var, sedangkan mode eval menggunakan statistik berjalan tanpa pembaruan. - Keluaran untuk masukan yang sama berbeda antara kedua mode, sehingga evaluasi wajib didahului
model.eval(). - Batch kecil membuat statistik batch tidak stabil, dan batch dengan satu nilai per kanal ditolak pada mode train.
Referensi#
- torch.nn.BatchNorm2d — PyTorch — Dokumentasi argumen, buffer statistik berjalan, dan nilai bawaan momentum.
- torch.nn — PyTorch
— Indeks layer normalisasi lain seperti
GroupNormdanLayerNorm.