Menjadikan proyek ML benar-benar reproducible: git untuk kode, DVC untuk data, dan model registry untuk artifact — lalu menghubungkan ketiganya dalam satu lineage sehingga setiap model bisa ditelusuri ke dataset dan kode yang memproduksinya

Setelah di episode 6 kita melacak eksperimen dan di episode 7 mengevaluasi secara jujur, kini kita menutup lubang yang paling sering mengubah proyek ML menjadi mimpi buruk: reproduksibilitas. Pernahkah kalian mendapat laporan "model produksi error, tapi aku tidak bisa menjelaskan dari mana model itu dibuat"? Itu terjadi karena kode, data, dan model di-versioning secara terpisah — atau tidak di-versioning sama sekali.
Software engineering sudah lama memecahkan ini dengan git untuk kode. Masalah ML lebih sulit karena ada dua hal lain yang ikut berubah: data (yang bisa sebesar ratusan GB, tidak cocok masuk git) dan model (artifact hasil training). Episode ini membangun lineage utuh: menghubungkan kode, data, dan model sehingga setiap artifact bisa ditelusuri balik ke sumbernya.
Kode dulu selalu masuk git. Untuk ML, ada tambahan penting: environment harus ikut ter-versioning. requirements.txt atau lockfile (uv/pip-tools) harus di-commit agar tiga bulan lagi kalian bisa membuat environment yang sama persis.
# requirements.lock (di-commit ke git)
numpy==1.26.4
pandas==2.2.2
scikit-learn==1.5.0
torch==2.4.1Tanpa pin versi, "works on my machine" menjadi "works on my machine last month" — sumber frustrasi terbesar di tim ML.
Data tidak bisa masuk git (terlalu besar), tapi harus tetap bisa di-versioning. DVC memecahkannya dengan trik elegan: file data besar disimpan di penyimpanan eksternal (S3, GCS, remote server), sedangkan git hanya menyimpan metadata ringan yang menunjuk ke versi data.
dvc init
dvc remote add -d storage s3://my-bucket/ml-data
dvc add data/raw_events.parquetFile data/raw_events.parquet.dvc yang dihasilkan berisi hash file data — masuk git. Saat data berubah, hash berubah, dan riwayat data ikut di git:
git log --oneline -- data/raw_events.parquet.dvc
dvc checkout # restore data sesuai versi git aktifKekuatan utama DVC: satu git commit = satu state kode + data yang konsisten. Checkout commit lama, jalankan dvc checkout, dan kalian punya data yang persis seperti dulu.
Praktik yang disarankan: data mentah bersifat immutabel (read-only, tak pernah diubah di tempat). Transformasi menghasilkan data turunan ber-version. Ini mencegah masalah klasik: seseorang mengubah kolom di file CSV produksi tanpa ada yang tahu.
Di episode 6 kita sudah mengenalkan model registry. Dari sisi versioning, registry menyimpan versi model + lineage:
import mlflow
with mlflow.start_run() as run:
mlflow.log_param("dataset_version", "v3") # hash data dari DVC
mlflow.log_param("git_commit", "abc123f") # versi kode
mlflow.log_param("feature_pipeline", "v2")
mlflow.log_metric("roc_auc", 0.824)
mlflow.register_model("runs:/<RUN_ID>/model", "churn-predictor")Dengan ini, setiap model versi v5 di registry bisa menjawab tiga pertanyaan: data apa yang dipakai? kode versi apa? hyperparameter apa?
Lineage yang utuh memungkinkan reproduksi penuh: dari model live, kalian menelusuri ke run, ke commit kode, ke versi data, lalu menjalankan ulang training untuk memverifikasi hasil yang sama.
Alur kerja yang sudah terbukti di lapangan:
random_state, torch.manual_seed).Verifikasi periodik: simpan satu set commit, lalu jalankan ulang pipeline setahun kemudian. Jika hasilnya tidak sama, sistem reproducibility-nya masih bocor.
git checkout v1.4.2
dvc checkout
uv sync --locked
python -m src.trainWarning
Randomness adalah musuh reproducibility. GPU (khususnya operasi seperti cuDNN) tidak deterministik secara penuh, dan perbedaan kecil pun bisa memengaruhi hasil. Tujuan praktis bukan determinisme bit-perfect, melainkan stabilitas yang cukup sehingga hasil training tidak berubah dramatis tanpa perubahan yang jelas.
| Pitfall | Dampak | Pencegahan |
|---|---|---|
pip install tanpa pin versi | Environment tidak reproducible | Lockfile + --locked |
| Data diubah di tempat tanpa versioning | Hasil training tidak bisa ditelusuri | Data mentah immutabel + DVC |
Model disimpan model_final.pkl begitu saja | Tidak tahu dari run mana | Registry dengan lineage |
| Hash data tidak dicatat di eksperimen | Model tidak terhubung ke data | log_param("dataset_version", ...) |
| Tanpa seed | Dua run identik hasil beda | Set seed deterministik |
Pada episode 8 ini, kalian telah membangun sistem reproduksibilitas penuh:
Di episode 9 selanjutnya kita akan membahas Containerization & Serving — mengemas model ke Docker, memilih framework serving (Triton, Seldon, BentoML), dan menyajikan prediksi lewat REST maupun gRPC dengan latensi dan throughput yang terukur. Sampai jumpa di episode 9!