• Aiden's Lab 뉴스레터
  • Posts
  • 오픈소스 MLOps 플랫폼 MLflow로 머신러닝 모델을 학습하고 웹 UI에서 확인해보자

오픈소스 MLOps 플랫폼 MLflow로 머신러닝 모델을 학습하고 웹 UI에서 확인해보자

MLflow로 머신러닝 모델을 학습하고 생성되는 데이터를 웹 UI에서 확인해봅니다.

안녕하세요, Aiden’s Lab 뉴스레터입니다.

지난 아티클에서 우리는 minikube 클러스터에 오픈소스 MLOps 플랫폼 MLflow와 두 개의 저장소(PostgreSQL과 MinIO)를 배포하고 연동하는 실습을 진행했습니다.

이제 MLflow를 좀 더 MLOps답게 사용해보겠습니다.

이번 실습 가이드를 진행하고 나면...

본 아티클을 읽으시면서 (직접 따라하시면 더욱 좋고요!) 자연스럽게 몸에 익히게 되는 것이 있습니다. 바로 MLOps 기본기인 모델 학습 추적입니다.

모델 학습 추적이란 ML(머신러닝) 모델을 학습시키면서 생성되는 각종 데이터를 기록하는 것을 의미하는데요. 이건 MLOps가 등장하게 된 이유 중 하나이기 때문에 꼭 알아두셔야 합니다.

이제 실습에 사용할 ML 스크립트 먼저 살펴보겠습니다.

우리가 사용할 데이터셋과 ML 모델 살펴보기

실습 준비가 너무 복잡하지 않도록 Scikit-learn(다양한 ML 개발 툴을 모아둔 오픈소스 Python 라이브러리)에 내장된 샘플 데이터셋 중 '당뇨병(Diabetes)' 데이터셋을 사용해서 ML 학습을 진행할 예정입니다.

(출처: GeeksforGeeks)

  • 해당 데이터셋은 아래와 같이 11개의 컬럼으로 구성됩니다.

    • 10개의 환자 데이터(나이, 성별, 체질량 지수 등) 컬럼 -> 독립 변수(X)

    • 1개의 당뇨병 진행 정도(혈당 수치) 컬럼 -> 종속 변수(y)

    • 이 데이터셋으로 학습을 완료한 모델은 새로운 환자 데이터(X)를 받았을 때 당뇨병 진행 정도(y)를 예측할 수 있는 거죠.

  • 해당 데이터셋은 학습용과 평가용 데이터로 나뉩니다.

    • 학습(Train)용 데이터는 ML 모델을 학습할 때 사용되고,

    • 평가(Test)용 데이터는 학습된 ML 모델의 성능을 평가할 때 사용됩니다.

그리고 우리가 학습시킬 ML 모델은 scikit-learn의 선형 회귀 모델 Ridge인데요. 이 모델을 사용하는 이유는...

  • alpha라고 하는 파라미터만 사용해서 하이퍼파라미터 기록이 복잡하지 않고,

  • 반복 없이 빠르게 학습할 수 있는 모델로 MLflow 실습에 적합하기 때문입니다.

아마 선형 회귀에 대해 들어보셨을 수도 있습니다. 여러 데이터 점들이 어떤 추세를 보이는지 선으로 그린 것을 말하는데요. 선형 회귀 모델은 이렇게 얻은 추세선으로 새로운 입력 데이터에 대한 예측을 수행하는 겁니다.

그런데 만약 모델이 학습 과정에서 주어진 모든 데이터에 너무 딱 맞게 학습하면 어떻게 될까요? 아마 새로운 데이터가 들어올 때 정확한 예측을 하기 어려워질 겁니다.

이걸 과적합(Overfitting)이라고 하는데요. ML 모델을 개발한다는 것은 너무 덜 학습되지도, 학습용 데이터셋에만 너무 치우치지도 않은 적정선(Balanced)을 찾는 것입니다. 아래 그림을 보면 좀 더 이해가 쉬우실 겁니다.

위에서 잠깐 언급한 Ridge 모델의 alpha 파라미터가 바로 이 과적합을 줄여주는 역할을 수행합니다. 추세에서 너무 멀리 떨어진 데이터는 신경을 덜 쓰는 거죠.

  • alpha를 높이 설정(예: 1.0)하면 모델의 과적합을 줄이고,

  • alpha를 낮게 설정(예: 0.1)하면 모델이 학습용 데이터에 더 강하게 맞춥니다.

모델 학습이 끝나면 평가용 데이터를 학습된 모델에 투입해서 예측하게 하고 그 값을 비교하여 해당 모델이 잘 학습되었는지 성능을 평가합니다. 이때 사용되는 평가 지표는 두 가지입니다.

  • MSE: 학습된 모델의 예측값과 실제 값 간의 거리 (낮을수록 모델 성능이 좋다고 평가)

  • R2: 학습된 모델의 정확도 (높을수록 모델 성능이 좋다고 평가)

실습에 사용할 ML 스크립트 살펴보기

지금까지의 내용을 다시 정리해보겠습니다.

MLOps 관점에서 MLflow로 기록될 데이터는 아래와 같습니다.

  • 메타데이터

    • alpha: 모델 학습 시 입력 파라미터 (log_param 메소드로 저장)

    • MSE: 학습된 모델의 성능 지표 (log_metric 메소드로 저장)

    • R2: 학습된 모델의 성능 지표 (log_metric 메소드로 저장)

  • 아티팩트

    • 학습된 모델 (log_model 메소드로 저장)

우리가 MLflow 실습에 사용할 ML 스크립트는 아래와 같은 흐름으로 진행됩니다.

  1. 모델에 사용될 하이퍼파라미터(alpha)를 사용자의 입력값으로 받아옴

  2. 실습용 데이터셋을 학습용 데이터와 평가용 데이터로 나눔

  3. MLflow의 Experiment를 새로 설정

  4. Ridge 모델을 학습용 데이터로 학습 (입력한 alpha 값 사용)

  5. 학습 완료한 모델을 평가용 데이터로 평가 및 성능 지표(MSE, R2) 산출

  6. 모델의 하이퍼파라미터와 성능 지표 저장

  7. 학습 완료한 모델 저장

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("--- 학습 완료: 모델 저장됨 ---")

학습된 모델을 저장할 때 input_example 파라미터의 값을 지정하는 이유

위 ML 스크립트에서 우리는 학습용 환자 데이터(모델의 입력 데이터)의 처음 5개 행을 training_sample에 따로 저장한 다음, 학습된 모델을 mlflow의 log_model 메소드로 저장할 때 input_example의 값으로 전달했습니다.

모델이 받을 수 있는 입력 데이터의 양식을 예시 데이터로 명시한 것인데요.

해당 모델이 실제로 사용될 때 들어오는 입력 데이터가 올바른 양식인지 미리 검증할 수 있고, MLflow 웹 UI에서 모델의 입력 데이터 예시를 확인할 수도 있기 때문에 ML 모델 개발에 큰 도움이 됩니다.

ML 스크립트 실행하기

이론 설명은 여기까지. 예제 ML 스크립트를 Kubernetes 환경에서 실행해보겠습니다. 먼저 지난 아티클에서 MLflow를 배포했던 minikube 클러스터를 다시 실행합니다.

minikube start -p {mlflow를 배포했던 클러스터명}

위에서 살펴본 ML 스크립트 실행에 필요한 Kubernetes 리소스는 2가지인데요.

  • ML 스크립트를 저장하는 ConfigMap 리소스

  • ML 스크립트를 Python 이미지로 실행하는 Job 리소스

각 리소스를 하나의 매니페스트 파일로 모아 정의합니다. 저는 train-mlflow.yaml이란 파일명으로 매니페스트를 저장해보겠습니다.

apiVersion: v1
kind: ConfigMap
metadata:
  name: train-script
data:
  train.py: |
    {ML 스크립트 붙여넣는 곳}

---
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

매니페스트에서 우리가 주의 깊게 봐야할 것은 train.py를 실행하는 명령어의 인자(Argument)를 1.0로 설정했다는 점입니다. 실습 ML 스크립트에서 언급했던 Ridge 모델의 하이퍼파라미터인 alpha 값을 1.0로 설정한다는 뜻인데요.

이렇게 처음에는 1.0으로 입력해서 ML 스크립트(모델 학습 및 평가 후 기록)를 돌리고, 다음번엔 다른 alpha 값으로 설정해서 다시 ML 스크립트를 돌릴 예정입니다.

매니페스트 파일이 준비되었다면 아래 명령어로 ConfigMap과 Job 리소스를 배포하여 ML 스크립트를 실행해봅시다.

kubectl apply -f {매니페스트 파일명}

그럼 새로운 ConfigMap과 Job이 배포될 텐데요. kubectl logs {새로 생성된 Job명} -f 명령어로 Job에서 출력되는 로그를 실시간으로 확인해볼게요.

ML 스크립트가 실행되기 전에 MLflow와 boto3, Sciket-learn 라이브러리가 먼저 설치되기 때문에 시간이 좀 걸릴 겁니다. 그래도 참고 기다려보면...

우리가 ML 스크립트에 명시한 로그와, MLflow의 Experiment와 Run이 저장되었다는 로그를 모두 확인할 수 있습니다. Job 로그를 실시간으로 보고있었다면 CTRL + C 단축키로 나옵니다.

MLflow 웹 UI에서 모델 학습 관련 데이터 살펴보기

이제 minikube 클러스터에 배포했던 MLflow의 웹 UI에 접속하고, ML 모델 학습 과정에서 생성된 데이터가 모두 잘 저장되었는지 확인해보겠습니다. 새로운 터미널창을 열어서 아래 명령어로 MLflow Service에 접근하는 URL을 생성합니다.

minikube service -p {minikube 클러스터명} -n {mlflow 네임스페이스명} {mlflow Service명}

웹 브라우저에서 해당 URL로 접속하면... 우리가 방금 실행한 ML 스크립트의 Experiment인 diabetes_prediction가 반겨줄 겁니다.🎉

해당 Experiment 이름을 클릭해 들어가면 방금 ML 스크립트를 돌리면서 생성된 Run이 표시되는데요. Run은 Experiment를 수행한 단위로, 각 Run마다 하이퍼파라미터와 성능 지표가 저장됩니다.

왼쪽 Models 탭을 누르면 저장된 ML 모델이 표시됩니다.

다시 Runs 탭으로 돌아온 다음, 최근 생성된 Run 이름을 누르면 우리가 ML 스크립트에서 저장하도록 명시한 mse, r2, alpha 값과 학습된 모델(my_model)까지 모두 확인할 수 있습니다.

이전 페이지(Experiment)로 가서 왼쪽의 Models 탭을 누른 다음 최근 저장된 모델(my_model)을 클릭하면 아래와 같이 모델 상세 페이지가 표시됩니다.

Metrics 패널을 보면 모델의 성능 지표로 기록했던 mser2가 있고, Parameters 패널에서는 우리가 모델을 학습할 때 설정했던 alpha 값이 보이네요. Runs 패널은 해당 모델의 Run이 표시됩니다.

오른쪽 패널의 가장 아래 Model versions에서는 저장된 모델의 버전을 확인할 수 있습니다. 우리가 ML 스크립트에서 모델을 저장(log_model)할 때 registered_model_name 파라미터의 값으로 diabetes_predictor를 전달했던 것 기억하시나요?

모델의 논리적인 그룹명을 지정한 것인데요. 이걸 Registered model이라고도 합니다. ML 모델 학습을 여러 번 수행할 때 registered_model_name 파라미터 값을 동일하게 설정해서 모델을 여러 번 저장하면, MLflow가 해당 모델이 동일하다고 판단하여 자동으로 버전을 1씩 올리며 관리해줍니다.

그러면... 모델 상세 페이지 맨 위에 있는 my_model은 무엇일까요?

우리가 MLflow 메소드로 모델을 저장할 때 name 파라미터로 전달한 모델 이름(Model name)입니다.

모델이 아티팩트 저장소에 실제 저장되는 폴더명을 지정하는 데에 사용되며, 해당 폴더는 각 Run ID 폴더 하위에 속합니다.

이 페이지의 Overview 탭 맨 오른쪽에 있는 Artifacts 탭을 들어가면 우리가 모델 학습을 하면서 생성 및 저장된 파일들을 확인할 수 있습니다.

200
  • MLmodel: 모델의 메타데이터

  • model.pkl: 저장된 ML 모델 파일

  • conda.yaml, requirements.txt, python_env.yaml: 모델이 실행될 때 필요한 라이브러리 등 환경 정보

  • input_example.json, serving_input_example.json: 모델의 입력 예제

    • MLflow 메소드로 모델 저장 시 input_example 파라미터 값이 지정되었을 경우 저장됨

마무리

우리는 이번 실습에서...

  • 실습에 사용할 ML 모델과 데이터셋, ML 스크립트를 자세히 알아봤고,

  • 로컬 Kubernetes 클러스터에서 실습용 ML 스크립트를 Kubernetes Job으로 실행했으며,

  • ML 스크립트 수행으로 생성된 각종 데이터를 MLflow 웹 UI로 차근차근 살펴봤습니다.

MLflow는 현업에서 많이 사용되는 오픈소스 MLOps 플랫폼입니다. 그래서 우리가 알아둬야 할 내용이 아직 많은데요.

하지만 너무 방대한 분량을 한 번에 다루면 아티클을 읽고 실습하는 데에 부담이 될 수 있겠다 생각해서 여기서 한 번 끊어가려 합니다.

다음 아티클에서는 실제 ML 개발과 같이 하이퍼파라미터를 여러번 조정하는 실험(Experiment)을 거치고, 모델 버전별로 성능 지표를 비교해보도록 하겠습니다.

저번 아티클부터 MLflow 실습 가이드 시리즈를 다루고 있는데요. 계속 함께해주셔서 감사합니다. 이어지는 다음 실습에서 또 뵙겠습니다.

오늘도 행복한 하루 되세요😸

아 맞다, minikube!

만약 실습을 끝냈다면 사용 중이던 minikube 클러스터를 중지해야 리소스를 낭비하지 않을 수 있습니다. 로컬 PC의 리소스도 소중하니까요.

minikube 실습을 끝낼 때는 항상 아래 명령어를 기억해주세요👇

minikube stop -p {minikube 클러스터명}

함께 읽어보면 좋은 아티클

이번 뉴스레터는 어떠셨나요?

이번 글에서 다룬 주제에 대해 어떻게 생각하는지 알려주세요! 뉴스레터를 더 나은 방향으로 개선하기 위해 아래 폼에서 짧은 피드백을 받고 있어요.

👉 피드백 보내기 (1~2분 소요)

여러분들의 소중한 의견은 Aiden’s Lab 뉴스레터에게 큰 힘이 됩니다!

🔭Aiden’s Lab에서 더 많은 아티클을 만나보세요

발행된 뉴스레터를 아카이빙하고 다양한 정보를 공유하는 기술 블로그를 운영 중입니다.