Layer dan MLP

Menumpuk neuron menjadi layer dan menghitung forward pass dengan tangan.

Daftar isi

Layer adalah kumpulan neuron yang menerima masukan yang sama, dan MLP (multilayer perceptron) adalah tumpukan layer dengan aktivasi di sela-selanya. Topik ini menuliskan operasi layer dalam bentuk matriks, menghitung forward pass jaringan kecil dengan tangan, dan membangun MLP yang sama di PyTorch. Setelah selesai, hubungan antara gambaran neuron-per-neuron dan operasi matriks yang sesungguhnya dijalankan komputer menjadi eksplisit.

Dari neuron ke operasi matriks#

Sebuah layer dengan m m neuron yang menerima masukan berdimensi n n memiliki matriks bobot W W berukuran m×n m \times n dan vektor bias b b berukuran m m . Seluruh neuron layer itu dihitung sekaligus oleh satu perkalian matriks:

h=φ(Wx+b) h = \varphi(Wx + b)

Perkalian matriks inilah alasan pelatihan deep learning berjalan cepat, karena pustaka matriks yang dioptimalkan menghitungnya jauh lebih murah daripada perulangan neuron per neuron. Notasi tersebut juga menjadi bahasa bersama dengan dokumentasi PyTorch, di mana modul nn.Linear(n, m) memuat tepat satu matriks W W dan satu vektor b b sesuai bentuk di atas.

Menumpuk layer berarti menyusun operasi tersebut berurutan, misalnya dua layer tersembunyi dan satu layer keluaran ditulis h1=φ1(W1x+b1) h_1 = \varphi_1(W_1 x + b_1) , h2=φ2(W2h1+b2) h_2 = \varphi_2(W_2 h_1 + b_2) , dan y=W3h2+b3 y = W_3 h_2 + b_3 . Urutan penerapan disebut forward pass, dan seluruh perhitungannya deterministik terhadap masukan serta parameter.

Forward pass dengan tangan#

Contoh berikut memakai jaringan 2-2-1 dengan aktivasi ReLU pada layer tersembunyi dan tanpa aktivasi pada keluaran. Bobotnya dipilih sederhana agar setiap langkah dapat dihitung dengan mental, dan NumPy memverifikasi hasilnya.

python Contoh: forward pass jaringan 2-2-1 dengan tangan
import numpy as np

x = np.array([1.0, 2.0])
W1 = np.array([[1.0, 0.0], [0.5, -0.5]])
b1 = np.array([0.0, 1.0])
W2 = np.array([[1.0, 2.0]])
b2 = np.array([-0.5])

z1 = W1 @ x + b1
h1 = np.maximum(0, z1)
y = float((W2 @ h1 + b2)[0])
print("z1 =", np.round(z1, 2))
print("h1 =", np.round(h1, 2))
print("y  =", round(y, 2))
text Keluaran
z1 = [1.  0.5]
h1 = [1.  0.5]
y  = 1.5

Baca keluarannya per baris. Pertama, kedua neuron layer satu menghasilkan z1 z_1 masing-masing 1 dan 0,5. Kedua, ReLU mempertahankannya karena keduanya positif, sehingga h1 h_1 identik dengan z1 z_1 . Ketiga, layer keluaran menjumlahkan 1⋅1+2⋅0,5−0,5=1,5 1 \cdot 1 + 2 \cdot 0{,}5 - 0{,}5 = 1{,}5 . Apabila salah satu z1 z_1 bernilai negatif, ReLU akan menolaknya menjadi nol dan layer keluaran tidak akan melihatnya sama sekali.

MLP yang sama di PyTorch#

PyTorch menyatakan jaringan yang sama dengan nn.Sequential, yaitu kotak yang menjalankan modul-modulnya berurutan. Kode berikut membangun jaringan 2-2-1 di atas, menyalin bobot tangan ke dalamnya, dan membandingkan keluarannya dengan hasil NumPy tadi.

python Contoh: MLP 2-2-1 dengan nn.Sequential
import torch
import torch.nn as nn

mlp = nn.Sequential(
    nn.Linear(2, 2),
    nn.ReLU(),
    nn.Linear(2, 1),
)
with torch.no_grad():
    mlp[0].weight.copy_(torch.tensor([[1.0, 0.0], [0.5, -0.5]]))
    mlp[0].bias.copy_(torch.tensor([0.0, 1.0]))
    mlp[2].weight.copy_(torch.tensor([[1.0, 2.0]]))
    mlp[2].bias.copy_(torch.tensor([-0.5]))

x = torch.tensor([[1.0, 2.0]])
y = mlp(x)
print("bentuk keluaran:", tuple(y.shape))
print("y =", round(y.item(), 2))
print("jumlah parameter:", sum(p.numel() for p in mlp.parameters()))
text Keluaran
bentuk keluaran: (1, 1)
y = 1.5
jumlah parameter: 9

Nilai 1,5 pada keluaran cocok persis dengan perhitungan tangan, dan jumlah parameter enam bertambah satu bias keluaran menjadi tujuh, yaitu empat bobot layer satu, dua bias layer satu, dua bobot layer dua, dan satu bias layer dua. Perhatikan pula penggunaan torch.no_grad() saat menyalin bobot, karena penyalinan bukan bagian dari proses yang perlu dicatat autograd. Konvensi bentuk masukan berukuran batch, yaitu (jumlah_contoh, dimensi), dipakai di sini sejak awal karena seluruh PyTorch mengikutinya.

Mengapa menumpuk layer membantu#

Menumpuk layer memungkinkan fungsi sederhana disusun menjadi fungsi yang kompleks. Layer pertama membentuk garis-garis keputusan, layer kedua menggabungkan garis-garis itu menjadi poligon, dan layer berikutnya menggabungkan poligon menjadi region berbentuk bebas. Teorema aproksimasi universal menyatakan bahwa satu layer tersembunyi yang cukup lebar sudah mampu mendekati fungsi kontinu mana pun pada domain terbatas, tetapi teorema itu tidak mengatakan bahwa cara tersebut yang paling murah.

Dalam praktik, kedalaman yang wajar sering lebih hemat parameter daripada lebar yang ekstrem untuk menangkap struktur bertingkat, dan itulah alasan kata deep melekat pada bidang ini. Harga yang dibayarkan adalah permukaan loss yang lebih sulit ditelusuri dan kebutuhan disiplin pelatihan yang dibahas mulai modul Mekanika pelatihan. Untuk saat ini, kemampuan menghitung forward pass dengan tangan sudah mencukupi sebagai fondasi.

Ringkasan#

  • Satu layer adalah φ(Wx+b) \varphi(Wx + b) dan nn.Linear memuat tepat satu pasang matriks bobot dan bias tersebut.
  • Forward pass MLP adalah susunan operasi layer berurutan, dan jaringan kecil dapat dihitung dengan tangan lalu diverifikasi dengan NumPy maupun PyTorch.
  • nn.Sequential menyusun modul berurutan, dan jumlah parameter dihitung dari ukuran setiap matriks dan vektor bias.
  • Kedalaman menyusun fungsi sederhana menjadi kompleks dengan hemat dibandingkan satu layer yang sangat lebar.

Referensi#