포스트

머신러닝 실전 워크플로 (6) - 실험 추적: MLflow

실험이 쌓이면 무엇을 바꿔 무엇이 좋아졌는지 잃어버립니다. MLflow로 파라미터, 지표, 아티팩트를 기록하고 실행을 비교해 실험을 재현 가능하게 만드는 6편입니다.

머신러닝 실전 워크플로 (6) - 실험 추적: MLflow

머신러닝 실전 워크플로 시리즈의 6편입니다. 5편의 “피처 파이프라인”에 이어집니다.

무엇이 좋아졌는지 잃어버리는 문제

5편까지 데이터와 피처가 재현 가능한 형태로 준비됐습니다. 이제 모델을 붙이고 실험을 돌립니다. 학습률을 바꿔 보고, 피처를 하나 더 넣어 보고, 트리 깊이를 조정합니다. 처음 몇 번은 결과를 기억할 수 있습니다.

문제는 실험이 쌓일 때 생깁니다. 서른 번쯤 돌리고 나면 검증 RMSE가 가장 낮았던 실행이 어떤 조합이었는지 기억나지 않습니다. 학습률 0.05에 피처 20개였는지, 0.1에 15개였는지 헷갈립니다. 노트북 셀은 마지막 실행 결과로 덮어써졌고, 저장한 모델 파일은 model_final.pkl, model_final_v2.pkl, model_real_final.pkl처럼 쌓여 어느 것이 무엇인지 모릅니다. 좋은 점수는 봤는데 그 점수를 다시 만들 수 없는 상태, 이것이 실험 추적이 없을 때 반복되는 상황입니다.

실험 추적은 이 문제를 정면으로 풉니다. 실행 하나를 돌릴 때마다 무엇을 바꿨고, 무엇이 좋아졌고, 무엇이 나왔는지를 자동으로 남겨 두는 것입니다. 그러면 나중에 실행들을 나란히 놓고 비교할 수 있고, 가장 좋았던 실행을 그대로 재현할 수 있습니다.

실험 추적이 기록하는 세 가지

한 번의 실행에서 남길 것은 세 가지입니다. 이 셋이 묶여 있어야 하나의 실행을 온전히 설명합니다.

  • 파라미터: 무엇을 바꿨나. 하이퍼파라미터(학습률, 트리 깊이), 쓴 피처 목록, 데이터 버전처럼 실행의 입력이 되는 값입니다.
  • 지표: 무엇이 좋아졌나. 검증 RMSE, MAE, AUC처럼 실행의 성능을 요약하는 값입니다.
  • 아티팩트: 무엇이 나왔나. 학습된 모델 파일, 피처 중요도 그림, 혼동행렬처럼 실행이 만들어 낸 결과물입니다.

파라미터와 지표가 짝지어 기록되면 “이 파라미터를 이렇게 바꿨더니 지표가 이렇게 됐다”를 실행별로 되짚을 수 있습니다. 이것이 실험 추적의 핵심입니다. 어떤 지표를 남길지는 문제 유형에 따라 다르고, 회귀는 8편의 회귀 지표를, 분류는 7편의 분류 지표를 참고해 정합니다.

MLflow 최소 코드

MLflow는 이 세 가지를 기록하는 오픈소스 도구입니다. 로컬에서 바로 돌아가고 별도 서버가 없어도 되므로 이 시리즈의 기준으로 씁니다. 핵심 함수는 네 개입니다. start_run으로 실행을 열고, 그 안에서 log_param, log_metric, log_artifact로 세 가지를 남깁니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import mlflow
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.metrics import mean_squared_error, mean_absolute_error

mlflow.set_experiment("taxi-demand")   # 관련 실행들을 묶는 실험 이름. 없으면 새로 만든다

params = {"n_estimators": 300, "max_depth": 3, "learning_rate": 0.05}

with mlflow.start_run(run_name="gbr-baseline"):   # 실행 하나를 연다
    model = GradientBoostingRegressor(**params).fit(X_tr, y_tr)
    pred = model.predict(X_val)

    rmse = mean_squared_error(y_val, pred) ** 0.5
    mae = mean_absolute_error(y_val, pred)

    mlflow.log_params(params)              # 무엇을 바꿨나 (dict는 log_params, 단일값은 log_param)
    mlflow.log_metric("rmse", rmse)        # 무엇이 좋아졌나
    mlflow.log_metric("mae", mae)
    mlflow.sklearn.log_model(model, "model")   # 결과물(모델)을 아티팩트로 저장

with mlflow.start_run(...) 블록이 실행 하나의 경계입니다. 블록 안에서 기록한 파라미터, 지표, 아티팩트가 이 실행에 묶이고, 블록을 빠져나오면 실행이 닫힙니다. set_experiment로 지정한 실험은 관련 실행들을 담는 상위 묶음이라, 같은 문제의 여러 시도가 한곳에 모입니다.

그림이나 리포트 같은 파일은 log_artifact로 그대로 올립니다.

1
2
    # 학습 곡선이나 피처 중요도를 파일로 저장한 뒤 실행에 붙인다
    mlflow.log_artifact("feature_importance.png")

반복 학습에서 에폭마다 지표가 어떻게 움직였는지 남기려면 step을 줍니다. 그러면 UI에서 학습 곡선으로 볼 수 있습니다.

1
2
3
for epoch in range(n_epochs):
    val_rmse = evaluate(model, X_val, y_val)
    mlflow.log_metric("val_rmse", val_rmse, step=epoch)   # 값 하나가 아니라 곡선으로 남는다

자동 기록: autolog

기록 코드를 매번 손으로 넣는 것도 번거롭습니다. autolog는 프레임워크를 감지해서 파라미터와 지표, 모델을 자동으로 남깁니다. 한 줄이면 됩니다.

1
2
3
4
5
6
import mlflow

mlflow.sklearn.autolog()   # 이 한 줄이면 이후 fit 호출에서 파라미터와 지표, 모델이 자동 기록된다

with mlflow.start_run(run_name="gbr-autolog"):
    GradientBoostingRegressor(**params).fit(X_tr, y_tr)   # log_param, log_metric을 부르지 않아도 남는다

mlflow.lightgbm.autolog()mlflow.pytorch.autolog()처럼 프레임워크별 버전이 있고, LightGBM이나 XGBoost는 검증셋을 넘기면 반복마다의 평가 지표까지 자동으로 곡선으로 남깁니다. autolog는 빠르게 시작할 때 좋고, 무엇을 남길지 정확히 통제하고 싶을 때는 앞의 수동 방식을 씁니다. 둘을 섞어서, autolog로 기본을 깔고 필요한 커스텀 지표만 log_metric으로 더해도 됩니다.

실행을 비교한다

기록의 목적은 비교입니다. mlflow ui로 웹 UI를 띄우면 실험 안의 실행들이 표로 나오고, 파라미터와 지표 열을 정렬하고 겹쳐 그려 어떤 조합이 좋았는지 눈으로 확인합니다.

1
mlflow ui   # 기본 http://localhost:5000 에서 실행 목록과 비교 화면을 연다

코드에서 바로 뽑을 수도 있습니다. search_runs는 실행들을 DataFrame으로 돌려주므로, 지표로 정렬해서 가장 좋은 실행을 찾습니다.

1
2
3
4
runs = mlflow.search_runs(
    experiment_names=["taxi-demand"],
    order_by=["metrics.rmse ASC"])       # 검증 RMSE가 낮은 순
runs[["run_id", "params.max_depth", "params.learning_rate", "metrics.rmse"]].head()

이제 “RMSE가 가장 낮았던 실행이 무엇이었나”에 표로 답할 수 있습니다. 그 실행의 run_id로 저장된 모델 아티팩트를 다시 불러오면 점수를 그대로 재현합니다. 서른 번의 실험이 기억이 아니라 기록으로 남아 있기 때문입니다.

지표 이름과 파라미터 이름은 실행마다 일관되게 씁니다. 같은 값을 어떤 실행에선 rmse, 다른 실행에선 val_rmse로 남기면 나중에 정렬과 비교가 어긋납니다. 이름 규칙을 처음에 정해 두는 편이 좋습니다.

대안

같은 일을 하는 도구로 Weights & Biases(W&B), Neptune, Comet 같은 관리형 서비스가 있습니다. 협업과 대시보드가 강점이지만 계정과 연동이 필요합니다. MLflow는 오픈소스라 로컬에서 바로 시작할 수 있어 기준으로 삼았고, 개념(파라미터, 지표, 아티팩트, 실행 비교)은 어느 도구든 같습니다.

NYC 택시 파이프라인에서는

이 흐름을 NYC 택시 수요 예측에 실제로 적용한 예는 NYC 택시 파이프라인 6편에 있습니다. 5편에서 만든 LightGBM 베이스라인과 PyTorch 모델의 실행을 MLflow로 기록하고, 검증 지표로 나란히 비교해 어느 쪽을 다음 단계로 넘길지 정합니다.

여기서는 실행을 기록하고 비교하는 데까지 왔습니다. 가장 좋았던 실행을 골랐다면, 그 모델을 배포 후보로 등록하고 버전을 관리하는 일이 남습니다. 그것은 8편의 모델 레지스트리에서 다룹니다. 그 전에, 비교의 근거가 되는 검증 점수 자체가 정직한지를 7편에서 점검합니다. 누수가 낀 점수를 아무리 잘 기록해도 비교는 무의미하기 때문입니다.

정리

개념한 줄 요약
문제실험이 쌓이면 무엇을 바꿔 무엇이 좋아졌는지 잃어버리고 재현하지 못한다
파라미터무엇을 바꿨나. 하이퍼파라미터, 피처 목록, 데이터 버전을 log_param으로
지표무엇이 좋아졌나. 검증 RMSE, MAE 같은 성능을 log_metric으로
아티팩트무엇이 나왔나. 모델 파일, 그림, 리포트를 log_artifact
start_run실행 하나의 경계. 블록 안의 기록이 이 실행에 묶인다
autolog한 줄로 파라미터, 지표, 모델을 자동 기록. 빠른 시작에 좋다
실행 비교mlflow uisearch_runs로 정렬해 최고 실행을 찾고 재현한다
대안W&B, Neptune, Comet 같은 관리형 서비스. 개념은 같다

실험이 기억이 아니라 기록으로 남으면, 비교와 재현이 가능해집니다. 다음 편은 그 비교의 근거가 되는 검증 점수를 정직하게 만드는 방법입니다.

다음 글: 머신러닝 실전 워크플로 (7) - 검증 전략과 누수 점검

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.