Mengotomasi siklus hidup ML: pipeline training ke evaluasi ke deploy, model registry, drift monitoring, versioning data dengan DVC, dan CI/CD untuk machine learning agar eksperimen naik ke produksi secara konsisten dan dapat diulang.

Di episode 11 kita mencatat eksperimen dengan MLflow. Sekarang bayangkan skalanya naik: tim 5 orang, 200 eksperimen, 3 model di produksi, dan data berubah tiap minggu. Notebook per orang tidak akan bertahan — kalian butuh sistem: MLOps.
Mengapa MLOps wajib? Karena perbedaan antara "model bagus di notebook" dan "sistem ML yang bisa diandalkan di produksi" sangat besar. MLOps mengambil semua disiplin yang sudah kita bangun — tracking, reproducibility, evaluasi, deployment — dan mengubahnya menjadi pipeline otomatis yang bisa dijalankan siapa pun, kapan pun, dengan hasil yang konsisten.
Empat pilar MLOps yang akan kita bangun:
Git tidak dirancang untuk file besar seperti dataset. DVC (Data Version Control) meniru cara kerja Git, tetapi untuk data dan model: ia menyimpan pointer/metadata di Git, sedangkan isi data disimpan di penyimpanan eksternal (lokal, S3, R2).
dvc init
dvc remote add -d storage ~/ml-storage
dvc add data/raw.csv
git add data/raw.csv.dvc .dvc
git commit -m "feat: add v1 dataset"Saat dataset berubah:
dvc add data/raw.csv
git commit -am "feat: add v2 dataset"
dvc pushSekarang setiap commit Git bisa di-reproduce bersama versi data yang tepat — dvc checkout mengembalikan data sesuai commit. Inilah jawaban atas pertanyaan episode 11: "data apa yang dipakai run ini?"
Ubahlah notebook menjadi script terstruktur yang bisa dijalankan end-to-end:
import yaml
import pandas as pd
import mlflow
import lightgbm as lgb
from sklearn.model_selection import train_test_split
def main(config):
df = pd.read_csv(config["data_path"])
X = df.drop("target", axis=1)
y = df["target"]
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=config["seed"]
)
with mlflow.start_run():
model = lgb.LGBMClassifier(**config["model"], random_state=config["seed"])
model.fit(X_train, y_train)
score = model.score(X_test, y_test)
mlflow.log_params(config["model"])
mlflow.log_metric("test_acc", score)
mlflow.lightgbm.log_model(model, "model")
return score
if __name__ == "__main__":
config = yaml.safe_load(open("config/experiment.yaml"))
print(main(config))Script ini bisa dijalankan manual, oleh cron, atau oleh CI/CD — alih-alih menekan shift+enter di notebook secara manual.
Untuk pipeline multi-langkah (extract → preprocess → train → eval), gunakan orkestrator agar tiap langkah terisolasi dan bisa dijalankan ulang secara independen:
from prefect import flow, task
@task
def extract():
return pd.read_csv("data/raw.csv")
@task
def preprocess(df):
return df.fillna(0)
@task
def train(df):
return lgb.LGBMClassifier().fit(df.drop("target", axis=1), df["target"])
@flow
def ml_pipeline():
df = extract()
df = preprocess(df)
model = train(df)
return modelPilihan orkestrator (Airflow untuk jadwal kompleks, Prefect/Dagster untuk kesederhanaan) tergantung skala tim — yang penting: tidak ada lagi langkah manual yang hilang.
Model registry adalah tempat model "resmi" — versi, status (staging/production/archived), dan artifact lengkap. MLflow menyediakannya:
from mlflow.tracking import MlflowClient
client = MlflowClient()
model_info = mlflow.lightgbm.log_model(model, "model")
client.create_registered_model("churn-predictor")
client.create_model_version(
name="churn-predictor",
source=model_info.model_uri,
run_id=mlflow.active_run().info.run_id,
)
client.transition_model_version_stage(
name="churn-predictor", version=1, stage="Production"
)Model yang ter-register punya versi dan riwayat — deploy bisa merujuk ke versi tertentu, rollback tinggal pilih versi lama.
Terapkan prinsip CI/CD (dari dunia software) ke ML. Alur yang masuk akal:
name: ML CI
on:
pull_request:
jobs:
train-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install -r requirements.txt
- run: python train.py --config config/ci.yaml
- run: python evaluate.py --threshold 0.85evaluate.py akan exit non-zero jika metrik di bawah ambang — membuat PR ditolak sebelum kode merusak produksi.
Model di produksi menghadapi data yang berubah seiring waktu — pelanggan baru, perilaku baru, musim baru. Drift adalah saat distribusi data input bergeser dari data training.
| Jenis drift | Arti |
|---|---|
| Data drift | Distribusi fitur input berubah |
| Concept drift | Hubungan fitur-target berubah |
| Output drift | Distribusi prediksi berubah |
Deteksi sederhana: bandingkan distribusi fitur produksi vs training secara berkala. Jika bergeser melewati ambang → trigger retraining:
import numpy as np
def psie_score(train_dist, prod_dist):
train_dist = train_dist / train_dist.sum() + 1e-6
prod_dist = prod_dist / prod_dist.sum() + 1e-6
return np.sum((prod_dist - train_dist) * np.log(prod_dist / train_dist))
psie = psie_score(np.histogram(X_train[:, 0])[0],
np.histogram(X_prod[:, 0])[0])
print(f"PSI = {psie:.3f}")
# PSI < 0.1 sehat; 0.1-0.25 waspada; > 0.25 drift signifikanPSI (Population Stability Index) adalah metrik drift populer: di bawah 0.1 sehat, 0.1-0.25 peringatan, di atas 0.25 berarti data produksi sudah sangat berbeda. Kita akan membangun monitoring produksi yang lengkap di episode 18.
Pada episode 17 ini, kalian telah membangun fondasi MLOps.
Inti yang harus dibawa pulang:
Di episode 18 selanjutnya kita akan membahas deployment & serving model — export joblib/ONNX, REST API dengan FastAPI, containerization dengan Docker, batch inference, serta monitoring data drift dan kebijakan retraining di produksi. Sampai jumpa di episode 18!