Model neural network tidak membaca dunia secara langsung; model hanya menerima tensor angka yang kita susun dari data mentah. Kualitas tensor itu menjadi batas atas performa — garbage in, garbage out berlaku penuh: arsitektur tercanggih pun tidak bisa mempelajari pola dari data yang memuat nilai hilang, baris ganda, dan label teks. Chapter ini merangkai siklus penyiapan dataset secara utuh, lalu mengeksekusi tahap pembersihannya pada DataFrame kecil yang sengaja diberi cacat.

Siklus Penyiapan Dataset #

Penyiapan dataset mengikuti lima tahap yang urutannya tidak boleh ditukar:

  1. Pengumpulan — menghimpun data mentah dari berkas CSV, basis data, sensor, atau API.
  2. Pembersihan — memperbaiki nilai hilang, menghapus baris duplikat, dan memetakan label string ke indeks kelas.
  3. Pembagian subset — memisahkan data latih, validasi, dan test.
  4. Penskalaan fitur — menyamakan rentang antar fitur memakai statistik data latih saja.
  5. Batching — mengemas tensor ke DataLoader supaya model menerima potongan data secara bergilir.

Urutan tersebut menjaga dua sifat penting. Pembersihan dilakukan sebelum pembagian supaya baris duplikat tidak terpisah ke dua subset — duplikat yang menyeberang latih dan validasi membuat metrik validasi terlihat lebih baik dari seharusnya. Penskalaan dilakukan setelah pembagian supaya statistiknya hanya dihitung dari data latih; alasan teknisnya dibahas tuntas di chapter Normalisasi dan Standardisasi .

Masalah Data Kotor #

Data mentah nyaris selalu membawa cacat; empat jenis paling sering beserta penanganannya dirangkum pada tabel berikut:

CacatGejala pada dataPenanganan umum
Nilai hilang (NaN)sel numerik kosong atau tak terbacaisi dengan median/rata-rata kolom, atau hapus barisnya
Baris duplikatsampel identik tercatat dua kalidrop_duplicates()
Label berupa stringkolom target memuat teks "sehat"/"rusak"petakan ke indeks kelas 0/1/...
Skala fitur berbeda jauhsatu kolom bernilai ribuan, kolom lain pecahanstandardisasi atau normalisasi min-max

Contoh: Dari DataFrame Kotor ke Tensor #

Contoh memakai data sensor ruang mesin fiktif — 20 baris, dua fitur numerik (suhu, getaran), satu kolom label string (kondisi), plus 2 nilai NaN dan 2 baris duplikat yang sengaja ditanam. Berkas siapkan_data.py menjalankan diagnosis, pembersihan, konversi ke tensor, hingga pratinjau satu batch:

python
"""Menyiapkan dataset: dari DataFrame kotor menjadi tensor siap dilatih.

Siklus penyiapan data terdiri dari lima tahap: pengumpulan, pembersihan,
pembagian subset, normalisasi, dan batching. Contoh ini mengeksekusi
tahap pembersihan (NaN, duplikat, label string) lalu konversi fitur
menjadi tensor PyTorch dan satu contoh batch dari DataLoader.
"""

import numpy as np
import pandas as pd
import torch
from torch.utils.data import DataLoader, TensorDataset

torch.manual_seed(42)
np.random.seed(42)

# ---------- 1. Data mentah: catatan sensor ruang mesin fiktif ----------
data_mentah = pd.DataFrame(
    {
        "suhu": [
            61.0, 58.5, 60.2, 61.0, 57.1, np.nan, 60.8, 59.4,
            60.2, 62.0, 57.6, np.nan, 60.5, 58.9, 59.8, 61.4,
            58.2, 60.1, 57.3, 60.6,
        ],
        "getaran": [
            1.20, 2.85, 1.05, 1.20, 3.30, 2.10, 0.95, 2.40,
            1.05, 0.75, 3.15, 1.90, 1.10, 2.55, 1.35, 0.65,
            3.00, 1.50, 3.45, 1.15,
        ],
        "kondisi": [
            "sehat", "rusak", "sehat", "sehat", "rusak", "rusak",
            "sehat", "rusak", "sehat", "sehat", "rusak", "rusak",
            "sehat", "rusak", "sehat", "sehat", "rusak", "sehat",
            "rusak", "sehat",
        ],
    }
)

print("===== TAHAP 1-2: DATA MENTAH DAN DIAGNOSIS =====")
print("cuplikan data mentah (6 baris pertama dari", len(data_mentah), "):")
print(data_mentah.head(6).to_string(index=False))
print()
print("jumlah baris          :", len(data_mentah))
print("nilai NaN per kolom   :", data_mentah.isna().sum().to_dict())
print("baris duplikat        :", int(data_mentah.duplicated().sum()))
print("label unik (string)   :", sorted(data_mentah["kondisi"].unique()))

# ---------- 3. Pembersihan: duplikat, NaN, label string ----------
bersih = data_mentah.drop_duplicates().reset_index(drop=True)

median_suhu = bersih["suhu"].median()
bersih["suhu"] = bersih["suhu"].fillna(median_suhu)

peta_label = {"sehat": 0, "rusak": 1}
bersih["kondisi"] = bersih["kondisi"].map(peta_label)

print()
print("===== HASIL PEMBERSIHAN =====")
print("median suhu (pengisi NaN) :", round(median_suhu, 2))
print("jumlah baris setelah bersih:", len(bersih), "(20 - 2 duplikat)")
print("sisa nilai NaN             :", int(bersih.isna().sum().sum()))
print("label setelah dipetakan    :", sorted(int(v) for v in bersih["kondisi"].unique()))

# ---------- 4. Fitur dan label menjadi tensor ----------
fitur = bersih[["suhu", "getaran"]].to_numpy(dtype=np.float32)
label = bersih["kondisi"].to_numpy(dtype=np.int64)

X = torch.from_numpy(fitur)
y = torch.from_numpy(label)

print()
print("===== KONVERSI KE TENSOR =====")
print("tensor fitur X :", tuple(X.shape), "| dtype", X.dtype)
print("tensor label y :", tuple(y.shape), "| dtype", y.dtype)
print("baris pertama X:", [round(v, 2) for v in X[0].tolist()], "| label y[0]:", y[0].item())

# ---------- 5. Pratinjau batching ----------
dataset = TensorDataset(X, y)
loader = DataLoader(
    dataset,
    batch_size=6,
    shuffle=True,
    generator=torch.Generator().manual_seed(42),
)
X_batch, y_batch = next(iter(loader))
print()
print("===== PRATINJAU BATCHING =====")
print("satu batch: fitur", tuple(X_batch.shape), "| label", y_batch.tolist())

Output yang Dihasilkan:

text
===== TAHAP 1-2: DATA MENTAH DAN DIAGNOSIS =====
cuplikan data mentah (6 baris pertama dari 20 ):
 suhu  getaran kondisi
 61.0     1.20   sehat
 58.5     2.85   rusak
 60.2     1.05   sehat
 61.0     1.20   sehat
 57.1     3.30   rusak
  NaN     2.10   rusak

jumlah baris          : 20
nilai NaN per kolom   : {'suhu': 2, 'getaran': 0, 'kondisi': 0}
baris duplikat        : 2
label unik (string)   : ['rusak', 'sehat']

===== HASIL PEMBERSIHAN =====
median suhu (pengisi NaN) : 59.95
jumlah baris setelah bersih: 18 (20 - 2 duplikat)
sisa nilai NaN             : 0
label setelah dipetakan    : [0, 1]

===== KONVERSI KE TENSOR =====
tensor fitur X : (18, 2) | dtype torch.float32
tensor label y : (18,) | dtype torch.int64
baris pertama X: [61.0, 1.2] | label y[0]: 0

===== PRATINJAU BATCHING =====
satu batch: fitur (6, 2) | label [1, 0, 0, 1, 0, 0]

Contoh di atas diuji nyata dengan pandas 2.2.3 dan PyTorch 2.14.1+cpu pada mesin CPU. Mari kita telaah kode di atas baris per baris:

  1. Baris 9-12 (impor): pandas menyediakan DataFrame beserta operasi pembersihannya; TensorDataset dan DataLoader dari PyTorch dipakai saat batching.
  2. Baris 14-15 (seed): kedua seed mengunci seluruh sumber keacakan sehingga hasil eksekusi identik setiap dijalankan.
  3. Baris 18-37 (data mentah): DataFrame dibangun dari tiga kolom; np.nan menandai sel suhu yang hilang, dan baris keempat mengulang baris pertama.
  4. Baris 39-46 (diagnosis): isna().sum() menghitung NaN per kolom, duplicated().sum() menghitung baris ganda, dan unique() memperlihatkan label masih string — tiga angka ini yang menentukan pilihan penanganan.
  5. Baris 49 (duplikat): drop_duplicates() membuang baris ganda; reset_index(drop=True) merapikan indeks agar berurutan dari nol.
  6. Baris 51-52 (nilai hilang): median dipilih pengisi NaN karena tahan nilai pencilan; keluaran menegaskan sisa nilai NaN: 0.
  7. Baris 54-55 (label): map() menerjemahkan "sehat"→0 dan "rusak"→1 — model klasifikasi menuntut target berupa indeks kelas.
  8. Baris 65-69 (konversi): to_numpy(dtype=...) menghasilkan array float32 (fitur) dan int64 (label) — dtype yang tepat mencegah konversi ulang; torch.from_numpy() membungkusnya tanpa menyalin data.
  9. Baris 78-85 (batching): TensorDataset memasangkan fitur-label per baris; DataLoader(batch_size=6, shuffle=True, generator=...) memotongnya menjadi batch acak yang dapat diulang — pola lengkap di chapter Dataset dan DataLoader .

Keluaran merangkum perjalanan itu: 20 baris menjadi 18 (20 - 2 duplikat), 2 NaN terisi median 59.95, label [0, 1] siap untuk CrossEntropyLoss, dan satu batch (6, 2) terpotong rapi dari tensor (18, 2) — urutan labelnya efek shuffle=True, bukan kesalahan.

Pada proyek nyata, kelima tahap dibungkus menjadi satu fungsi atau skrip yang dijalankan ulang setiap kali data mentah berubah — mengganti pengisi NaN dari median ke rata-rata, misalnya, cukup diubah di satu tempat. Dokumentasi operasi pandas yang dipakai tersedia di pandas.DataFrame.drop_duplicates dan pandas.DataFrame.fillna ; strategi pengisian nilai hilang yang lebih lengkap dibahas scikit-learn di modules/impute .

Tensor kini bersih, tetapi satu keputusan besar belum diambil: sampel mana yang boleh dilihat model saat belajar dan mana yang disimpan untuk ujian akhir. Kesalahan pada tahap ini menghasilkan angka evaluasi yang menipu — topik chapter berikutnya.