Siklus zero_grad → forward → backward → step dari chapter sebelumnya bekerja, tetapi parameter kita masih satu tensor telanjang yang dikelola satu per satu. Model nyata punya ratusan tensor tersebar di puluhan layer, dan mengelolanya manual sama tidak praktisnya dengan menulis backpropagation manual — alasan section Dasar Neural Network
menutup dengan kelas buatan sendiri. torch.nn menyediakan kerangka resmi untuk pola itu: nn.Module merapikan deklarasi layer ke dalam __init__, alur hitungan ke dalam forward, lalu menyediakan pencatatan parameter, pencetakan struktur, dan mode perilaku secara gratis.
Kerangka Dua Metode: __init__ dan forward #
Setiap model PyTorch adalah kelas Python yang mewarisi nn.Module dan mengisi dua metode. __init__ mendeklarasikan apa saja yang dimiliki model — layer, aktivasi, submodule lain — sedangkan forward mendefinisikan bagaimana data mengalir melaluinya. Dua hal terjadi otomatis di balik layar: setiap nn.Linear yang kita simpan sebagai atribut terdaftar sebagai submodule beserta parameternya (sehingga optimizer dan state_dict bisa menemukannya), dan pemanggilan model(x) diteruskan ke forward melalui __call__ milik Python.
MLP Pertama #
Berkas mlp_module.py mendefinisikan multilayer perceptron [4, 16, 8, 3] — arsitektur berlapis yang konsepnya dibahas di chapter Multilayer Perceptron
— lalu mencetak strukturnya, menghitung jumlah parameter, dan menjalankan forward pada batch dummy:
"""MLP pertama dengan nn.Module: struktur, jumlah parameter, dan forward dummy."""
import torch
import torch.nn as nn
torch.manual_seed(42)
class MLP(nn.Module):
"""Multilayer perceptron: input -> 16 -> 8 -> output, ReLU di hidden."""
def __init__(self, jumlah_input, jumlah_output):
super().__init__()
self.hidden1 = nn.Linear(jumlah_input, 16)
self.hidden2 = nn.Linear(16, 8)
self.output = nn.Linear(8, jumlah_output)
self.aktivasi = nn.ReLU()
def forward(self, x):
x = self.aktivasi(self.hidden1(x))
x = self.aktivasi(self.hidden2(x))
return self.output(x)
model = MLP(jumlah_input=4, jumlah_output=3)
print("Struktur model:")
print(model)
print()
total = sum(p.numel() for p in model.parameters())
print(f"Total parameter: {total}")
print("Rincian parameter:")
for nama, p in model.named_parameters():
print(f" {nama:22s} shape {tuple(p.shape)} -> {p.numel()} angka")
print()
x_dummy = torch.randn(8, 4) # batch 8 contoh, 4 fitur
with torch.no_grad():
y = model(x_dummy)
print(f"forward dummy: input {tuple(x_dummy.shape)} -> output {tuple(y.shape)}")
print("baris pertama output:", [round(v, 4) for v in y[0].tolist()])Output yang Dihasilkan:
Struktur model:
MLP(
(hidden1): Linear(in_features=4, out_features=16, bias=True)
(hidden2): Linear(in_features=16, out_features=8, bias=True)
(output): Linear(in_features=8, out_features=3, bias=True)
(aktivasi): ReLU()
)
Total parameter: 243
Rincian parameter:
hidden1.weight shape (16, 4) -> 64 angka
hidden1.bias shape (16,) -> 16 angka
hidden2.weight shape (8, 16) -> 128 angka
hidden2.bias shape (8,) -> 8 angka
output.weight shape (3, 8) -> 24 angka
output.bias shape (3,) -> 3 angka
forward dummy: input (8, 4) -> output (8, 3)
baris pertama output: [0.1287, -0.1899, -0.0899]Mari kita telaah kode di atas baris per baris:
- Baris 9 (
class MLP(nn.Module)): model mewarisinn.Module— dari sinilah berasalparameters(),train(),eval(), dan pencetakan struktur. - Baris 13 (
super().__init__()): baris wajib pertama__init__; tanpa baris ini PyTorch menolak pendaftaran atribut layer dengan galat eksplisit. Setelahnya, tiapnn.Linearyang disimpan sebagai atribut otomatis tercatat sebagai submodule. - Baris 14-16 (
nn.Linear): satu layer fully-connectedy = xWᵀ + b. Perhatikan urutan argumennya(masuk, keluar)— tetapiweighttersimpan dengan shape(keluar, masuk):hidden1.weightberukuran(16, 4). Bobot diinisialisasi acak mengikuti seed; bias diisi nol. - Baris 17 (
nn.ReLU()): aktivasi elementwise tanpa parameter; disimpan sebagai atribut agarforwardrapi. - Baris 19-22 (
forward): alur data dibaca dari atas ke bawah —xmenembushidden1, ReLU,hidden2, ReLU, laluoutput. Layer terakhir sengaja polos tanpa aktivasi; alasan strategisnya dibahas di chapter Loss dan Optimizer . - Baris 25-28 (
print(model)):nn.Moduletahu mencetak dirinya — tiap baris menampilkan nama atribut dan isinya, struktur yang persis mengikuti deklarasi__init__. - Baris 30-34 (
parameters()): iterator seluruh tensor daun ber-requires_grad, dari submodule terdalam.sum(p.numel() ...)menjumlah243 = (4·16+16) + (16·8+8) + (8·3+3)— angka yang pada section Dasar Neural Network kita lacak manual lewat shape, kini dihitung pustaka. - Baris 37-41 (forward dummy): batch
(8, 4)— delapan contoh, empat fitur — mengalir menjadi(8, 3). Bloktorch.no_grad()dipakai karena inferensi ini tidak membutuhkan graph, kebiasaan dari chapter Autograd .
nn.Sequential: Susunan Berurutan #
Bila alur model memang lurus — output satu layer langsung menjadi input layer berikutnya — nn.Sequential memotong boilerplate kelas: cukup daftarkan layer berurutan, dan forward tersusun sendiri. Model mode_train_eval.py di bawah memakai pola ini. Pilihannya praktis: nn.Sequential untuk susunan tetap, kelas nn.Module begitu alur bercabang, menerima argumen tambahan, atau butuh logika Python di tengah forward.
train() versus eval() #
Beberapa layer berperilaku berbeda antara pelatihan dan inferensi. nn.Dropout — regularisasi yang mematikan neuron secara acak selama training, dibahas mendalam pada section Regularisasi dan Generalisasi
— hanya aktif saat training; nn.BatchNorm1d menghitung statistik batch saat training tetapi memakai statistik tersimpan saat evaluasi. Metode model.train() dan model.eval() memindai seluruh bendera training ke tiap submodule. Berkas mode_train_eval.py membuktikan perbedaannya dengan input yang sama:
"""Mode train() vs eval(): Dropout berperilaku berbeda di masing-masing mode."""
import torch
import torch.nn as nn
torch.manual_seed(42)
model = nn.Sequential(
nn.Linear(4, 8),
nn.ReLU(),
nn.Dropout(p=0.5),
nn.Linear(8, 3),
)
x = torch.ones(1, 4)
model.train()
print("mode train (model.training =", model.training, ")")
with torch.no_grad():
print(" pemanggilan 1:", [round(v, 4) for v in model(x)[0].tolist()])
print(" pemanggilan 2:", [round(v, 4) for v in model(x)[0].tolist()])
print(" dropout mematikan neuron secara acak -> hasil berubah tiap panggilan")
model.eval()
print("mode eval (model.training =", model.training, ")")
with torch.no_grad():
print(" pemanggilan 1:", [round(v, 4) for v in model(x)[0].tolist()])
print(" pemanggilan 2:", [round(v, 4) for v in model(x)[0].tolist()])
print(" dropout dimatikan -> hasil identik dan stabil")Output yang Dihasilkan:
mode train (model.training = True )
pemanggilan 1: [0.2392, -0.3815, 0.5366]
pemanggilan 2: [0.9529, 0.1699, 0.1723]
dropout mematikan neuron secara acak -> hasil berubah tiap panggilan
mode eval (model.training = False )
pemanggilan 1: [0.6465, -0.1521, 0.3053]
pemanggilan 2: [0.6465, -0.1521, 0.3053]
dropout dimatikan -> hasil identik dan stabilPada mode train, dua pemanggilan menghasilkan keluaran berbeda karena mask Dropout diacak ulang tiap forward. Pada mode eval, keluaran identik — seluruh neuron menyala kembali. Skala antar mode tetap seimbang karena saat training Dropout mengalikan neuron yang bertahan dengan 1/(1−p), sehingga rata-rata besaran aktivasi sama dengan mode evaluasi.
Komponen nn yang Paling Sering Dijumpai #
| Komponen | Peran |
|---|---|
nn.Linear | layer fully-connected y = xWᵀ + b |
nn.ReLU, nn.Tanh, nn.Sigmoid | aktivasi elementwise tanpa parameter |
nn.Sequential | wadah berurutan tanpa perlu kelas custom |
nn.Dropout | mematikan neuron acak saat training (regularisasi) |
nn.BatchNorm1d | menormalisasi statistik antar layer |
nn.Embedding | memetakan indeks token ke vektor padat |
nn.MSELoss, nn.CrossEntropyLoss, … | fungsi loss sebagai nn.Module |
Katalog lengkapnya — ratusan layer, loss, dan utilitas — terdokumentasi di docs.pytorch.org/docs/stable/nn.html
, dan kontrak nn.Module di docs.pytorch.org/docs/stable/generated/torch.nn.Module.html
. Model kini punya rumah yang rapi dan parameter yang tercatat otomatis. Dua penghuni siklus pelatihan masih kosong: fungsi loss yang menghakimi prediksi, dan optimizer yang menggeser parameter — keduanya dibahas sekaligus di chapter berikutnya.