Section Data Quality dan Observability sudah memberi kita perangkat validasi data umum: skema, kelengkapan, dan kontrak antar pipeline. Machine learning menambah tiga pemeriksaan yang tidak ada padanannya di dunia analytics, karena ketiganya berakar pada kontrak waktu antara training dan serving: data training harus lolos skema sebelum model menyentuhnya, distribusi data produksi harus diawasi terhadap distribusi training, dan fitur yang terlalu pintar memprediksi label harus dicurigai bocor. Chapter ini mengimplementasikan ketiganya dalam satu skrip dari nol, sehingga mekanismenya transparan sebelum kita memakai alat siap pakai.

Pemeriksaan A: Skema yang Memblokir Training #

Dataset fraud TokoKita dideklarasikan sebagai kontrak eksplisit: tipe setiap kolom dan rentang nilai yang sah. Deklarasi semacam ini adalah inti dari Schema Validation dan alat seperti Great Expectations maupun Pandera :

python
# --- Bagian A: validasi skema pada data training --------------------------------
SKEMA = {
    "tx_id": "int64",
    "customer_id": "object",
    "nilai": "int64",
    "jam": "int64",
    "is_fraud": "int64",
}
BATAS = {"jam": (0, 23), "nilai": (1_000, 50_000_000)}

Skrip kemudian membuat 500 baris data training dan menyuntikkan tiga kerusakan sengaja: kolom nilai bertipe float64 (seharusnya int64), satu baris ber-jam 25, dan satu baris bernilai negatif. Loop pemeriksaan mengumpulkan seluruh pelanggaran itu sebelum training dimulai:

python
pelanggaran = []
for kolom, tipe in SKEMA.items():
    if kolom in training.columns and str(training[kolom].dtype) != tipe:
        pelanggaran.append(f"dtype {kolom}: {training[kolom].dtype} != {tipe}")
for kolom, (bawah, atas) in BATAS.items():
    di_luar = ~training[kolom].between(bawah, atas)
    if di_luar.any():
        pelanggaran.append(
            f"rentang {kolom}: {di_luar.sum()} baris di luar [{bawah}, {atas}]"
        )
print("== A. Validasi skema ==")
print("\n".join(f"  [BLOKIR] {p}" for p in pelanggaran))

Output yang Dihasilkan:

text
== A. Validasi skema ==
  [BLOKIR] dtype nilai: float64 != int64
  [BLOKIR] rentang jam: 1 baris di luar [0, 23]
  [BLOKIR] rentang nilai: 1 baris di luar [1000, 50000000]

Label [BLOKIR] bukan hiasan: untuk data training, pelanggaran skema sebaiknya menghentikan pipeline, bukan sekadar dicatat. Melanjutkan training dengan jam 25 sama saja mengajari model bahwa dunia punya jam ke-25. Berbeda dari validasi pipeline analytics yang boleh memperingatkan lalu melanjutkan, validasi data training bersifat gerbang — satu pelanggaran berarti seluruh proses latihan ditunda sampai sumbernya dibereskan.

Pemeriksaan B: Drift Distribusi dengan PSI #

Data produksi bergeser perlahan: promo besar menaikkan nilai transaksi rata-rata, jam sibuk berpindah, pelanggan baru datang. Population Stability Index (PSI) mengukur seberapa jauh distribusi baru menjauh dari distribusi training dalam satu angka:

python
def psi(basis, baru, bin=10):
    """Population Stability Index antar dua distribusi dengan bin kuantil basis."""
    tepi = np.quantile(basis, np.linspace(0, 1, bin + 1))
    tepi[0], tepi[-1] = -np.inf, np.inf
    proporsi_a = np.histogram(basis, tepi)[0] / len(basis)
    proporsi_b = np.histogram(baru, tepi)[0] / len(baru)
    eps = 1e-6
    return float(np.sum((proporsi_a - proporsi_b) * np.log((proporsi_a + eps) / (proporsi_b + eps))))

Mari kita telaah baris-baris kode di atas:

  1. Baris 3-4 (tepi = ...): distribusi basis dipotong menjadi 10 bin kuantil, sehingga bin berisi jumlah baris yang merata; batas terluar direntangkan agar nilai ekstrem tetap tertampung.
  2. Baris 5-6 (proporsi_a, proporsi_b): kedua distribusi dihitung proporsinya pada bin yang sama — inilah perbandingan apple-to-apple antar waktu.
  3. Baris 7-8 (return ...): setiap bin menyumbang selisih proporsi dikali log rasio; bin yang proporsinya menyusut drastis memberi kontribusi besar. Konstanta eps menjaga logaritma tetap aman saat sebuah bin kosong.

Skrip membandingkan distribusi nilai dari 5.000 baris training terhadap dua kandidat minggu produksi: 2.000 transaksi minggu biasa, dan 2.000 transaksi setelah kampanye promo:

Output yang Dihasilkan:

text
== B. PSI distribusi 'nilai' terhadap training ==
  minggu biasa   : PSI 0.007 -> stabil
  setelah promo  : PSI 1.283 -> DRIFT
PSIVonisTindakan
di bawah 0.10stabillanjutkan
0.10 - 0.25waspada driftperiksa fitur lain, siapkan data baru
di atas 0.25driftdata produksi sudah jauh dari training

PSI dihitung per fitur, bukan per dataset — model dengan 40 fitur membutuhkan 40 angka PSI setiap periode pemantauan, dan cukup satu fitur yang drift untuk menurunkan kualitas model. Laporan mingguan yang baik mengurutkan fitur dari PSI tertinggi, sehingga tim tahu harus menyelidiki sumber data mana lebih dulu; angka PSI juga menjadi input langsung keputusan retraining yang kita rangkai di chapter Integrasi dengan MLOps.

Pemeriksaan C: Deteksi Dini Leakage #

Pemeriksaan ketiga menjangkar kembali ke pelajaran chapter Kebutuhan Data Machine Learning. Fitur yang berkorelasi terlalu kuat dengan label adalah kandidat bocor, karena fitur jujur hampir selalu bising:

python
label = rng.integers(0, 2, 2_000)
fitur_jujur = np.where(label == 1, rng.normal(150_000, 40_000, 2_000),
                       rng.normal(120_000, 40_000, 2_000)).round()
# fitur mencurigakan: dibangun dari kejadian SETELAH transaksi (pembatalan dana)
fitur_mencurigakan = (label * rng.integers(1, 3, 2_000)).astype("float64")

df_c = pd.DataFrame(
    {"fitur_jujur": fitur_jujur, "fitur_mencurigakan": fitur_mencurigakan, "label": label}
)
korelasi = df_c.corr()["label"].drop("label")
print()
print("== C. Korelasi fitur terhadap label ==")
for nama, k in korelasi.items():
    vonis_k = "PERIKSA KETERSEDIAAN SAAT SERVING" if abs(k) > 0.80 else "wajar"
    print(f"  {nama:<18}: {k:+.3f} -> {vonis_k}")

Output yang Dihasilkan:

text
== C. Korelasi fitur terhadap label ==
  fitur_jujur       : +0.346 -> wajar
  fitur_mencurigakan: +0.903 -> PERIKSA KETERSEDIAAN SAAT SERVING

Korelasi 0.903 bukan berarti fitur tersebut pasti bocor — fitur sungguhan memang bisa kuat. Vonis di atas berarti satu pertanyaan wajib dijawab sebelum fitur dipakai: apakah nilainya tersedia pada detik prediksi dibuat? Fitur jujur nilai lulus karena berdiri di 0.346, korelasi yang khas untuk sinyal nyata yang bercampur derau.

Ketiga pemeriksaan ini mengasumsikan dataset training sudah tersedia dan berhenti di masa lalu. Bagaimana fitur tersebut dihitung berulang setiap hari? Chapter Batch Feature Computation membangun mekanismenya.