Skor yang hanya tampil di layar terminal hilang begitu jendela ditutup, dan eksperimen yang tidak tercatat harus diulang setiap kali angkanya diragukan. Topik ini membahas unit minimum pelacakan eksperimen, yaitu parameter, data, dan metrik, kemudian mempraktikkannya dengan ledger, yaitu buku catatan berjalan berformat JSON yang dikelola sendiri menggunakan pustaka bawaan Python. Alat pelacak khusus disebut sebagai penunjuk saja, sedangkan kebiasaan yang dilatih di sini tetap berlaku pada alat mana pun.
Unit minimum pencatatan#
Satu run, yaitu satu kali eksekusi lengkap dari pelatihan sampai evaluasi, baru layak disebut tercatat apabila tiga kelompok informasinya lengkap. Ketiganya menjawab pertanyaan yang berbeda dan saling melengkapi.
| Kelompok | Isi minimum | Pertanyaan yang dijawab |
|---|---|---|
| Parameter | hyperparameter yang disetel dan versi pustaka | Konfigurasi apa yang menghasilkan angka ini? |
| Data | nama dataset dan tanggal run | Data apa yang digunakan saat itu? |
| Metrik | skor CV dan skor data uji | Seberapa baik, dan seberapa stabil? |
Versi pustaka ikut dicatat karena hasil bergantung pada implementasinya, dan pembaruan scikit-learn dapat mengubah perilaku parameter bawaan maupun algoritma di baliknya. Nama dataset dan tanggalnya menjelaskan bahan yang digunakan, sebab dataset bawaan relatif stabil, sedangkan berkas data milik sendiri berubah isinya dari waktu ke waktu. Metriknya mencakup dua tingkat: skor CV, yaitu hasil evaluasi berlipat pada data latih, mengukur seberapa stabil modelnya, sedangkan skor data uji mengukur estimasi akhir pada data yang tidak pernah disentuh pelatihan.
Skrip pencatat eksperimen#
Skrip berikut menjalankan tiga konfigurasi pada dataset breast_cancer. Dua konfigurasi menggunakan LogisticRegression dengan nilai C berbeda di dalam pipeline StandardScaler, dan satu konfigurasi menggunakan RandomForestClassifier. Setiap konfigurasi dievaluasi dengan cross-validation, yaitu evaluasi berlipat yang mengulang pelatihan pada beberapa potongan data latih, lalu diakhiri satu pelatihan penuh dan pengukuran pada data uji. Hasilnya ditulis ke /tmp/eksperimen.json dengan modul json bawaan Python, lalu dibaca kembali dan dicetak sebagai tabel.
"""Mencatat eksperimen klasifikasi ke dalam ledger JSON sederhana."""
import json
import time
import sklearn
from sklearn.datasets import load_breast_cancer
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score, train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
LEDGER = "/tmp/eksperimen.json"
RANDOM_STATE = 42
def evaluasi_konfigurasi(nama, model, X_train, X_test, y_train, y_test):
"""Menghitung skor CV dan skor test untuk satu konfigurasi model."""
skor_cv = cross_val_score(model, X_train, y_train, cv=5)
model.fit(X_train, y_train)
return {
"model": nama,
"cv_rata": round(float(skor_cv.mean()), 4),
"cv_simpangan": round(float(skor_cv.std()), 4),
"skor_test": round(float(model.score(X_test, y_test)), 4),
"sklearn": sklearn.__version__,
"tanggal": time.strftime("%Y-%m-%d"),
}
bc = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(
bc.data, bc.target, test_size=0.25, stratify=bc.target, random_state=RANDOM_STATE
)
konfigurasi = [
("regresi-logistik C=1.0", make_pipeline(StandardScaler(), LogisticRegression(max_iter=1000))),
("regresi-logistik C=0.1", make_pipeline(StandardScaler(), LogisticRegression(max_iter=1000, C=0.1))),
("random-forest n=50", RandomForestClassifier(n_estimators=50, random_state=RANDOM_STATE)),
]
ledger = [evaluasi_konfigurasi(nama, model, X_train, X_test, y_train, y_test)
for nama, model in konfigurasi]
with open(LEDGER, "w") as berkas:
json.dump(ledger, berkas, indent=2)
# membaca kembali ledger dari berkas, membuktikan catatan tersimpan
with open(LEDGER) as berkas:
run = json.load(berkas)
print(f"{'model':<24} {'cv_rata':>8} {'cv_std':>7} {'test':>7}")
for r in run:
print(f"{r['model']:<24} {r['cv_rata']:>8.4f} {r['cv_simpangan']:>7.4f} {r['skor_test']:>7.4f}")model cv_rata cv_std test
regresi-logistik C=1.0 0.9695 0.0095 0.9860
regresi-logistik C=0.1 0.9765 0.0166 0.9790
random-forest n=50 0.9530 0.0224 0.9510Tabel pada keluaran menunjukkan konfigurasi C=0.1 memperoleh rata-rata skor CV tertinggi, sedangkan C=1.0 unggul pada data uji, dan random forest dengan 50 pohon tertinggal pada keduanya. Perbandingan semacam ini baru bermakna karena parameternya tercatat pada baris yang sama dengan skornya, dan seluruhnya dihasilkan dengan random_state=42 sehingga dapat diulang. Dict setiap run juga memuat versi scikit-learn dan tanggalnya, sehingga baris tetap dapat ditafsirkan meskipun dibaca berbulan-bulan kemudian.
Menambah run baru ke ledger#
Ledger yang baik tumbuh seiring bertambahnya percobaan. Penambahannya mengikuti pola disiplin: berkas dibaca terlebih dahulu, satu run baru ditambahkan ke daftarnya, kemudian seluruh daftar ditulis kembali. Langkah pembacaan wajib dilakukan karena json.dump menulis ulang berkas dari awal, sehingga penulisan tanpa pembacaan menghapus seluruh riwayat run sebelumnya.
Contoh berikut menambahkan satu run untuk konfigurasi C=10.0. Ledger yang dibaca adalah berkas yang dihasilkan skrip pada bagian “Skrip pencatat eksperimen”.
import json
import time
import sklearn
from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score, train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
LEDGER = "/tmp/eksperimen.json"
RANDOM_STATE = 42
bc = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(
bc.data, bc.target, test_size=0.25, stratify=bc.target, random_state=RANDOM_STATE
)
# satu run baru, misalnya percobaan dengan C=10
model = make_pipeline(StandardScaler(), LogisticRegression(max_iter=1000, C=10.0))
skor_cv = cross_val_score(model, X_train, y_train, cv=5)
model.fit(X_train, y_train)
run_baru = {
"model": "regresi-logistik C=10.0",
"cv_rata": round(float(skor_cv.mean()), 4),
"cv_simpangan": round(float(skor_cv.std()), 4),
"skor_test": round(float(model.score(X_test, y_test)), 4),
"sklearn": sklearn.__version__,
"tanggal": time.strftime("%Y-%m-%d"),
}
# disiplin append: baca ledger lama, tambahkan satu run, tulis kembali
with open(LEDGER) as berkas:
ledger = json.load(berkas)
ledger.append(run_baru)
with open(LEDGER, "w") as berkas:
json.dump(ledger, berkas, indent=2)
with open(LEDGER) as berkas:
run = json.load(berkas)
print("jumlah run dalam ledger:", len(run))
print("run terakhir :", run[-1]["model"])
print("skor test run terakhir :", run[-1]["skor_test"])jumlah run dalam ledger: 4
run terakhir : regresi-logistik C=10.0
skor test run terakhir : 0.972Keluaran menunjukkan jumlah run bertambah menjadi empat tanpa menghapus run sebelumnya. Skor data uji 0.972 untuk C=10.0 kini menjadi bagian riwayat yang tetap tersimpan, bukan angka yang hidup hanya di layar. Dengan catatan yang terus bertumbuh dalam satu berkas, tabel perbandingan dapat dicetak ulang kapan saja tanpa mengulang satu pun pelatihan.
Alat khusus sebagai penunjuk#
Ketika jumlah run mencapai puluhan konfigurasi dengan banyak dataset, ledger manual mulai kehilangan kenyamanannya. Alat seperti MLflow, Weights & Biases, atau DVC menyediakan penyimpanan run terpusat, antarmuka perbandingan antar-run, dan tempat menyimpan artefak model beserta metriknya. Topik ini hanya menunjuk alat semacam itu tanpa mempraktikkannya karena unit minimumnya sama: alat mana pun pada dasarnya menyimpan parameter, data, dan metrik per run. Kebiasaan mengisi ketiganya dengan disiplin justru yang paling sulit dipelajari belakangan, dan pustaka bawaan Python sudah cukup untuk melatihnya.
Batas ledger manual juga perlu diketahui. Berkas tunggal tidak memiliki kontrol akses, tidak mencatat isi berkas data secara otomatis, dan rawan konflik apabila beberapa proses menulis bersamaan. Skemanya pun perlu dirawat, misalnya menambah kolom keterangan ketika kebutuhan bertambah, karena struktur catatan ditentukan sendiri oleh penulisnya. Semua batas itu dapat diterima pada skala kecil, dan justru menjadi tanda kapan saatnya berpindah ke alat yang lebih lengkap.
Kesalahan umum#
Kesalahan pertama adalah mencatat skor tanpa parameter lengkap. Baris “0.9790” tanpa keterangan C dan versi pustaka tidak dapat direproduksi, sehingga catatannya kehilangan sebagian besar nilainya. Seluruh parameter yang menyimpang dari nilai bawaan dicatat, dan versi pustaka menyertainya pada dict yang sama.
Kesalahan kedua adalah menulis ledger tanpa membaca isinya terlebih dahulu. Mode tulis json.dump menimpa seluruh berkas, sehingga riwayat run lama hilang oleh satu run baru. Pola baca, tambah, tulis kembali pada contoh di atas mencegah kehilangan itu.
Kesalahan ketiga adalah menggunakan skor data uji berulang kali untuk memilih model. Data uji terpisah (holdout) hanya bermakna selama tidak ikut mengambil keputusan, dan pemilihan yang berulang di atasnya membuat skornya terlalu optimistis. Keputusan konfigurasi diambil dari skor CV, sedangkan skor data uji dicatat sekali sebagai estimasi akhir.
Ringkasan#
- Run yang tercatat memuat tiga kelompok: parameter beserta versi pustaka, dataset beserta tanggal, dan skor CV beserta skor data uji.
- Ledger JSON ringan yang dikelola dengan modul json sudah cukup untuk menyimpan seluruh run dan membacanya kembali.
- Penambahan run mengikuti pola baca, tambah, tulis kembali, karena penulisan langsung menimpa riwayat.
- Alat khusus seperti MLflow disebut sebagai penunjuk, sedangkan inti pelacakannya tetap disiplin parameter, data, dan metrik.
Referensi#
- json — JSON encoder and decoder — Python documentation — Modul bawaan yang digunakan untuk menulis dan membaca ledger eksperimen.
- sklearn.model_selection.cross_val_score — scikit-learn documentation — Fungsi evaluasi berlipat yang menghasilkan skor CV pada setiap run.
- sklearn.model_selection.train_test_split — scikit-learn documentation — Pemecahan data latih dan uji yang digunakan pada seluruh skrip contoh.
- sklearn.datasets.load_breast_cancer — scikit-learn documentation — Dataset yang digunakan pada skrip pencatat eksperimen.