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:

  1. Folder arsip per versi — salinan dataset dibekukan dengan nama versi (data_v1.csv, data_v2.csv) dan tidak pernah ditulis ulang.
  2. Hash konten (sha256) — sidik jari 64 karakter heksadesimal dari isi berkas; satu byte berubah, hash berubah total.
  3. 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:

python
"""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:

text
===== 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:

  1. Baris 20-22 (hash): hashlib.sha256 membaca seluruh byte berkas lalu menghasilkan sidik jari 64 karakter — properti pentingnya: perubahan satu byte mengubah hasil total, tidak ada “sedikit berbeda”.
  2. Baris 25-37 (tulis_csv): dataset sensor 50 baris dibangun dengan seed tetap sehingga versi dasarnya identik antar mesin.
  3. Baris 46-59 (arsip v1): shutil.copy membekukan salinan ke folder arsip/; manifest pertama mencatat hash, jumlah baris, dan tanggal — tanggal versi dibuat oleh manusia, hash tidak bisa dipalsukan.
  4. 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.
  5. Baris 75-101 (manifest v2): pd.concat menambahkan baris versi kedua; jumlah baris tetap 50 sehingga hanya hash yang mengungkap perbedaan antar versi.
  6. Baris 104-113 (deteksi): kedua hash dibandingkan dan hasilnya False; tanpa versioning, perubahan pada baris 47 akan lolos tanpa jejak.
  7. Baris 116-130 (verifikasi ulang): hash arsip dihitung ulang dan dicocokkan dengan manifest — dua nilai True membuktikan arsip belum ditulis ulang oleh siapa pun.

Alat untuk Skala Lebih Besar #

PendekatanSkala cocokCatatan
Folder + manifest manualdataset < beberapa ratus MBtanpa dependensi, kontrol penuh
DVCdataset besar, banyak versimenyimpan berkas di penyimpanan remote, git hanya menyimpan metafile — dvc.org
Hugging Face Datasetsdataset publik/berbagi timsetiap 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.