Eksperimen deep learning hanya bisa diulang bila dua hal terkunci: kode dan data. Kode mudah — git menyimpan setiap barisnya. Data justru sering berubah diam-diam: satu baris dikoreksi, satu kolom ditambah, berkas diunduh ulang dari server yang isinya sudah diperbarui. Enam bulan kemudian, angka evaluasi tidak bisa direproduksi dan tidak ada yang tahu mengapa. Chapter ini membangun mekanisme versioning sederhana — folder per versi, hash konten, manifest CSV — lalu membuktikan mekanismenya mendeteksi perubahan sekecil satu nilai.
Kenapa Dataset Harus Diversi #
Hubungan eksperimen dan dataset adalah relasi satu-ke-satu: skor 0.94 milik pasangan (kode, dataset) tertentu, bukan milik kode semata. Tanpa identitas dataset, tiga masalah muncul berurutan — hasil lama tak dapat diverifikasi, pembandingan antar eksperimen tidak adil karena datanya diam-diam berbeda, dan keputusan produksi kehilangan jejak audit. Prinsip yang sama berlaku pada kode dan diperluas di chapter Reproducible Experiments ; di sini kita fokus pada sisi data.
Tiga Komponen Versioning Manual #
Mekanisme paling sederhana yang tetap benar terdiri dari tiga komponen:
- Folder arsip per versi — salinan dataset dibekukan dengan nama versi (
data_v1.csv,data_v2.csv) dan tidak pernah ditulis ulang. - Hash konten (sha256) — sidik jari 64 karakter heksadesimal dari isi berkas; satu byte berubah, hash berubah total.
- Manifest — tabel CSV berisi versi, nama berkas, hash, jumlah baris, dan tanggal; manifest ikut di-commit ke git sehingga identitas dataset tercatat bersama kode.
Demo: Dua Versi dan Satu Deteksi #
Berkas dataset_versioning.py membuat dataset sensor mini, mengarsipkannya sebagai versi pertama, mengoreksi satu nilai sensor menjadi versi kedua, lalu membuktikan hash menangkap perubahan tersebut dan verifikasi ulang arsip tetap cocok:
"""Dataset versioning: hash konten, folder per versi, manifest CSV.
Kita buat dataset mini "data_v1.csv", simpan salinannya sebagai versi
arsip dengan manifest hash sha256, ubah satu baris menjadi "data_v2.csv",
lalu buktikan manifest mendeteksi perubahan tersebut. Semua berkas
disimpan di folder sementara yang otomatis dibersihkan.
"""
import hashlib
import shutil
import tempfile
from pathlib import Path
import numpy as np
import pandas as pd
rng = np.random.default_rng(42)
def hash_sha256(berkas):
"""Hash konten berkas dengan sha256 (heksadesimal 64 karakter)."""
return hashlib.sha256(Path(berkas).read_bytes()).hexdigest()
def tulis_csv(path, n):
"""Tulis dataset mini: hasil sensor dengan label kondisi."""
df = pd.DataFrame(
{
"suhu": np.round(rng.normal(60, 2, n), 1),
"getaran": np.round(rng.uniform(0.5, 3.5, n), 2),
"kondisi": rng.choice(["sehat", "rusak"], n, p=[0.8, 0.2]),
}
)
df.to_csv(path, index=False)
return df
kerja = Path(tempfile.mkdtemp(prefix="versi_"))
# ---------- 1. Versi 1: dataset awal ----------
v1 = tulis_csv(kerja / "data_v1.csv", 50)
print("===== VERSI 1 =====")
print(v1.head(3).to_string(index=False))
print("baris:", len(v1))
# ---------- 2. Manifest: hash + metadata tiap berkas versi ----------
arsip = kerja / "arsip"
arsip.mkdir()
shutil.copy(kerja / "data_v1.csv", arsip / "data.csv")
manifest = pd.DataFrame(
{
"versi": ["v1"],
"berkas": ["data.csv"],
"sha256": [hash_sha256(arsip / "data.csv")],
"baris": [len(v1)],
"tanggal": ["2026-10-01"],
}
)
print()
print("===== MANIFEST SETELAH VERSI 1 =====")
print(manifest.to_string(index=False))
# ---------- 3. Versi 2: satu nilai sensor dikoreksi ----------
rng_koreksi = np.random.default_rng(7)
baris_ubah = int(rng_koreksi.integers(0, len(v1)))
v2 = v1.copy()
v2.loc[baris_ubah, "suhu"] = 71.3 # koreksi hasil kalibrasi ulang
v2.to_csv(kerja / "data_v2.csv", index=False)
shutil.copy(kerja / "data_v2.csv", arsip / "data_v2.csv")
manifest_baru = pd.concat(
[
manifest,
pd.DataFrame(
{
"versi": ["v2"],
"berkas": ["data_v2.csv"],
"sha256": [hash_sha256(arsip / "data_v2.csv")],
"baris": [len(v2)],
"tanggal": ["2026-10-08"],
}
),
],
ignore_index=True,
)
print()
print("===== MANIFEST SETELAH VERSI 2 =====")
print(manifest_baru.to_string(index=False))
# ---------- 4. Deteksi perubahan lewat hash ----------
print()
print("===== DETEKSI PERUBAHAN =====")
print("hash v1 :", manifest_baru.loc[0, "sha256"][:16], "...")
print("hash v2 :", manifest_baru.loc[1, "sha256"][:16], "...")
print("hash identik? ", manifest_baru.loc[0, "sha256"]
== manifest_baru.loc[1, "sha256"])
print("baris yang diubah:", baris_ubah)
print("suhu v1:", round(float(v1.loc[baris_ubah, "suhu"]), 1),
"-> suhu v2:", round(float(v2.loc[baris_ubah, "suhu"]), 1))
# verifikasi ulang: hash arsip vs hash berkas kerja saat ini
ulang = pd.DataFrame(
{
"berkas": ["data.csv", "data_v2.csv"],
"hash_sekarang": [
hash_sha256(arsip / "data.csv"),
hash_sha256(arsip / "data_v2.csv"),
],
}
)
ulang["cocok_dgn_manifest"] = [
ulang.loc[i, "hash_sekarang"] == manifest_baru.loc[i, "sha256"]
for i in range(2)
]
print()
print("===== VERIFIKASI ULANG ARSIP =====")
print(ulang.assign(
hash_sekarang=ulang["hash_sekarang"].str[:16] + "..."
).to_string(index=False))
# simpan manifest sebagai berkas csv juga
manifest_baru.to_csv(kerja / "manifest.csv", index=False)
print()
print("manifest tersimpan:", (kerja / "manifest.csv").name,
"| ukuran:", (kerja / "manifest.csv").stat().st_size, "byte")
shutil.rmtree(kerja)
print("folder sementara dibersihkan.")Output yang Dihasilkan:
===== VERSI 1 =====
suhu getaran kondisi
60.6 2.86 sehat
57.9 2.49 rusak
61.5 2.62 sehat
baris: 50
===== MANIFEST SETELAH VERSI 1 =====
versi berkas sha256 baris tanggal
v1 data.csv e0a89442786bc13e413f2d5240755a6c6e951c9172926d82d15d27bde1a5c40f 50 2026-10-01
===== MANIFEST SETELAH VERSI 2 =====
versi berkas sha256 baris tanggal
v1 data.csv e0a89442786bc13e413f2d5240755a6c6e951c9172926d82d15d27bde1a5c40f 50 2026-10-01
v2 data_v2.csv efaf9a1f9928569001f3c781e2ab87175b602b46f55218c8c03fb0914734aa43 50 2026-10-08
===== DETEKSI PERUBAHAN =====
hash v1 : e0a89442786bc13e ...
hash v2 : efaf9a1f99285690 ...
hash identik? False
baris yang diubah: 47
suhu v1: 60.4 -> suhu v2: 71.3
===== VERIFIKASI ULANG ARSIP =====
berkas hash_sekarang cocok_dgn_manifest
data.csv e0a89442786bc13e... True
data_v2.csv efaf9a1f99285690... True
manifest tersimpan: manifest.csv | ukuran: 219 byte
folder sementara dibersihkan.Contoh di atas diuji nyata dengan pandas 2.2.3 pada mesin CPU dan selesai dalam waktu di bawah satu detik. Mari kita telaah kode di atas baris per baris:
- Baris 20-22 (hash):
hashlib.sha256membaca seluruh byte berkas lalu menghasilkan sidik jari 64 karakter — properti pentingnya: perubahan satu byte mengubah hasil total, tidak ada “sedikit berbeda”. - Baris 25-37 (tulis_csv): dataset sensor 50 baris dibangun dengan seed tetap sehingga versi dasarnya identik antar mesin.
- Baris 46-59 (arsip v1):
shutil.copymembekukan salinan ke folderarsip/; manifest pertama mencatat hash, jumlah baris, dan tanggal — tanggal versi dibuat oleh manusia, hash tidak bisa dipalsukan. - Baris 64-72 (koreksi): satu nilai suhu pada baris 47 dinaikkan dari 60.4 ke 71.3, mensimulasikan kalibrasi ulang sensor — skenario perubahan yang paling sering terjadi di dunia nyata.
- Baris 75-101 (manifest v2):
pd.concatmenambahkan baris versi kedua; jumlah baris tetap 50 sehingga hanya hash yang mengungkap perbedaan antar versi. - Baris 104-113 (deteksi): kedua hash dibandingkan dan hasilnya
False; tanpa versioning, perubahan pada baris 47 akan lolos tanpa jejak. - Baris 116-130 (verifikasi ulang): hash arsip dihitung ulang dan dicocokkan dengan manifest — dua nilai
Truemembuktikan arsip belum ditulis ulang oleh siapa pun.
Alat untuk Skala Lebih Besar #
| Pendekatan | Skala cocok | Catatan |
|---|---|---|
| Folder + manifest manual | dataset < beberapa ratus MB | tanpa dependensi, kontrol penuh |
| DVC | dataset besar, banyak versi | menyimpan berkas di penyimpanan remote, git hanya menyimpan metafile — dvc.org |
| Hugging Face Datasets | dataset publik/berbagi tim | setiap unggulan otomatis mendapat revisi — docs |
Ketiga pendekatan memakai prinsip yang sama dengan demo di atas: berkas dibekukan, identitas direkam sebagai hash, dan riwayat perubahan dapat ditelusuri. Perbedaannya hanya pada otomatisasi dan skala.
Identitas dataset kini terkunci. Kompetensi berikutnya berhubungan dengan jumlah data: bagaimana memperluas data latih tanpa mengumpulkan satu sampel baru pun — lewat strategi augmentasi yang dirancang sadar, bukan transformasi asal tempel.