Model deep learning hanya mengolah angka, tetapi data nyata datang dalam berbagai wujud: harga rumah bernilai ratusan ribu, jumlah kamar hanya satu digit, kategori berupa nama kota, dan label berupa teks. Cara kita menerjemahkan semua itu menjadi bilangan menentukan lancar atau tidaknya proses training. Skala fitur yang timpang membuat gradient descent berjalan tersendat, encoding kategori yang salah menciptakan urutan palsu, dan pemilihan dtype memengaruhi memori sekaligus akurasi perhitungan. Chapter ini membahas ketiganya dengan contoh NumPy yang dihitung nyata dari nol.
Mengapa Skala Fitur Penting #
Gradient descent memperbarui semua parameter dengan learning rate yang sama. Jika satu fitur bernilai ratusan ribu dan fitur lain bernilai satu digit, kontribusi fitur besar mendominasi perhitungan neuron, loss menjadi lembah yang memanjang, dan langkah pembaruan harus diambil sangat kecil supaya tidak melompati titik minimum. Kondisi serupa menimpa semua algoritma berbasis jarak: KNeighbors dan KMeans menghitung jarak antar data, sehingga fitur berskala besar secara otomatis dianggap “lebih penting” walau tidak ada alasannya. Dataset digits pada chapter sebelumnya selamat karena seluruh pikselnya memang berkisar 0-16 — keberuntungan yang jarang terjadi pada data nyata.
Normalisasi Min-Max dan Z-Score #
Dua teknik standar menyamakan skala seluruh fitur. Min-max memetakan setiap kolom ke rentang 0-1 lewat rumus (x - min) / (max - min). Z-score memetakan setiap kolom agar memiliki rata-rata 0 dan simpangan baku 1 lewat rumus (x - mean) / std. Berkas normalisasi_fitur.py menghitung keduanya secara manual pada data rumah:
"""Normalisasi min-maks dan z-score dihitung manual dengan NumPy."""
import numpy as np
data = np.array([
[120000.0, 3.0, 10.0], # [harga ribu, kamar, umur]
[350000.0, 4.0, 2.0],
[80000.0, 2.0, 25.0],
[500000.0, 5.0, 1.0],
])
harga = data[:, 0]
print("Harga asli :", harga)
print("Rentang harga : 80000.0 s.d. 500000.0")
minmax_manual = (harga - harga.min()) / (harga.max() - harga.min())
print("Min-maks kolom :", np.round(minmax_manual, 4))
zscore_manual = (harga - harga.mean()) / harga.std()
print("Z-score kolom :", np.round(zscore_manual, 4))
print("Cek mean/std z :", round(zscore_manual.mean(), 4), round(zscore_manual.std(), 4))
mins = data.min(axis=0)
maxs = data.max(axis=0)
minmax_matriks = (data - mins) / (maxs - mins)
print("Min-maks semua kolom:")
print(np.round(minmax_matriks, 4))
rata = data.mean(axis=0)
simpangan = data.std(axis=0)
zscore_matriks = (data - rata) / simpangan
print("Z-score semua kolom:")
print(np.round(zscore_matriks, 4))Output yang Dihasilkan:
Harga asli : [120000. 350000. 80000. 500000.]
Rentang harga : 80000.0 s.d. 500000.0
Min-maks kolom : [0.0952 0.6429 0. 1. ]
Z-score kolom : [-0.8308 0.5101 -1.064 1.3847]
Cek mean/std z : 0.0 1.0
Min-maks semua kolom:
[[0.0952 0.3333 0.375 ]
[0.6429 0.6667 0.0417]
[0. 0. 1. ]
[1. 1. 0. ]]
Z-score semua kolom:
[[-0.8308 -0.4472 0.0521]
[ 0.5101 0.4472 -0.7809]
[-1.064 -1.3416 1.6138]
[ 1.3847 1.3416 -0.885 ]]Mari kita telaah kode di atas baris per baris:
- Baris 5-10 (
data): empat rumah dengan tiga fitur berskala sangat berbeda — harga puluhan ribu hingga 500 ribu, kamar 2-5, umur 1-25. - Baris 16 (
minmax_manual): harga asli dipetakan ke[0.0952, 0.6429, 0, 1]— rumah termurah menjadi 0, termahal menjadi 1, sisanya proporsional di antara keduanya. - Baris 19-21 (
zscore_manual): harga dinormalkan dengan rata-rata dan simpangan baku; hasil cetak0.0dan1.0membuktikan kolom hasil benar memiliki rata-rata 0 dan simpangan baku 1. - Baris 23-25 (
minmax_matriks): versi matriks sekaligus —data.min(axis=0)menghasilkan vektor minimum tiap kolom, lalu broadcasting NumPy mengurangkan vektor tersebut dari setiap baris tanpa perulangan eksplisit. - Baris 29-31 (
zscore_matriks): pola yang sama untuk z-score. Setelah normalisasi, ketiga kolom berada pada skala yang sebanding dan siap masuk model.
One-Hot Encoding untuk Fitur Kategori #
Kategori seperti kota (Jakarta, Bandung, Surabaya) tidak punya urutan angka yang bermakna. Jika kita menggantinya dengan 0, 1, 2, model akan menganggap Bandung berada “di antara” Jakarta dan Surabaya — urutan palsu yang merusak pembelajaran. One-hot encoding menyelesaikannya dengan memberi satu kolom biner per kategori: baris berisi 1 pada kolom kategorinya dan 0 pada sisanya. Berkas onehot_presisi.py menerapkannya secara manual sekaligus menguji presisi dua dtype:
"""One-hot encoding manual dan konsekuensi presisi float32 vs float64."""
import numpy as np
kelas = np.array([0, 2, 1, 0]) # label kategori: 0, 1, 2
jumlah_kelas = 3
onehot = np.zeros((kelas.size, jumlah_kelas))
onehot[np.arange(kelas.size), kelas] = 1.0
print("Label :", kelas)
print("One-hot :")
print(onehot)
tambah32 = np.float32(1.0) + np.float32(1e-8)
tambah64 = np.float64(1.0) + np.float64(1e-8)
print("float32 1.0 + 1e-8 :", tambah32)
print("float64 1.0 + 1e-8 :", tambah64)
angka32 = np.full(100_000, 0.1, dtype=np.float32)
angka64 = np.full(100_000, 0.1, dtype=np.float64)
print("Jumlah 100000 x 0.1 (float32):", angka32.sum())
print("Jumlah 100000 x 0.1 (float64):", angka64.sum())
print("Ukuran float32:", angka32.itemsize, "byte | float64:", angka64.itemsize, "byte")Output yang Dihasilkan:
Label : [0 2 1 0]
One-hot :
[[1. 0. 0.]
[0. 0. 1.]
[0. 1. 0.]
[1. 0. 0.]]
float32 1.0 + 1e-8 : 1.0
float64 1.0 + 1e-8 : 1.00000001
Jumlah 100000 x 0.1 (float32): 10000.001
Jumlah 100000 x 0.1 (float64): 10000.000000000002
Ukuran float32: 4 byte | float64: 8 byteMari kita telaah kode di atas baris per baris:
- Baris 8-9 (
onehot): matriks nol berukuran 4×3 dibuat lebih dulu; pengindeksan mewahonehot[np.arange(4), kelas] = 1.0lalu menyalakan satu sel per baris sesuai labelnya — baris[0, 2, 1, 0]berubah menjadi pola biner pada keluaran. - Baris 14-17 (penjumlahan kecil): pada float32,
1.0 + 1e-8tetap tercetak1.0karena float32 hanya menyimpan sekitar 7 angka signifikan; float64 menyimpan sekitar 16 angka sehingga hasilnya1.00000001. - Baris 19-22 (penjumlahan besar): menjumlahkan
0.1sebanyak 100.000 kali menumpuk galat pembulatan — float32 meleset menjadi10000.001sedangkan float64 hanya meleset di digit ke-12. Nilai0.1sendiri memang tak dapat direpresentasikan tepat dalam biner. - Baris 23 (
itemsize): satu elemen float32 memakai 4 byte, setengah dari float64 — selisih yang berlipat ganda ketika model menyimpan miliaran parameter.
Memilih Presisi dan Format #
Deep learning hampir selalu memakai float32 sebagai dtype standar: memorinya setengah, komputasinya lebih cepat, dan 7 angka signifikan terbukti cukup untuk training model besar. float64 dipakai saat akurasi numerik penting — verifikasi gradien, perhitungan ilmiah, atau data berjumlah kecil. GPU modern menambahkan pilihan float16/bfloat16 dengan teknik mixed precision yang akan kita bahas pada bab training lanjutan. Ringkasan pilihan format data:
| Format / teknik | Kapan dipakai |
|---|---|
| Min-max scaling | rentang keluaran harus 0-1 (piksel), distribusi tidak perlu normal |
| Z-score standardisasi | algoritma berbasis gradient dan jarak, distribusi mendekati normal |
| One-hot encoding | kategori tanpa urutan: kota, warna, jenis produk |
| Label integer | target klasifikasi, atau kategori berurutan bermakna (rating 1-5) |
float32 | training deep learning standar — ringan dan cukup presisi |
float64 | verifikasi numerik, data kecil, eksperimen ilmiah |
Dokumentasi resmi NumPy merinci sistem dtype secara lengkap , dan panduan preprocessing scikit-learn menyediakan versi siap pakai dari normalisasi di atas. Data kini bersih skala dan berformat tepat, tetapi masih berbentuk matriks dua dimensi. Gambar berwarna, kumpulan kalimat, dan video membutuhkan struktur berdimensi lebih banyak — tensor — yang menjadi topik chapter penutup section ini.