Semua teknik pada bab-bab sebelumnya mengasumsikan training sudah berjalan — namun sebelum epoch pertama, ada satu keputusan yang menentukan apakah sinyal belajar sanggup menembus jaringan: nilai awal bobot. Inisialisasi yang buruk membuat aktivasi menyusut tiap layer hingga gradien praktis nol, atau sebaliknya membengkak hingga nilai melimpah — dua wajah dari masalah vanishing dan exploding gradient yang dibahas pada chapter Backpropagation . Chapter ini menunjukkan mengapa memulai dari nol adalah pilihan terburuk, lalu membedakan dua keluarga inisialisasi modern — Xavier untuk aktivasi simetris dan Kaiming untuk ReLU — dengan pengukuran variansi aktual pada jaringan delapan layer.

Mengapa Nol Itu Pilihan Terburuk #

Ada dua alasan terpisah. Pertama, masalah simetri: bila seluruh bobot sebuah layer bernilai sama, seluruh neuron pada layer itu menerima input yang sama dan menghasilkan gradien yang sama pula — sepanjang training, neuron-neuron tersebut bergerak serentak dan tidak pernah menjadi berbeda; jaringan berlebar 256 efektifnya hanya berlebar 1. Kedua, masalah skala: bobot nol menghasilkan aktivasi nol di seluruh layer berikutnya, sehingga sinyal dan gradien mati sejak layer pertama — terlihat pada baris pertama keluaran pengujian kita nanti, yang mencatat variansi 0,000 di kedelapan layer.

Solusinya adalah memulai dari nilai acak kecil — namun seberapa kecil? Terlalu kecil, variansi menyusut tiap layer (vanishing); terlalu besar, variansi membengkak tiap layer (exploding). Analisis Glorot dan Bengio (2010) menemukan titik tengahnya: pertahankan variansi aktivasi agar tetap sama antar layer dengan menskala bobot sesuai jumlah koneksi masuk dan keluarnya.

Xavier dan Kaiming #

Dua keluarga inisialisasi yang menjadi standar industri hari ini lahir dari analisis tersebut. Xavier (sebutan lain: Glorot) menyetel sebaran bobot berdasarkan rata-rata jumlah koneksi masuk-keluar fan_avg = (fan_in + fan_out) / 2 — pasangan alami untuk aktivasi simetris seperti tanh dan sigmoid yang memetakan ke rentang berimbang. Kaiming (sebutan lain: He) menyesuaikan untuk ReLU: karena ReLU memotong separuh nilai menjadi nol, variansi keluarannya tinggal setengah, sehingga bobot perlu lebih besar dengan skala berdasarkan fan_in saja. Modul torch.nn.init menyediakan keduanya dalam beberapa sebaran:

InisialisasiAktivasi pasanganRumus skalaFungsi torch
Xavier uniformtanh, sigmoidbatas ±√(6/(fan_in+fan_out))nn.init.xavier_uniform_
Xavier normaltanh, sigmoidstd √(2/(fan_in+fan_out))nn.init.xavier_normal_
Kaiming uniformReLUbatas ±√(6/fan_in)nn.init.kaiming_uniform_
Kaiming normalReLUstd √(2/fan_in)nn.init.kaiming_normal_

Variansi Aktivasi yang Terukur #

Percakapan tentang “sinyal sehat” menjadi beralasan begitu diukur. Berkas varians_aktivasi.py menyusun jaringan uji delapan layer Linear lebar 256, mengalirkan masukan 256 sampel ber-variansi 1, lalu mencatat variansi aktivasi setelah tiap layer untuk empat kombinasi inisialisasi-aktivasi — termasuk dua kombinasi yang sengaja dipasangkan salah:

python
"""Variansi aktivasi per layer untuk empat strategi inisialisasi.

Jaringan uji: 8 hidden layer Linear lebar 256 tanpa bias acak
(bias di-nol-kan). Masukan: 256 sampel x 256 fitur dari N(0, 1).
Variansi aktivasi dicatat setelah tiap layer. Inisialisasi yang
cocok dengan aktivasinya menjaga variansi tetap pada rentang sehat;
inisialisasi nol membuat semua aktivasi mati sejak layer pertama.
"""

import torch
import torch.nn as nn

torch.manual_seed(42)

DALAM = 8       # jumlah hidden layer
LEBAR = 256     # lebar tiap layer

x = torch.randn(256, LEBAR)                     # variansi = 1
GAIN_TANH = nn.init.calculate_gain("tanh")      # 5/3

KONFIGURASI = [
    ("nol + Tanh",
     lambda w: nn.init.zeros_(w), torch.tanh),
    ("Xavier + Tanh",
     lambda w: nn.init.xavier_uniform_(w, gain=GAIN_TANH), torch.tanh),
    ("Kaiming + ReLU",
     lambda w: nn.init.kaiming_normal_(w, nonlinearity="relu"), torch.relu),
    ("Xavier-tanh + ReLU",
     lambda w: nn.init.xavier_uniform_(w, gain=GAIN_TANH), torch.relu),
]


def jalankan(nama, init_fn, aktivasi):
    torch.manual_seed(7)                        # bobot awal identik
    layers = [nn.Linear(LEBAR, LEBAR) for _ in range(DALAM)]
    for lin in layers:
        init_fn(lin.weight)
        nn.init.zeros_(lin.bias)                # bias nol agar adil

    a = x
    variasi = []
    for lin in layers:
        a = aktivasi(lin(a))
        variasi.append(a.var().item())

    print(f"{nama:20s} " + " ".join(f"{v:6.3f}" for v in variasi))


print(f"variansi aktivasi setelah layer 1..{DALAM} "
      f"(variansi input = {x.var().item():.3f})")
print(f"{'konfigurasi':20s} " + " ".join(f"{'L' + str(i):>6s}" for i in range(1, DALAM + 1)))
print("-" * 76)
for nama, init_fn, aktivasi in KONFIGURASI:
    jalankan(nama, init_fn, aktivasi)

Output yang Dihasilkan:

text
variansi aktivasi setelah layer 1..8 (variansi input = 1.012)
konfigurasi              L1     L2     L3     L4     L5     L6     L7     L8
----------------------------------------------------------------------------
nol + Tanh            0.000  0.000  0.000  0.000  0.000  0.000  0.000  0.000
Xavier + Tanh         0.575  0.477  0.444  0.434  0.428  0.424  0.426  0.424
Kaiming + ReLU        0.690  0.697  0.752  0.922  0.937  0.761  0.708  0.569
Xavier-tanh + ReLU    0.945  1.309  1.557  2.277  2.778  3.589  5.391  7.699

Mari kita telaah kode di atas baris per baris:

  1. Baris 18-19 (masukan dan gain): masukan uji diambil dari randn sehingga variansinya 1; calculate_gain("tanh") mengembalikan 5/3 — faktor penguat yang dianjurkan Glorot agar tanh (yang menekan amplitudo di wilayah jenuhnya) tetap menjaga variansi.
  2. Baris 21-30 (empat kombinasi): tiap entri memasangkan fungsi inisialisasi dengan fungsi aktivasi. Dua entri pertama adalah pasangan benar, entri keempat sengaja memakai Xavier-untuk-tanh bersama ReLU sebagai pembanding yang salah pasangan.
  3. Baris 34-38 (isolasi variabel): seed di-reset dan bias di-nol-kan di dalam jalankan agar keempat baris keluaran hanya berbeda karena strategi inisialisasi, bukan karena keberuntungan acak atau kontribusi bias.
  4. Baris 40-44 (pengukuran): aktivasi mengalir layer demi layer; setelah tiap layer, a.var().item() mencatat variansi seluruh elemen — angka inilah yang menjadi kolom L1 sampai L8.

Empat baris keluaran menceritakan empat nasib. Baris “nol + Tanh” mati seketika: variansi 0,000 di semua layer — bukti langsung bahwa tanpa komponen acak, sinyal tidak pernah lewat dari layer pertama. Baris “Xavier + Tanh” stabil: variansi turun dari 0,575 lalu bertahan di sekitar 0,42-0,43 hingga layer kedelapan — inilah perilaku yang dirancang Glorot. Baris “Kaiming + ReLU” bergerak di rentang 0,57-0,94, cukup sehat untuk ReLU yang memang membiarkan separuh neuron bernilai nol. Baris “Xavier-tanh + ReLU” menanjak terus 0,945 → 1,309 → … → 7,699 — hampir delapan kali variansi input hanya dalam delapan layer; pada jaringan 50 layer, pembengkakan eksponensial semacam ini membuat nilai melimpah dan training gagal.

Katalog lengkap fungsi-fungsi torch.nn.init — termasuk constant_, orthogonal_, dan calculate_gain — terdokumentasi di docs.pytorch.org/docs/stable/nn.init.html . Inisialisasi menjaga kesehatan sinyal sekali di awal; bab berikutnya memperkenalkan mekanisme yang menjaganya terus-menerus sepanjang training dengan menormalkan aktivasi di dalam jaringan itu sendiri: Batch Normalization.