MLOps

[MLOps] 17. MLflow에 모델 저장하기 실습

mlslly 2024. 4. 30. 14:33

* 프로그래머스의 마키나락스 MLOPS 강의를 참고하여 작성함

 

이제 모델을 저장소에 저장하는 실습을 해보도록 한다. 

모델의 저장 방법 및 구조 (docker compose)에 대해서는 이전 포스팅 참고

https://ysryuu.tistory.com/36 

 

[MLOps] 16. 모델 저장소 구축 방법 (feat. 도커 컴포즈)

* 프로그래머스의 마키나락스 MLOPS 강의를 참고하여 작성함 앞서 기획했던 모델 저장 아키텍쳐대로, 모델 저장소를 구축해 보도..

ysryuu.tistory.com

 

 

모델 저장하기의 전체 구조는 아래와 같다. 

 

1) Minio에서 최신 데이터를 다운로드하고 

2) HPO (옵튜나를 활용해 하이퍼 파라미터를 최적화 하면서) 과정에서 MLflow에 실험 내용을 로깅한다 

3) 최적화를 통해 best 모델을 찾아 학습시키고

4) MLflow 서버에, 아티펙트 스토어에 모델을 저장한다

 

모델 저장하기

 

1. 아티펙트 스토어 패키지 설치 

$ pip3 install boto3

 

 

2. 터미널 계속 사용을 위한 처리 

$ docker compose up -d

-d는 detach 옵션으로, Docker Compose를 사용하여 정의된 서비스를 백그라운드에서 실행하는 명령어. 

현재 디렉토리에 있는 docker-compose.yml 파일을 기반으로 Docker 컨테이너를 시작하고, -d 옵션은 컨테이너를 백그라운드에서 실행하도록함

 

3. 모델 파일 작성 (best model 저장)

 

<optuna_train_best_minio_save.py 파일 내용>

import os
import uuid
import mlflow
import optuna 
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
from minio import Minio
import pandas as pd

UNIQUE_PREFIX = str(uuid.uuid4())[:8]

BUCKET_NAME = 'raw-data'
OBJECT_NAME = 'iris'

os.environ['MLFLOW_S3_ENDPOINT_URL'] = 'http://0.0.0.0:9000'
os.environ['MLFLOW_TRACKING_URI'] = 'http://0.0.0.0:5001'
os.environ['AWS_ACCESS_KEY_ID'] = 'minio'
os.environ['AWS_SECRET_ACCESS_KEY'] = 'miniostorage'

def download_data():

    # minio 클라이언트 
    url = '0.0.0.0:9000'
    access_key = 'ysminio'
    secret_key = 'ysstorage*'
    client = Minio(url, access_key=access_key, secret_key=secret_key, secure=False )

    # 데이터 다운로드 
    object_stat = client.stat_object(BUCKET_NAME, OBJECT_NAME)
    data_version_id = object_stat.version_id
    client.fget_object(BUCKET_NAME, OBJECT_NAME, file_path='download_data.csv')
    return data_version_id


def load_data():
    data_version_id = download_data()
    df = pd.read_csv('download_data.csv')
    X,y = df.drop(columns=['target']), df['target']
    data_dict = {'data':X, 'target':y, 'version_id':data_version_id }
    return data_dict

def objective(trial) :
    # suggest new parameter
    trial.suggest_int('n_estimators',100,1000, step=100)
    trial.suggest_int('max_depth', 3,10)

    run_name = f'{UNIQUE_PREFIX}-{trial.number}'
    with mlflow.start_run(run_name=run_name):

        # load data
        data_dict = load_data()
        mlflow.log_param('bucket_name', BUCKET_NAME)
        mlflow.log_param('object_name', OBJECT_NAME)
        mlflow.log_param('version_id', data_dict['version_id'])
        X, y = data_dict['data'], data_dict['target']

        X_train, X_valid, y_train, y_valid = train_test_split(X,y,test_size=0.3, random_state=2024)

        # train model

        clf = RandomForestClassifier(n_estimators=trial.params['n_estimators'], max_depth=trial.params['max_depth'], random_state=2024)
        clf.fit(X_train, y_train)

        # evaluate data

        y_pred = clf.predict(X_valid)
        acc_score = accuracy_score(y_valid, y_pred)
        
        # log metrics
        mlflow.log_metrics({'accuracy': acc_score})
    
    return acc_score


def train_best_model(params) : 

    run_name = f'{UNIQUE_PREFIX}-best-model'
    with mlflow.start_run(run_name=run_name): # logging
        
        #log parameter
        mlflow.log_params(params)
        
        # load data
        data_dict = load_data()
        mlflow.log_param('bucket_name', BUCKET_NAME)
        mlflow.log_param('object_name', OBJECT_NAME)
        mlflow.log_param('version_id', data_dict['version_id'])
        X, y = data_dict['data'], data_dict['target']

        # train model
        clf = RandomForestClassifier(n_estimators=params['n_estimators'], max_depth=params['max_depth'], random_state=2024)
        clf.fit(X, y) 
    
        # save model 
        mlflow.sklearn.log_model(sk_model=clf, artifact_path= 'my_model' )

        return clf


if __name__ == '__main__' : 

    # set mlflow
    mlflow.set_experiment('hpo-tutorial')

    # study
    sampler = optuna.samplers.RandomSampler(seed=2024)
    study = optuna.create_study(sampler=sampler, study_name = 'hpo-tutorial', direction='maximize')

    # optimize
    study.optimize(objective, n_trials=5)

    best_params = study.best_params
    best_clf = train_best_model(best_params)

 

 

4. 파일을 다시 업로드 

$ python3 upload_data.py

 

 

5. 모델 파일 실행

$ python3 optuna_train_best_minio_save.py

 

6. MLflow 확인

 

hpo-tutorial 이라는 이름의 experiment가 추가된 것을 볼 수 있으며, 

optuna trial 마다 총 5개의 모델이 저장되고, 가장 성능이 좋았던 best param 모델은 따로 Models란에 sklearn 으로 추가된 것을 확인할 수 있음 

 

또한 모델 (sklearn)을 클릭하면,

best model의 아티펙트들이 'my_model'이라는 폴더 하에 저장된 것을 볼 수 있음