Tur layer

Layer bawaan torch.nn: Linear dan fungsi aktivasi ReLU serta sigmoid.

Daftar isi

Layer adalah komponen komputasi yang dapat dirangkai menjadi model, dan torch.nn menyediakannya dalam bentuk siap pakai sehingga model jarang dibangun dari operasi tensor mentah. Topik ini menelusuri nn.Linear sebagai layer yang paling sering digunakan untuk data tabular, disusul fungsi aktivasi ReLU dan Sigmoid dalam dua bentuknya, yaitu sebagai module dan sebagai fungsi. Bagian akhir membahas nn.Sequential sebagai cara ringkas merangkai layer, beserta satu jebakan pada urutan layer yang melemahkan model tanpa menimbulkan error.

nn.Linear dan bentuk masukannya#

nn.Linear(in_features, out_features) menerapkan transformasi linear terhadap dimensi terakhir tensor masukannya. Transformasi tersebut dirumuskan sebagai berikut, dengan WW berupa matriks bobot dan bb berupa vektor bias.

y=xW⊤+by = x W^{\top} + b

Bentuk masukan yang dituntut adalah (N, in_features) dan keluarannya berbentuk (N, out_features), dengan N menyatakan jumlah sampel. Bobot tersimpan dengan bentuk (out_features, in_features), sedangkan bias dengan bentuk (out_features,). Urutan tersebut mengikuti rumus di atas, karena perkalian xW⊤x W^{\top} menuntut WW berbentuk (out_features, in_features).

python Bentuk masukan, keluaran, bobot, dan bias nn.Linear
import torch
import torch.nn as nn

torch.manual_seed(0)

linear = nn.Linear(in_features=4, out_features=3)

x = torch.randn(5, 4)     # (N, in_features) = (5, 4)
y = linear(x)

print(y.shape)
print(linear.weight.shape)
print(linear.bias.shape)
text Keluaran
torch.Size([5, 3])
torch.Size([3, 4])
torch.Size([3])

Keluaran torch.Size([5, 3]) sesuai prediksi, yaitu dimensi terakhir berubah dari 4 menjadi 3, sedangkan dimensi sampel tetap 5. Bentuk bobot torch.Size([3, 4]) dan bias torch.Size([3]) juga konsisten dengan anotasi pada diagram berikut.

x(N, in_features)nn.Linear(in_features, out_features)bobot (out_features, in_features)bias (out_features,)y(N, out_features)
Pemetaan bentuk pada nn.Linear

Diagram di atas menegaskan pola yang berlaku untuk setiap Linear. Anotasi bentuk seperti ini diperiksa terlebih dahulu setiap kali layer dirangkai, karena ketidakcocokan bentuk antara layer merupakan salah satu sumber error yang sering muncul saat membangun model.

Fungsi aktivasi: module dan fungsi#

Layer Linear hanya mampu memetakan data secara linear. Fungsi aktivasi ditempatkan di antara layer untuk memberikan sifat non-linear, dan dua yang paling umum adalah ReLU dan sigmoid. ReLU mempertahankan nilai positif dan mengubah nilai negatif menjadi nol, sedangkan sigmoid memetakan setiap nilai ke rentang (0, 1).

ReLU(z)=max⁡(0,z)\mathrm{ReLU}(z) = \max(0, z)
σ(z)=11+e−z\sigma(z) = \frac{1}{1 + e^{-z}}

Keduanya tersedia dalam dua bentuk. Sebagai module, nn.ReLU dan nn.Sigmoid digunakan seperti layer lain, yaitu ditetapkan sebagai atribut di __init__ atau menjadi anggota nn.Sequential. Sebagai fungsi, torch.relu dan torch.sigmoid dipanggil langsung terhadap tensor. Hasil kedua bentuk itu identik, sebagaimana diperiksa pada kode berikut.

python nn.ReLU dan nn.Sigmoid dibandingkan torch.relu dan torch.sigmoid
import torch
import torch.nn as nn

x = torch.tensor([-1.5, 0.0, 2.0])

relu = nn.ReLU()
sigmoid = nn.Sigmoid()

print("nn.ReLU()    :", relu(x))
print("torch.relu() :", torch.relu(x))
print("nn.Sigmoid() :", sigmoid(x))
print("torch.sigmoid:", torch.sigmoid(x))
print(relu(x).equal(torch.relu(x)))
print(sigmoid(x).equal(torch.sigmoid(x)))
text Keluaran
nn.ReLU()    : tensor([0., 0., 2.])
torch.relu() : tensor([0., 0., 2.])
nn.Sigmoid() : tensor([0.1824, 0.5000, 0.8808])
torch.sigmoid: tensor([0.1824, 0.5000, 0.8808])
True
True

Keluaran tersebut memperlihatkan perilaku keduanya pada nilai -1.5, 0.0, dan 2.0. ReLU memetakan dua nilai pertama menjadi nol, sedangkan sigmoid mengonversinya menjadi 0.1824, 0.5000, dan 0.8808 yang berada di rentang (0, 1). Dua baris terakhir bernilai True, sehingga bentuk module dan bentuk fungsi memberikan hasil yang sama persis. Pemilihan bentuknya merupakan persoalan gaya: module digunakan agar seragam dengan layer lain saat model dirangkai, sedangkan fungsi digunakan untuk komputasi singkat di dalam forward(). Berbeda dengan Linear, keduanya tidak membawa parameter yang dipelajari, sehingga penelusuran named_parameters() pada topik Parameter tidak pernah menampilkannya.

Urutan layer menentukan sifat model#

Urutan layer bukan sekadar urutan penulisan, melainkan penentu sifat matematis model. Dua layer Linear yang dipanggil berturut-turut tanpa aktivasi di antaranya membentuk pemetaan yang ekuivalen dengan satu layer Linear saja, sebab:

W2(W1x+b1)+b2=(W2W1)x+(W2b1+b2)W_2 (W_1 x + b_1) + b_2 = (W_2 W_1) x + (W_2 b_1 + b_2)

Ruas kanan berupa transformasi linear dengan bobot gabungan W2W1W_2 W_1 dan bias gabungan. Artinya, kemampuan menangkap pola non-linear hilang tanpa error apa pun, dan pelatihan tetap berjalan seperti biasa. Potongan kode berikut membuktikan ekuivalensi tersebut dengan menyusun bobot gabungannya secara manual.

python Dua Linear tanpa aktivasi ekuivalen satu Linear
import torch
import torch.nn as nn

torch.manual_seed(0)

fc1 = nn.Linear(4, 8)
fc2 = nn.Linear(8, 3)

x = torch.randn(5, 4)
y = fc2(fc1(x))     # dua transformasi linear berturut-turut, tanpa aktivasi

fc_gabungan = nn.Linear(4, 3)
with torch.no_grad():
    fc_gabungan.weight.copy_(fc2.weight @ fc1.weight)
    fc_gabungan.bias.copy_(fc2.weight @ fc1.bias + fc2.bias)

print(y.shape)
print(torch.allclose(y, fc_gabungan(x), atol=1e-6))
text Keluaran
torch.Size([5, 3])
True

torch.allclose mengembalikan True, sehingga rangkaian dua layer itu benar-benar sama dengan satu layer yang bobotnya dikalikan. Karena itu, fungsi aktivasi non-linear wajib berada di antara layer-layer Linear. Perlu ditegaskan pula bahwa layer tidak dirangkai dengan operasi aritmetika antarobjek, misalnya perkalian fc1 * fc2. Rangkaian dibentuk dengan memanggil satu layer setelah layer lain, sebagaimana dilakukan di dalam forward().

nn.Sequential sebagai susunan ringkas#

nn.Sequential menerima sederetan module dan membungkusnya menjadi satu module tunggal. Saat objeknya dipanggil, keluaran setiap module menjadi masukan module berikutnya, dari kiri ke kanan. Cara ini ringkas untuk model yang alirannya lurus, sebab definisi __init__ dan forward() tidak perlu ditulis sendiri. Urutan argumen sekaligus menjadi urutan eksekusi, sehingga susunan Linear lalu ReLU menghasilkan model yang berbeda dengan ReLU lalu Linear.

python Model dua layer dengan nn.Sequential
import torch
import torch.nn as nn

torch.manual_seed(0)

model = nn.Sequential(
    nn.Linear(4, 8),
    nn.ReLU(),
    nn.Linear(8, 3),
)

x = torch.randn(5, 4)
logits = model(x)

print(logits.shape)
text Keluaran
torch.Size([5, 3])

Bentuk keluarannya sama dengan model pola kelas pada topik nn.Module, karena susunannya identik. Keterbatasan Sequential terletak pada bentuk alirannya: setiap module hanya menerima satu masukan dan menghasilkan satu keluaran. Begitu aliran data bercabang, disambung dengan operasi tambahan, atau membutuhkan logika khusus di tengah aliran, pola kelas dengan forward() yang ditulis secara manual kembali diperlukan.

Ringkasan#

  • nn.Linear(in_features, out_features) menerapkan y=xW⊤+by = x W^{\top} + b pada dimensi terakhir, mengubah (N, in_features) menjadi (N, out_features), dengan bobot berbentuk (out_features, in_features) dan bias (out_features,).
  • ReLU dan sigmoid tersedia sebagai module (nn.ReLU, nn.Sigmoid) maupun fungsi (torch.relu, torch.sigmoid), dan hasil keduanya identik.
  • Dua Linear berturutan tanpa aktivasi kolaps menjadi satu pemetaan linear, sehingga aktivasi non-linear wajib di antara layer.
  • nn.Sequential merangkai module berurutan menjadi satu objek, dengan syarat alirannya lurus dan bentuk antara module cocok.

Referensi#