MLflow는 ML 모델 학습 중에 생성되는 데이터들을 저장하고, Web UI에서 버전별로 관리할 수 있는 오픈소스 MLOps 플랫폼입니다.
우리는 지난 아티클에서 로컬 Kubernetes 클러스터에 배포한 MLflow를 사용해서…
- 하이퍼파라미터와 성능 지표, ML 모델을 저장한 다음
- Web UI에 접속해서 저장된 데이터들을 꼼꼼히 살펴봤는데요.
이번에는 ML 스크립트의 하이퍼파라미터를 조정해서 모델을 여러 번 학습 후 저장하고, 버전별로 비교하는 모델 버전 관리 실습을 해보겠습니다. MLOps에서 반드시 알아야 하는 내용이니 꼭 챙겨가세요.
실습용 ML 스크립트와 Kubernetes 매니페스트 다시 살펴보기
이번 실습은 지난 아티클의 실습을 진행했다는 가정하에 준비했습니다.
- 만약 아직 minikube 클러스터에 MLflow, PostgreSQL, MinIO를 배포하는 실습을 하지 않으셨다면 이 아티클을,
- ML 스크립트를 실행해서 모델 학습 및 관련 데이터 저장하는 실습을 아직 진행하지 않으셨다면 이 아티클을 꼭 먼저 봐주세요.
지난 실습에서 계속 사용 중인 ML 스크립트는 아래와 같은 흐름으로 진행되는데요.
- 모델에 사용될 하이퍼파라미터(
alpha)를 사용자의 입력값으로 받아옴 - 실습용 데이터셋을 학습용 데이터와 평가용 데이터로 나눔
- MLflow의 Experiment를 새로 설정
- Ridge 모델을 학습용 데이터로 학습 (입력한
alpha값 사용) - 학습 완료한 모델을 평가용 데이터로 평가 및 성능 지표(
MSE,R2) 산출 - 모델의 하이퍼파라미터와 성능 지표 저장
- 학습 완료한 모델 저장
해당 스크립트를 실행하는 데에 필요한 Kubernetes 매니페스트 파일은 아래와 같습니다.
apiVersion: v1
kind: ConfigMap
metadata:
name: train-script
data:
train.py: |
import sys
import mlflow
import mlflow.sklearn
from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split
from sklearn.linear_model import Ridge
from sklearn.metrics import mean_squared_error, r2_score
if __name__ == "__main__":
# 하이퍼파라미터(alpha) 입력
# 해당 ML 스크립트 실행할 때 입력 (예: 0.1)
alpha_value = float(sys.argv[1]) if len(sys.argv) > 1 else 1.0
print(f"--- 모델 학습 (Alpha={alpha_value}) ---")
# 샘플 데이터 불러오기
# X = 환자 데이터 (성별, 나이, 체질량 지수...), y = 당뇨병 진행 정도
# 데이터셋을 학습용(X_train, y_train)과 평가용(X_test, y_test)으로 나눔
db = load_diabetes()
X_train, X_test, y_train, y_test = train_test_split(db.data, db.target)
# MLflow Experiment 새로 설정 (diabetes_prediction)
mlflow.set_experiment("diabetes_prediction")
with mlflow.start_run():
# 학습용 데이터로 Ridge 모델 학습 시작
model = Ridge(alpha=alpha_value)
model.fit(X_train, y_train)
# 평가용 데이터로 학습된 모델 평가
predictions = model.predict(X_test)
mse = mean_squared_error(y_test, predictions)
r2 = r2_score(y_test, predictions)
print(f" MSE (Error): {mse:.2f}")
print(f" R2 (Score): {r2:.2f}")
# 하이퍼파라미터, 성능 지표 기록 (PostgreSQL)
mlflow.log_param("alpha", alpha_value)
mlflow.log_metric("mse", mse)
mlflow.log_metric("r2", r2)
# 학습용 환자 데이터(X)의 처음 5개 행을 아래의 입력 예시(input_example)로 저장
training_sample = X_train[:5]
# 학습된 모델 저장 (MinIO)
mlflow.sklearn.log_model(
sk_model=model,
name="my_model",
registered_model_name="diabetes_predictor",
input_example=training_sample
)
print("--- 학습 완료: 모델 저장됨 ---")
---
apiVersion: batch/v1
kind: Job
metadata:
name: mlflow-train-job
spec:
template:
spec:
restartPolicy: Never
containers:
- name: checker
image: python:3.9-slim
command: ["/bin/sh", "-c"]
args:
# mlflow와 boto3, scikit-learn 설치 후 ML 스크립트 실행
# ML 스크립트(train.py)에 Ridge의 alpha 값(1.0)을 지정하여 실행
- pip install mlflow boto3 scikit-learn && python /code/train.py 1.0
volumeMounts:
- name: script-vol
mountPath: /code
env:
# 환경변수에 MLflow URI와 MinIO 아티팩트 업로드를 위한 URI 및 계정정보 등록
- name: MLFLOW_TRACKING_URI
value: "http://mlflow-demo.mlflow.svc.cluster.local"
- name: MLFLOW_S3_ENDPOINT_URL
value: "http://minio-demo.minio.svc.cluster.local:9000"
- name: AWS_ACCESS_KEY_ID
value: "minioadmin"
- name: AWS_SECRET_ACCESS_KEY
value: "minioadmin"
volumes:
- name: script-vol
configMap:
name: train-script모델 학습 하이퍼파라미터 조정하기
이전 실습에 사용하던 minikube 클러스터를 중지했다면 다시 실행합니다.
minikube start -p {mlflow를 배포했던 클러스터명}이제 처음에 사용했던 파라미터(alpha 값)를 약간 수정해서 ML 스크립트를 다시 돌려보겠습니다. 머신러닝 모델을 개발한다는 것은 결국 더 나은 성능을 내기 위해 다양한 파라미터로 계속 실험하는 것이거든요.
MLOps 관점에서 우리는 각 실험에 대한 데이터가 잘 기록되는지 확인하고 어떻게 각 실험 결과를 비교할 수 있는지 알아야 합니다.
우리가 학습 중인 ML 모델의 파라미터를 수정하는 것은 간단합니다. 방금 전 Job 리소스에서 ML 스크립트 실행 명령어의 전달인자만 바꿔주면 되거든요. 이전에는 1.0로 설정했었으니까 이번엔 0.1로 해보죠. 기존의 YAML 매니페스트 파일에서 아래와 같이 수정해줍니다.
...
args:
# mlflow와 boto3, scikit-learn 설치 후 ML 스크립트 실행
- pip install mlflow boto3 scikit-learn && python /code/train.py 0.1
...그리고 아까 실행했던 Job을 kubectl delete job {생성된 Job명} 명령어로 삭제합니다. (args 값만 다른) 동일한 이름의 Job을 다시 생성할 것이기 때문입니다.
💭ML 스크립트를 Kubernetes Job으로 실행할 때마다 기존 Job을 매번 지워야 할까?
우리가 ML 스크립트를 Kubernetes Job 리소스로 실행하는 이유는 스크립트 실행 환경을 통일해서 오류를 방지하기 위함입니다. 하지만 Job은 기본적으로 스크립트 실행이 끝난 뒤 계속
Complete상태로 남아있기 때문에, 새로운 하이퍼파라미터로 다시 모델을 학습하고 싶어도 동일한 Job의 ML 스크립트를 다시 실행할 수 없습니다. 그래서 이번 가이드에서는 기존 Job을 지우고 다시 실행했는데요. 사실 ML 개발은 모델 성능을 개선하기 위해 끊임없이 하이퍼파라미터와 학습 알고리즘을 수정해서 돌리는 실험과도 같습니다. 그러니까, 실제 ML 개발할 때엔 더 효율적이고 간편한 방법을 찾아야 하는 것입니다. 그럼 어떻게 해야 할까요? 우린 아래처럼 고민해볼 수 있을 것입니다.
- Job을 보다 효율적으로 사용하기 위해 고급 설정을 추가 (예: 완료된 Job을 자동 제거)
- Job을 대체할 수 있는 다른 Workflow 툴을 사용 (예: Kubeflow Pipelines)
무엇이 정답이라기보다는, 상황에 맞춰 가장 효율적인 방법을 찾는 것이 중요합니다.
Job 삭제가 완료되었다면 다시 kubectl apply -f {매니페스트 파일명} 명령어를 실행합니다.
그리고 kubectl logs {새로 생성된 Job명} -f 명령어로 Job에서 출력되는 로그를 실시간으로 확인하면,

처음 실행했던 것과 비슷한 구성으로 로그가 표시됩니다. mse와 r2 값은 처음 돌렸을 때와 달라졌네요. MLflow 웹 UI에서 확인해봅시다.
새로운 터미널 창에서 아래 명령어를 실행해 MLflow Service로 접근할 수 있는 URL을 생성합니다.
minikube service -p {minikube 클러스터명} -n {mlflow 네임스페이스명} {mlflow Service명}
💭왜 매번
minikube service를 실행해야 할까?minikube에 배포한 Kubernetes Service에 접근할 때마다 새로운 터미널을 켜고
minikube service명령어를 실행시켜야 하는 건 번거로운 일입니다. 그럼 로컬에서 Kubernetes 실습할 땐 항상 이런 작업을 해야 할까요? 저도 minikube를 쓰면서 고민을 많이 했는데요. 그러다 동일하게 로컬 Kubernetes 클러스터를 구축해주면서도 다른 방식으로 Service에 접근할 수 있게 해주는 Kind(Kubernetes in Docker)를 여러분께 소개해드리면 좋겠다는 생각이 들었습니다. Kind에 대해서는 곧 관련 아티클에서 더 자세히 다뤄보겠습니다.
URL이 생성되었으면 웹 브라우저로 접속합니다. 아래와 같이 MLflow 웹 UI가 표시되면 우리가 생성했던 diabetes_prediction Experiment(MLflow에서의 ML 모델 개발 단위)를 클릭합니다.

그럼 우리가 지난번에 해당 Experiment를 수행하면서 생성된 Run 위에 새로운 Run이 생성된 걸 확인할 수 있습니다. 맨 위에 있는 최근 Run을 클릭해볼게요.

최근 수행했던 ML 스크립트의 학습 후 성능지표(Metrics), 학습에 쓰인 하이퍼파라미터(Parameters) 학습 후 저장된 모델(Logged models), 해당 모델의 버전(Registered models)까지 모두 확인할 수 있습니다.

MLflow 웹 UI에서 모델 버전별로 확인하기
지금까지 우린 하이퍼파라미터를 조정하며 총 2번 모델 학습을 진행했습니다.
MLflow에서는 ML 학습 후 생성되는 데이터를 추적하고 모델의 버전 관리를 할 수 있다고 했었죠?
우리가 학습한 모델을 버전별로 확인해보겠습니다.
먼저 Experiment 페이지로 다시 돌아와서, 아래와 같이 3개의 View 아이콘 중 중간의 Chart view를 누릅니다. 그럼 Run 목록이 기본값인 테이블에서 차트 형태로 전환되는데요.

우리가 실습 중인 Experiment(diabetes_prediction)의 각 Run에 대한 성능지표(mse, r2)를 그래프로 편하게 비교해볼 수 있습니다.

지난 아티클에서 MSE가 낮고 R2가 높을수록 성능이 좋은 것으로 판단한다고 했었죠? 두 Run을 비교해보니 최근 것이 전체적으로 더 나은 성능으로 보이네요.
Models 탭에서도 Chart view를 누르면 각 모델 버전의 지표를 차트로 볼 수 있습니다.

아까 Run 탭에서 살펴본 것과 동일하게 두 번째 버전의 모델 성능이 비교적 더 좋습니다.
이렇게 모델 버전별로 성능 지표를 바로 확인할 수 있어 어떤 버전의 모델을 사용자에게 제공해야 할지 빠르고 편하게 판단할 수 있습니다.
MLflow 웹 UI에서 모델 학습 결과까지 비교해봤습니다.
실습이 끝난 뒤...
실습을 마친 minikube 클러스터를 당분간 안 쓸 것 같다면 중지해주는 것이 좋습니다. minikube 클러스터를 멈추고 싶을 때는 기억해주세요👉
minikube stop -p {minikube 클러스터명}
마무리
이번 아티클에서는 기존 ML 스크립트의 하이퍼파라미터를 수정해서 다시 돌리고(Kubernetes Job), MLflow에서 Experiment의 Run 단위와 Model 단위로 모델 학습 결과를 비교해보는 실습을 진행했습니다.
중간에 우리가 효율적인 작업을 위해 고민해볼만한 내용도 덧붙였으니 꼭 확인해주세요.
이번 실습까지 우리는 MLflow를 Kubernetes에 배포하고, MLflow의 웹 UI를 자세히 살펴보고, ML 학습 후 버전별로 모델 성능을 비교해봤습니다.
모두 MLOps에 필수적인 작업이기 때문에 실습 가이드에서 최대한 쉽고 자세하게 다뤄봤는데요. 아마 여기까지 따라오셨다면 MLOps란 무엇이고 ML 개발이 어떻게 수행되는지 더 깊게 와닿으셨을 겁니다.
어떠셨나요? 혹시 MLflow의 다른 기능에 대해 더 알고 싶으시거나 다른 MLOps 툴 실습 가이드도 궁금하시다면 아래 피드백 페이지에서 편하게 알려주세요.
그럼 저는 다음 아티클에서 더 흥미롭고 유익한 주제로 찾아오겠습니다.
감사합니다. 행복한 하루 되세요😸