Dataset menyediakan vektor 64 fitur dan sepuluh kelas; kini kita merancang jaringan yang menjembatani keduanya. Pilihan arsitektur untuk proyek ini adalah MLP tiga layer tersembunyi — cukup dalam untuk menangkap pola goresan tulisan tangan, namun cukup kecil agar pelatihannya selesai dalam hitungan detik di CPU. Seluruh definisi model hidup dalam satu berkas src/model.py sehingga skrip pelatihan dan skrip inferensi mengimpor model yang sama.
Arsitektur yang Dipilih #
Alur data pada model ini berjalan lurus dari vektor piksel ke skor kelas:
| Tahap | Ukuran | Peran |
|---|---|---|
| Masukan | 64 | piksel gambar yang telah distandardisasi |
fc1 + ReLU + Dropout | 64 | layer tersembunyi pertama |
fc2 + ReLU + Dropout | 64 | layer tersembunyi kedua |
fc3 (keluaran) | 10 | logits untuk digit 0-9 |
Definisi lengkapnya dalam src/model.py:
"""Model MLP untuk klasifikasi digit tulisan tangan 8x8.
Dataset: sklearn.datasets.load_digits -> 1797 gambar 8x8 piksel, 10 kelas.
Arsitektur: 64 -> 64 -> 64 -> 10 dengan aktivasi ReLU dan Dropout.
"""
import torch
import torch.nn as nn
class MLPDigit(nn.Module):
"""Multilayer perceptron untuk vektor 64 piksel gambar digit."""
def __init__(self, masukan=64, tersembunyi=64, kelas=10, dropout=0.2):
super().__init__()
self.fc1 = nn.Linear(masukan, tersembunyi)
self.fc2 = nn.Linear(tersembunyi, tersembunyi)
self.fc3 = nn.Linear(tersembunyi, kelas)
self.aktivasi = nn.ReLU()
self.dropout = nn.Dropout(dropout)
def forward(self, x):
x = x.flatten(start_dim=1) # (N, 8, 8) atau (N, 64) -> (N, 64)
x = self.dropout(self.aktivasi(self.fc1(x)))
x = self.dropout(self.aktivasi(self.fc2(x)))
return self.fc3(x) # logits (N, 10)
def hitung_jumlah_parameter(model):
"""Hitung total elemen seluruh tensor parameter sebuah model."""
return sum(p.numel() for p in model.parameters())Mari kita telaah kode di atas baris per baris:
class MLPDigit(nn.Module): seluruh model PyTorch mewarisinn.Module; mekanisme ini yang mendaftarkan setiapnn.Linearagar parameternya terlacak optimizer — pola yang dibahas di chapter Membangun Model dengan nn.Module .__init__dengan argumen bawaan:masukan=64, tersembunyi=64, kelas=10membuat arsitektur mudah diubah dari satu tempat tanpa menyentuhforward.self.fc1hinggaself.fc3: tigann.Linearmembentuk rantai 64-64-64-10; namafc(fully-connected) adalah konvensi umum untuk layer padat.x.flatten(start_dim=1): mem ratakan masukan(N, 8, 8)menjadi(N, 64)sambil menjaga dimensi batch — model menerima kedua bentuk masukan.self.dropout(...)setelah ReLU: Dropout mematikan 20 persen neuron secara acak saat pelatihan sehingga model tidak bergantung pada satu jalur fitur tertentu (lihat Fungsi Aktivasi dan pembahasan regularisasi).return self.fc3(x)tanpa aktivasi: layer terakhir menghasilkan logits mentah karenaCrossEntropyLossmilik kita mengharapkan logits, bukan probabilitas.hitung_jumlah_parameter: menjumlahkanp.numel()seluruh parameter — angka yang kita klaim di laporan harus bisa dihitung ulang dari sini.
Menguji Model Sebelum Dilatih #
Berkas tests/test_model.py memeriksa hal-hal yang murah diuji sebelum pelatihan: bentuk keluaran dari dua bentuk masukan, jumlah parameter, serta perilaku Dropout pada mode train dan eval. Inti pemeriksaannya:
model = MLPDigit()
# 1. Bentuk keluaran: masukan (N, 8, 8) maupun (N, 64) -> (N, 10).
x_kotak = torch.rand(4, 8, 8)
x_datar = torch.rand(4, 64)
assert model(x_kotak).shape == (4, 10), "bentuk keluaran (N, 8, 8) salah"
assert model(x_datar).shape == (4, 10), "bentuk keluaran (N, 64) salah"
# 2. Jumlah parameter sesuai arsitektur 64-64-64-10.
diharapkan = (64 * 64 + 64) + (64 * 64 + 64) + (64 * 10 + 10)
jumlah = hitung_jumlah_parameter(model)
assert jumlah == diharapkan, f"jumlah parameter {jumlah} != {diharapkan}"Output yang Dihasilkan (python3 tests/test_model.py):
parameter : 8970
bentuk keluaran : (4, 10)
SEMUA TEST LULUSModel memiliki 8.970 parameter — angka yang diperoleh dari (64×64+64) + (64×64+64) + (64×10+10), bukan tebakan. Pengujian mode eval memastikan dua forward memberi hasil identik karena Dropout mati, sementara mode train sengaja diperiksa mengubah keluaran antar pemanggilan.
Model terdefinisi dan lolos uji bentuk, tetapi bobotnya masih acak — akurasinya hari ini hanya sekitar peluang tebakan acak. Bagian berikutnya menyusun pipeline yang mengubah 8.970 parameter acak tersebut menjadi pengenalan digit dengan akurasi di atas 95 persen.