프로젝트 일지/분모자

MLflow - MLflow 개요 & Tracking 기초 (with python)

데브겸 2023. 3. 7. 17:39

 

공식 도큐먼트를 보고 싶으신 분은 여기로 

 

0. MLflow란?

머신러닝 라이프사이클을 관리해주는 플랫폼.

MLflow가 등장하기 이전에는 다음과 같은 어려움이 있었음

1) 실험 트래킹의 어려움: 개인의 로컬 환경이나 노트북에서 진행하다보니 어떤 데이터, 코드, 파라미터로 실험했는지 트래킹하기 어려움

2) 코드 재현성의 어려움: 환경 설정이나 라이브러리 의존성 등 코드 외에 사항들 때문에 재현이 어려움

3) 표준 패키징과 배포 방법의 부재: 각 개인, 팀마다 방법이 다 다름 

4) 모델 관리를 위한 중앙 저장소의 부재: 중앙 저장소 없이 협업을 하려니 너무너무 힘듦

 

MLflow는 아래의 컴포넌트들로 문제를 해결하려 함

1) MLflow Tracking

: 머신러닝 실험에서 사용, 발생한 파라미터, 코드 버전, 평가 결과, 아티팩트 등을 로깅해주는 API와 UI를 제공

 

2) MLflow Projects

: conda나 docker를 활용하여 모델들을 재실행(run)할 수 있도록 하는 코드 패키징 포맷 제공

- Dockerfile이나 Docker-compose.yaml와 비슷한 형태의 MLproject 파일에 프로젝트, 패키징에 필요한 정보를 적어 관리

 

3) MLFlow Models

: docker, spark나 클라우드의 ml 제품 들에 쉽게 배포할 수 있도록하는 패키징 포맷 제공

 

4) MLFlow Registry

: 전체 라이프사이클을 관리해주는 (협업하게 도와주는) 중앙 모델 저장소

- 모델 저장소에서 모델이 저장될 때마다 버전을 기록하여 버전관리를 도와줌

- Registry에 모델을 쉽게 올리고, 다운로드 받을 수 있어 협업을 용이하게 함

 

 

1. MLflow 설치하기

conda 가상 환경에서 하고 싶어서 'mlops'라는 가상환경 실행, mlflow 설치

$ conda activate mlops
$ pip install mlflow

 

 

2. MLflow 기본 기능 익히기 (Tracking)

참고

0. MLproject
MLflow는 MLproject라는 파일을 통해 전체 프로젝트에 대한 정보를 기록함. MLproject에 정보를 기록하면 MLflow가 project URI와 source version을 기억할 수 있음

1. Experiment (실험)
MLflow에서는 머신러닝 프로젝트의 단위를 'Experiment'로 설정. Experiment는 쉽게 말하자면 하나의 프로젝트와 비슷. 즉, '브랜드 로고 detection 실험', 'chatting translation 실험' 등 공통 평가 지표를 지니고 수행하는 프로세스.

2. Run (실행)
Experiment는 여러 Run으로 이루어질 수 있음. loss function, 모델 파라미터 등을 바꿔 실험을 여러 번 실행해보는 것 (즉, 하나의 실험은 여러 개의 실행을 가질 수 있음). MLflow에는 각 run에 대한 결과들이 각각 기록됨. '브랜드 로고 detection 실험의 파라미터 1번 세팅 실행, 2번 세팅 실행 등등'

 

2-1. MLproject

MLflow의 가장 기본적인 기능은 ML 실험에서의 모델, 평가, 파라미터 등을 로깅하는 것

이것이 어떻게 진행되는가를 MLflow 공식 문서에 있는 sklearn_elasticnet_wine 예제를 통해서 보겠음 (우선 로컬에 기록을 저장하는 방향으로 실습 진행)

git clone https://github.com/mlflow/mlflow
cd mlflow/examples/sklearn_logistic_regression

폴더 구성은 위와 같음

MLflow는 MLproject라는 파일을 통해 전체 프로젝트(이 경우 'sklearn_logistic_regression' 프로젝트)에 대한 메타 정보나 환경 등을 설정 (Dockerfile처럼 MLproject로 이름 고정)

# MLproject

name: sklearn_logistic_example

python_env: python_env.yaml

entry_points:
  main:
    command: "python train.py"

 

2-2. Tracking

그럼 우리는 mlflow로 무엇을 할 것이냐

# train.py

import numpy as np
from sklearn.linear_model import LogisticRegression

import mlflow
import mlflow.sklearn

if __name__ == "__main__":
    X = np.array([-2, -1, 0, 1, 2, 1]).reshape(-1, 1)
    y = np.array([0, 0, 1, 1, 1, 0])
    lr = LogisticRegression()
    lr.fit(X, y)
    score = lr.score(X, y)
    print("Score: %s" % score)
    mlflow.log_metric("score", score)
    mlflow.sklearn.log_model(lr, "model")

위 코드를 잘 보면 log_metric과 log_model 부분이 있음. 이를 통해 mlflow에 metricr과 model을 기록 (=트래킹)!!

 

mlflow에서 로깅할 수 있는 것들은 아래와 같다

 

  • mlflow.log_param(): logs a single key-value param in the currently active run. The key and value are both strings
    • log_params()로 여러 개의 파라미터를 한 번에 로깅 가능
  • mlflow.log_metric(): logs a single key-value metric. The value must always be a number. MLflow remembers the history of values for each metric. 
    • log_metrics()로 여러 개의 메트릭을 한 번에 로깅 가능
  • mlflow.set_tag(): sets a single key-value tag in the currently active run. The key and value are both strings.
    • set_tags()로 여러 개의 태그를 한 번에 로깅 가능
  • mlflow.log_artifact(): logs a local file or directory as an artifact, optionally taking an artifact_path to place it in within the run’s artifact URI. Run artifacts can be organized into directories, so you can place the artifact in a directory this way.
    • log_artifacts()로 주어진 모든 디렉토리의 것들을 아티팩트로 저장 가능

 

 

train.py 파일을 실행

python train.py

위를 보면 model이 run d623~에 저장되었다는 안내를 볼 수 있음

 

다시 한 번 ls를 해보면 mlruns라는 폴더가 새로 생긴 것을 확인할 수 있음. MLflow는 이 mlruns 폴더에 실행 내용을 기록함 

 

tree로 mlruns 폴더를 살펴보면 artifacts(model), metric, param 등이 기록된 것을 확인할 수 있음.

참고로 mlruns 바로 아래에 있는 0는 experiment ID이고, 그 아래 d623~은 run ID이다.

즉, 0이라는 실험에 대한 d623 실행의 기록을 tree 상으로 확인하는 셈이다.

 

참고: Model Artifact란?

Model artifacts are the output that results from training a model, and typically consist of trained parameters, a model definition that describes how to compute inferences, and other metadata. (출처: https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_ModelArtifacts.html)

 

 

 

2-3. 웹 대시보드에서 확인하기 

MLflow는 웹 대시보드 형태로 실험과 실행을 관리할 수 있음. 터미널 환경에서 ui로 접속하고 싶다면 아래 명령어 입력하기

# 터미널 하나 더 켜서 
mlflow ui

http://127.0.0.1:5000 으로 접속하면 아래와 같은 화면을 볼 수 있음

클릭하여 상세 페이지로 접속

아까 터미널 환경에서 떴던 run id d623~를 다시 확인할 수 있음. 그리고 그 아래에 프로젝트에 대한 상세한 정보를 확인할 수 있음

 

3. Automatic logging

그런데 예시처럼 내가 로깅할 것을 하나하나 다 수기로 작성해서 하면... 생각보다 귀찮은 작업이 될 것이다.

이를 해결해주는 것이 바로 autolog! 사용자가 명시하지 않아도 mlflow에서 자동으로 ml의 요소들을 로깅해준다

하지만 지원되는 프레임워크들이 한정적이라 잘 살펴보고 써야 한다는 것이 큰 단점...

2023.03 기준 지원 목록

pytorch의 경우 pytorch_lightning에서만 지원하고 있다는.... 크나큰 단점이 존재한다

 

 

여튼 mlflow examples에 있는 sklearn_autolog 예시로 autolog 기능을 알아보자.

 

cd example/sklearn_autolog/

 

sklearn_autolog는 폴더는 다음과 같이 구성되어 있다.

 

 

linear_regression.py는 autolog의 기본적인 사용법을 익힐 수 있는 예시 파일이다

pipeline.py는 sklearn에서 제공하는 pipeline에도 autolog를 적용할 수 있다는 것을 보여주는 예시 파일이다.

grid_search_cv.py는 gridsearch에서 또한 autolog를 적용할 수 있고, 이 때 어떤 것들이 logging 되는지 보여주기 위한 예시이다.

 

그럼 가장 기본이 되는 linear_regression.py부터 살펴보자

 

3-1. linear_regression.py

linear_regression.py의 코드는 아래와 같다

from pprint import pprint

import numpy as np
from sklearn.linear_model import LinearRegression

import mlflow
from utils import fetch_logged_data


def main():
    # enable autologging
    mlflow.sklearn.autolog()

    # prepare training data
    X = np.array([[1, 1], [1, 2], [2, 2], [2, 3]])
    y = np.dot(X, np.array([1, 2])) + 3

    # train a model
    model = LinearRegression()
    model.fit(X, y)
    run_id = mlflow.last_active_run().info.run_id
    print("Logged data and model in run {}".format(run_id))

    # show logged data
    for key, data in fetch_logged_data(run_id).items():
        print("\n---------- logged {} ----------".format(key))
        pprint(data)


if __name__ == "__main__":
    main()

 

위 코드가 일반 linear regression 코드와 다른 점은 두 부분

  • mlflows.sklearn.autolog() : autolog를 사용하기 위해 호출
    • autolog 기능을 사용하기 위해서 맨 처음에 작성해야 한다
    • logging information
      • parameters
      • training metrics (precision, recall, f1, accuracy, log loss, roc auc score, mse, rmse 등)
      • post training metrics (model.score, metic APIs degined in the sklearn.metrics module)
      • tags (모델의 패키지와 클래스 이름)
      • artifacts (run에 대한 메타 데이터와 모델 파일)

 

  • run_id = mlflow.last_active_run().info.run_id: 가장 최근의 실행 정보를 가져오는 것
    • 위 예시 코드에서는 autolog의 결과물을 보여주기 위해 이용함

 

아래 코드로 스크립트 파일 실행

python linear_regression.py

출력된 결과물을 통해 param, metric, tag, artifact 들이 로깅된 것을 확인할 수 있음.

예시 코드에서는 mlflows.sklearn.autolog()외에는 명시적으로 지정해준 것이 없으나, mlflow의 autolog가 자동으로 로깅해준 것.

 

 

 

3-2. pipeline.py

pipeline.py의 코드는 아래와 같다

# pipeline.py

from pprint import pprint

import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline

import mlflow
from utils import fetch_logged_data


def main():
    # enable autologging
    mlflow.sklearn.autolog()

    # prepare training data
    X = np.array([[1, 1], [1, 2], [2, 2], [2, 3]])
    y = np.dot(X, np.array([1, 2])) + 3

    # train a model
    pipe = Pipeline([("scaler", StandardScaler()), ("lr", LinearRegression())])
    pipe.fit(X, y)
    run_id = mlflow.last_active_run().info.run_id
    print("Logged data and model in run: {}".format(run_id))

    # show logged data
    for key, data in fetch_logged_data(run_id).items():
        print("\n---------- logged {} ----------".format(key))
        pprint(data)


if __name__ == "__main__":
    main()

 

linear_regression.py 때와 마찬가지로 mlflow.sklearn.autolog()외에 따로 설정해준 것은 없음

 

python pipeline.py

 

하지만 스크립트 파일을 실행을 했을 때 pipeline에 등록되어 사용된 scaler와 lr 그리고 metrics, tags, artifacts 등이 자동으로 기록된 것을 확인할 수 있음.

 

linear_regression.py의 결과물과 다른 점은

  • params
    • lr / lr__copy_X / lr__fit_intercept / lr__n_jobs / lr__positive (lr 빼고는 다 있었지만 'lr__ ' 부분이 추가됨)
    • memory
    • scaler scaler__copy / scaler__with_mean / scaler__with_std / steps / verbose

sklearn 내에 다양한 메소드에 대해서 각기 다른 autolog를 수행한다는 것을 알 수 있음

 

 

3-3. gridsearch.py

gridsearch.py의 코드는 아래와 같다

 

from pprint import pprint

import pandas as pd
from sklearn import svm, datasets
from sklearn.model_selection import GridSearchCV

import mlflow
from utils import fetch_logged_data


def main():
    mlflow.sklearn.autolog()

    iris = datasets.load_iris()
    parameters = {"kernel": ("linear", "rbf"), "C": [1, 10]}
    svc = svm.SVC()
    clf = GridSearchCV(svc, parameters)

    clf.fit(iris.data, iris.target)
    run_id = mlflow.last_active_run().info.run_id

    # show data logged in the parent run
    print("========== parent run ==========")
    for key, data in fetch_logged_data(run_id).items():
        print("\n---------- logged {} ----------".format(key))
        pprint(data)

    # show data logged in the child runs
    filter_child_runs = "tags.mlflow.parentRunId = '{}'".format(run_id)
    runs = mlflow.search_runs(filter_string=filter_child_runs)
    param_cols = ["params.{}".format(p) for p in parameters.keys()]
    metric_cols = ["metrics.mean_test_score"]

    print("\n========== child runs ==========\n")
    pd.set_option("display.max_columns", None)  # prevent truncating columns
    print(runs[["run_id", *param_cols, *metric_cols]])


if __name__ == "__main__":
    main()

 

특이한 점은 아까 보이지 않았던 'parent run'과 'child run'이라는 개념이 새로 등장한 것

document를 찾아보니 parent run과 child run은 sklearn의 gridsearch에서만 나오는 개념인 것 같다

 

  • parent run에서는 파이프라인 전체에서의 파라미터나 class name, artifact / gridsearch에서 찾은 best parameter 등 이 기록되는 것 같고
  • child run에서는 gridsearch를 진행할 때 각각의 경우의 수에 대한 run들에 대한 정보들을 기록되는 것 같다
    • 각각의 child run도 어쨌거나 run이기 때문에 고유한 run id를 가지게 된다

 

python grid_search_cv.py

 

실행하면 아래와 같은 결과 화면이 출력된다

parent run에는 앞선 두 예시와 비슷하지만 best_estimator 관련한 것들을 logging 진행한다는 점에서 조금 다르게 결과가 출력

child run에서는 각각 어떤 param으로 돌렸는지와 metric이 얼마 나왔는지를 출력한다

 

 

 

tree로 확인해보니 독립된 run들이 쫘라락 기록된 것을 확인할 수 있다

 

독특한 것은 child run의 tag 중에 'mlflow.parentRunId'태그가 있는데,

run id가 4d62~인 child run의 parentRunId를 보면 8239~로 시작하는 parent run id가 기록되어 있는 것을 확인할 수 있다

 

mlflow ui에서는 아래 빨간색 박스와 같은 방식으로 위계를 표현한 것을 확인할 수 있다