Belajar AI #11: Neural Network Dasar — Neuron, Activation Function, dan Layer

Dulu saya membayangkan neural network sebagai tiruan otak yang mistis, penuh keajaiban yang tidak akan pernah saya pahami. Kenyataannya setelah berani membuka tudung mesinnya: neural network adalah tumpukan operasi perkalian matriks yang diselingi fungsi non-linear sederhana. Tidak ada sihir, hanya aljabar linear yang diulang jutaan kali. Episode ini catatan saya membedah tiga blok bangunannya: neuron, activation function, dan layer.

Neuron: Fungsi Paling Membosankan di Dunia

Satu neuron sebenarnya cuma melakukan dua langkah. Pertama, kalikan setiap input dengan bobotnya, jumlahkan semuanya, tambahkan bias — ini persis regresi linear yang sudah kita bahas di episode awal. Kedua, masukkan hasilnya ke sebuah fungsi aktivasi. Secara matematis: z = w·x + b, lalu a = g(z). Selesai. Tidak ada dendrit, tidak ada sinaps yang hidup, hanya perkalian dan penjumlahan.

  • Bobot (w): seberapa penting tiap input. Ini parameter yang dioptimasi saat training.
  • Bias (b): geser ambang keputusan, seperti intercept pada regresi.
  • Aktivasi (g): fungsi non-linear yang memberi jaringan kemampuan memodelkan hubungan rumit.

Kenapa Butuh Activation Function

Ingin fakta yang membuat saya terkejut pertama kali mendengarnya: kalau Anda menyusun banyak layer tanpa fungsi aktivasi non-linear, seluruh jaringan itu runtuh menjadi satu regresi linear biasa. Komposisi dari fungsi-fungsi linear tetaplah linear. Neural network dengan seratus layer tanpa aktivasi sama saja dengan regresi linear yang sangat mahal.

Makanya activation function bukan detail teknis, tapi syarat eksistensinya deep learning. Beberapa yang paling sering dipakai:

  • ReLU: kembalikan input jika positif, nol jika negatif. Super murah dihitung, default untuk hidden layer modern.
  • Sigmoid: memetakan ke rentang 0 sampai 1, cocok untuk output probabilitas biner.
  • Tanh: mirip sigmoid tapi rentangnya -1 sampai 1, kadang lebih stabil untuk hidden layer.
  • Softmax: mengubah vektor angka menjadi distribusi probabilitas, standar untuk output multi-kelas.

Membedah Lebih Dalam: Sigmoid, Tanh, ReLU, Leaky ReLU, GELU

Saya merasa pemahaman saya melompat jauh saat mulai menulis setiap fungsi aktivasi dari nol. Berikut catatan masing-masing beserta kode Python sederhananya:

import numpy as np

def sigmoid(z):
    return 1 / (1 + np.exp(-z))

def tanh(z):
    return np.tanh(z)

def relu(z):
    return np.maximum(0, z)

def leaky_relu(z, alpha=0.01):
    return np.where(z > 0, z, alpha * z)

def gelu(z):
    return 0.5 * z * (1 + np.tanh(np.sqrt(2/np.pi) * (z + 0.044715 * z**3)))

Masing-masing punya karakter unik. Sigmoid mudah diferensiasi tapi menderita vanishing gradient — gradien mengecil mendekati nol di ekor distribusi, sehingga layer dalam berhenti belajar. Tanh sedikit lebih baik karena terpusat di nol, tapi masalah vanishing gradient tetap ada. ReLU memecah masalah itu untuk sebagian besar kasus: gradien konstan satu saat z positif. Namun ReLU punya “dying neuron” — neuron yang selalu output nol dan berhenti update sama sekali. Leaky ReLU memperbaikinya dengan mengizinkan sedikit gradien saat z negatif. GELU, yang dipopulerkan oleh transformer dan BERT, memperlakukan aktivasi sebagai probabilitas — input dengan nilai absolut besar tetap lewat, tapi mendapat penalti lunak. Dalam praktik modern, GELU menjadi default untuk arsitektur transformer.

ActivationRentang OutputGradienCocok Untuk
Sigmoid(0, 1)V vanishingOutput biner
Tanh(-1, 1)Sedikit vanishingHidden layer kecil
ReLU[0, ∞)Stabil, bisa 0Hidden layer default
Leaky ReLU(-∞, ∞)Ada saat z < 0Mencegah dying neuron
GELU(-∞, ∞)SmoothTransformer, NLP

Arsitektur Jaringan: Feedforward dan Recurrent

Setelah memahami neuron dan aktivasi, pertanyaan logisnya: bagaimana cara menyusun mereka? Dua pola utama yang saya pelajari pertama kali.

Feedforward neural network adalah yang paling sederhana. Data mengalir satu arah: input → hidden layer → output. Tidak ada loop, tidak ada memori. Cocok untuk data tabular, klasifikasi gambar sederhana, dan regresi. Arsitektur yang kita bahas di episode ini — neuron tersusun berlapis, setiap layer hanya melihat output layer sebelumnya — semuanya feedforward.

Recurrent neural network (RNN) menambahkan satu konsep: state. Pada setiap timestep, neuron menerima input ditambah output sebelumnya. Ini memungkinkan jaringan mengingat konteks sejarah, cocok untuk data sekuensial seperti teks atau time series. Sayangnya, RNN klasik punya masalah vanishing gradient yang parah untuk sekuens panjang, yang akhirnya diatasi oleh LSTM dan GRU — tapi itu cerita episode lain.

Weight Initialization: Kenapa Random Saja Tidak Cukup

Satu pelajaran yang menohok: semula saya menginisialisasi semua bobot dengan nol. Hasilnya? Seluruh neuron di satu layer belajar hal yang persis sama — symmetric initialization membunuh keberagaman. Solusinya: inisialisasi acak. Tapi tidak asal acak. Dua pendekatan utama:

Xavier/Glorot Initialization: scale bobot berbanding terbalik dengan jumlah input dan output neuron: W ~ N(0, 1/n_in) atau seragam antara ±√(6/(n_in + n_out)). Dirancang agar variance gradien tetap stabil melintasi layer — cocok untuk sigmoid dan tanh. He Initialization: dirancang khusus untuk ReLU, menggandakan scale menjadi W ~ N(0, 2/n_in) karena ReLU membuang setengah distribusi. Dalam PyTorch, ini sudah built-in: nn.init.xavier_uniform_() dan nn.init.kaiming_uniform_(). Saya belajar bahwa memilih initialization yang tepat bisa mempercepat konvergensi training dua hingga tiga kali lipat dibanding inisialisasi acak sembarang.

Membangun Neural Network dengan PyTorch

Setelah memahami teori, saya langsung praktik membuat multi-layer neural network dari nol menggunakan PyTorch. Ini kode paling sederhana yang saya tulis:

import torch
import torch.nn as nn

class SimpleNet(nn.Module):
    def __init__(self, input_dim, hidden1, hidden2, output_dim):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_dim, hidden1),
            nn.ReLU(),
            nn.Linear(hidden1, hidden2),
            nn.GELU(),
            nn.Linear(hidden2, output_dim),
        )

    def forward(self, x):
        return self.layers(x)

model = SimpleNet(input_dim=4, hidden1=16, hidden2=8, output_dim=3)
print(model)
print(f"Total parameters: {sum(p.numel() for p in model.parameters())}")

# Forward pass
x = torch.randn(32, 4)
logits = model(x)
print(logits.shape)  # torch.Size([32, 3])

Perhatikan dua hal yang berbeda dari kode NumPy sebelumnya: pertama, PyTorch secara otomatis menginisialisasi bobot dengan distribusi yang masuk akal (Kaiming untuk ReLU). Kedua, model ini mendukung backpropagation secara cuma-cuma — cukup panggil loss.backward() dan gradien mengalir ke seluruh parameter. Inilah keindahan framework modern: kita fokus pada arsitektur, framework menangani matematika training.

Universal Approximation Theorem: Jaringan Bisa Memodelkan Apapun

Salah satu konsep yang mengubah cara saya memandang neural network: Universal Approximation Theorem. Dalam bahasa sederhana, jaringan feedforward dengan satu hidden layer yang cukup lebar bisa mendekati fungsi apapun dengan presisi yang diinginkan — asalkan fungsi aktivasinya non-linear.

Analoginya seperti ini: bayangkan sebuah studio seni digital dengan pixel tak terbatas. Neural network dengan satu hidden layer adalah kanvas kosong, neuron adalah pixel-nya. Tambah cukup banyak pixel, dan Anda bisa mereproduksi foto apapun — dari wajah manusia hingga pemandangan alam. Namun ada tangkapan: mungkin diperlukan jutaan pixel untuk sesuatu yang sebenarnya bisa digambar dalam seribu garis geometris. Inilah mengapa deep learning memilih jaringan dalam (banyak layer dangkal) daripada jaringan lebar (satu layer sangat lebar) — kedalaman memberikan efisiensi representasi yang jauh lebih baik. Theorem ini menjamin bahwa neural network cukup powerful secara teori, tapi tidak memberitahu kita arsitektur mana yang paling efisien secara praktis.

Hyperparameter Tuning: Menyetel Knob Jaringan

Setelah jaringan bisa berjalan, pertanyaannya berubah menjadi: bagaimana membuatnya belajar dengan baik? Ada beberapa hyperparameter utama yang saya pelajari dari kesalahan:

  • Learning rate: terlalu besar → training divergen; terlalu kecil → training sangat lambat. Saya biasanya mulai dari 0.001, lalu menurunkan jika loss loncat-loncat. Learning rate schedule (menurunkan secara berkala) adalah teknik sederhana tapi sangat efektif.
  • Jumlah hidden layer: mulai dari 1-2 layer. Lebih dalam tidak selalu lebih baik — ada titik di mana gradien mengecil dan training berhenti. Untuk data tabular sederhana, dua hingga tiga layer sudah sering cukup.
  • Neuron per layer: kekuatan representasi. Tapi terlalu banyak neuron di satu layer bisa menyebabkan overfitting — model menghafal data training tapi gagal pada data baru. Pola piramida (banyak di awal, sedikit di akhir) sering menjadi titik awal yang baik.

Saya belajar bahwa hyperparameter tuning lebih merupakan seni daripada sains. Tidak ada rumus pasti yang cocok untuk semua masalah. Pendekatan terbaik yang saya temukan: mulai dengan baseline sederhana, ukur metrik, ubah satu hyperparameter pada satu waktu, dan catat setiap percobaan.

Visualisasi Sederhana Jaringan Saraf

Untuk memperkuat pemahaman visual, berikut diagram sederhana dari arsitektur jaringan yang kita bahas — sebuah feedforward network dengan dua hidden layer:

  INPUT LAYER       HIDDEN LAYER 1      HIDDEN LAYER 2      OUTPUT LAYER
  (4 fitur)          (8 neurons)         (8 neurons)         (3 kelas)

   [x1] ──────┐   ┌── [h1_1] ────┐   ┌── [h2_1] ────┐
               ├──→├── [h1_2] ────┤   ├──→├── [h2_1] ────┤
   [x2] ──────┤   ├── [h1_3] ────┤   ├──→├── [h2_2] ────┤
               ├──→├── [h1_4] ────┼──→├──→├── [h2_3] ────┼──→ [y1]
   [x3] ──────┤   ├── [h1_5] ────┤   ├──→├── ...         │
               ├──→├── [h1_6] ────┤   └──→└── [h2_8] ────┘──→ [y2]
   [x4] ──────┘   ├── [h1_7] ────┘                     ───→ [y3]
                   └── [h1_8] ────┘

   Tiap panah = W (bobot) + b (bias) + ReLU/GELU (aktivasi)
   Data mengalir satu arah: kiri ke kanan. Tidak ada loop.

Menghitung Jumlah Parameter

Kalau model adalah fungsi, jumlah parameter adalah jumlah “knob” yang dioptimasi training. Hitungannya sederhana: untuk layer yang menghubungkan n input ke m neuron, bobotnya n×m ditambah m bias. Dari contoh di atas:

LayerBobotBiasTotal
Input ke hidden (4 ke 8)4 × 8 = 32840
Hidden ke output (8 ke 3)8 × 3 = 24327
Total—67

67 parameter terdengar remeh. Sekarang naikkan skalanya: model bahasa modern punya miliaran parameter, artinya miliaran knob yang semuanya disentuh optimizer di setiap langkah training. Itulah alasan episode-episode berikutnya bicara soal GPU dan teknik training yang makin canggih.

Shape Error: Musuh Pertama Saya

Pengalaman pribadi: sembilan dari sepuluh bug di minggu-minggu awal saya belajar neural network bukan soal matematika, tapi salah bentuk matriks. Matrix multiplication itu picky — (32, 4) dikali (8, 3) langsung error karena kolom pertama tidak cocok dengan baris kedua. Tips yang menyelamatkan saya: tulis komentar shape di sebelah kanan hampir setiap baris kode, persis seperti contoh di atas. Repot sebentar, hemat berjam-jam debugging.

Momen Paham: Neural Network = Nested Functions

Ada satu momen yang tidak akan saya lupakan saat belajar materi ini. Saya duduk memandangi diagram jaringan selama hampir satu jam, lalu tiba-tiba menyadari sesuatu yang sederhana tapi mengubah segalanya: neural network hanyalah fungsi bersarang. Satu layer adalah f(x), layer berikutnya adalah g(f(x)), layer ketiga adalah h(g(f(x))). Tidak lebih, tidak kurang. Setiap kali saya merasa kebingungan dengan arsitektur yang rumit — attention mechanism, residual connections, batch normalization — saya kembali ke realisasi ini dan semuanya menjadi lebih mudah dicerna. Deep learning itu “deep” karena fungsi-fungsi itu bertumpuk banyak. Kecanggihannya datang dari pilihan fungsi mana yang ditumpuk, berapa banyak neuron di tiap level, dan bagaimana cara mereka belajar dari data.

Dengan tiga blok bangunan ini — neuron, aktivasi, layer — Anda sudah punya model mental lengkap untuk membaca arsitektur apapun. Pertanyaan berikutnya: bagaimana jaringan ini belajar dari kesalahannya? Itu bahasan episode 12 tentang backpropagation.

Sumber

Tinggalkan Balasan

Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai *

Situs ini menggunakan Akismet untuk mengurangi spam. Pelajari bagaimana data komentar Anda diproses