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:

python
"""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:

text
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:

  1. Baris 9 (class MLP(nn.Module)): model mewarisi nn.Module — dari sinilah berasal parameters(), train(), eval(), dan pencetakan struktur.
  2. Baris 13 (super().__init__()): baris wajib pertama __init__; tanpa baris ini PyTorch menolak pendaftaran atribut layer dengan galat eksplisit. Setelahnya, tiap nn.Linear yang disimpan sebagai atribut otomatis tercatat sebagai submodule.
  3. Baris 14-16 (nn.Linear): satu layer fully-connected y = xWᵀ + b. Perhatikan urutan argumennya (masuk, keluar) — tetapi weight tersimpan dengan shape (keluar, masuk): hidden1.weight berukuran (16, 4). Bobot diinisialisasi acak mengikuti seed; bias diisi nol.
  4. Baris 17 (nn.ReLU()): aktivasi elementwise tanpa parameter; disimpan sebagai atribut agar forward rapi.
  5. Baris 19-22 (forward): alur data dibaca dari atas ke bawah — x menembus hidden1, ReLU, hidden2, ReLU, lalu output. Layer terakhir sengaja polos tanpa aktivasi; alasan strategisnya dibahas di chapter Loss dan Optimizer .
  6. Baris 25-28 (print(model)): nn.Module tahu mencetak dirinya — tiap baris menampilkan nama atribut dan isinya, struktur yang persis mengikuti deklarasi __init__.
  7. Baris 30-34 (parameters()): iterator seluruh tensor daun ber-requires_grad, dari submodule terdalam. sum(p.numel() ...) menjumlah 243 = (4·16+16) + (16·8+8) + (8·3+3) — angka yang pada section Dasar Neural Network kita lacak manual lewat shape, kini dihitung pustaka.
  8. Baris 37-41 (forward dummy): batch (8, 4) — delapan contoh, empat fitur — mengalir menjadi (8, 3). Blok torch.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:

python
"""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:

text
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 stabil

Pada 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 #

KomponenPeran
nn.Linearlayer fully-connected y = xWᵀ + b
nn.ReLU, nn.Tanh, nn.Sigmoidaktivasi elementwise tanpa parameter
nn.Sequentialwadah berurutan tanpa perlu kelas custom
nn.Dropoutmematikan neuron acak saat training (regularisasi)
nn.BatchNorm1dmenormalisasi statistik antar layer
nn.Embeddingmemetakan 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.