![Poetry, PyCaret을 사용한 모델 배포](https://image.inblog.dev?url=https%3A%2F%2Finblog.ai%2Fapi%2Fog%3Ftitle%3DPoetry%252C%2520PyCaret%25EC%259D%2584%2520%25EC%2582%25AC%25EC%259A%25A9%25ED%2595%259C%2520%25EB%25AA%25A8%25EB%258D%25B8%2520%25EB%25B0%25B0%25ED%258F%25AC%26logoUrl%3Dhttps%253A%252F%252Finblog.ai%252Finblog_logo.png%26blogTitle%3D%25EB%258D%25B0%25EC%259D%25B4%25ED%2584%25B0%2520%25EC%2593%25B0%25EB%258A%2594%2520%25EB%25AC%25B8%25EB%258D%2595%25EB%25B0%25B0&w=2048&q=75)
최근 빠르게 ML 모델을 빌드하고 배포하는 과정을 여러 번 반복적으로 테스트해야 하는 상황에 직면했습니다. 이를 위해 다양한 라이브러리를 사용해 보았고, 최종적으로는 Poetry와 PyCaret을 사용하는 것이 가장 효율적이었습니다. Poetry는 의존성 충돌을 자동으로 해결해주는 점에서 편리했으며, PyCaret은 코드 템플릿화를 쉽게 할 수 있을 뿐 아니라, 모델 배포를 통한 API 개발까지 지원해주는 점이 좋았습니다. 이번 글에서는 Titanic 데이터를 활용해 이 두 라이브러리를 사용하여 모델을 빌드하고 배포하는 과정을 단계별로 설명하는 튜토리얼 코드를 기록합니다.
환경설정
- 가상환경은 poetry로 생성 후 진행함
- https://python-poetry.org/docs/basic-usage/
- poetry 설치 (Ubuntu 22.04 기준)
pip install poetry ---------------------- 만약 poetry 명령어를 사용할 수 없는 경우, echo $PATH -> 출력된 경로에 ~/.local/bin이 포함되어있는지 확인 없다면, echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc source ~/.bashrc ------------------------- poetry --version # Poetry (version 1.8.3) poetry config virtualenvs.in-project true # 프로젝트 폴더 내 가상환경 파일 설치
- 프로젝트 폴더 생성 후 가상환경 설정
mkdir my_project cd my_projec poetry init
poetry init
명령어를 실행하면 종속성 등 설정을 입력해줄 수 있는데, 어차피 프로젝트를 진행하면서 종속성을 맞춰줄 것이므로 일단 모두 넘어감- 가상환경 생성
poetry install
pyproject.toml
에 명시된 의존성을 설치하며, 가상환경 생성. 아직 의존성을 정의하지 않은 상태이기 때문에, 의존성 설치 없이 가상환경만 생성함- 가상환경 실행
poetry shell # 가상환경을 나가고 싶다면 `exit` 입력
- 라이브러리 설치
- 가상환경 내 라이브러리 설치는
poetry add
명령어 사용하며, 할 때마다 pyproject.toml 업데이트 됨
poetry add jupyter seaborn pycaret[mlops] ...
- 다만 이런 식으로 라이브러리를 설치할 때 의존성 맞춰주는 작업이 필요한데, 본문의 예시를 위해 최종적으로 수정한 pyproject.toml 파일은 아래와 같음
[tool.poetry] name = "pycaret-test" version = "0.1.0" description = "" authors = ["..."] readme = "README.md" [tool.poetry.dependencies] python = "^3.10" pycaret = {extras = ["mlops"], version = "^3.3.2"} kaleido = "0.2.1" pandas = "<2.2.0" jupyter = "^1.0.0" seaborn = "^0.13.2" fastapi = "^0.111.0" uvicorn = "^0.30.1" sktime = {version = "0.26.0", python = ">=3.10,<3.13"} [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api"
PyCaret을 활용한 모델 배포
목표: Titanic data를 사용해 간단한 모델을 만든 후 배포해보기
- Jupyter Notebook에서 아래 코드를 사용해 간단히 모델 생성
import pandas as pd from pycaret.classification import * import seaborn as sns data = sns.load_dataset(('titanic')) data = pd.DataFrame(data) # 대강 전처리 data['age'].fillna(data['age'].median(), inplace=True) data['embarked'].fillna(data['embarked'].mode()[0], inplace=True) data.rename(columns={'class': 'class_'}, inplace=True) s = setup(data=data, target='survived') # 모델 학습 # compare_models() # 10분 정도 시간 소요 # model = create_model() model = create_model('lr') tuned_model = tune_model(model) final_model = finalize_model(tuned_model) # save_model(final_model, 'titanic_survival_model') # titanic_survival_model.pkl 생성 # final_model = load_model('titanic_survival_model')
모델 배포를 위한 API 생성
create_api()
: PyCaret 모델을 기반으로 FastAPI 앱을 생성하여 REST API로 추론 가능하게 함
create_api(final_model, 'titanic_survival_model_api')
- 위 명령어가 잘 실행되었을 경우 titanic_survival_model_api.py, titanic_survival_model_api.pkl 파일이 생성됨
- 다만, 자동으로 생성된 titanic_survival_model_api.py 파일은 아래와 같은 문제가 있으므로 수정 필요
- 만약 기본값으로 nan 값이 들어가있다면 에러 발생. numpy를 추가로 import 해주거나, 기본값 변경해줘야 함
- ‘class’ 라는 열 이름은 사용할 수 없음(예약어)
- input_model, output_model의 필드 타입 정의가 부실함
pydantic
의create_model
을 사용할 때 필드 타입을 명시해주고- 데이터프레임 생성시
dict
의by_alias=True
옵션을 사용하여 JSON 데이터와 필드 이름을 일치시켜줘야함 - 호스트를
127.0.0.1
로 설정하면 외부에서 접근할 수 없음.0.0.0.0
으로 변경 - 기본 8000번 포트 변경 필요
최종적으로 수정한 titanic_survival_model_api.py
# -*- coding: utf-8 -*- import pandas as pd from pycaret.classification import load_model, predict_model from fastapi import FastAPI import uvicorn from pydantic import BaseModel app = FastAPI() model = load_model("titanic_survival_model_api") class InputModel(BaseModel): pclass: int = 3 sex: str = 'male' age: float = 22.0 sibsp: int = 0 parch: int = 0 fare: float = 9.0 embarked: str = 'S' class_: str = 'Third' # 'class'는 예약어이므로 'class_'로 변경 who: str = 'man' adult_male: bool = True deck: str = None embark_town: str = 'Southampton' alive: str = 'no' alone: bool = True class OutputModel(BaseModel): prediction: int @app.post("/predict", response_model=OutputModel) def predict(data: InputModel): data = pd.DataFrame([data.dict(by_alias=True)]) predictions = predict_model(model, data=data) return {"prediction": predictions["prediction_label"].iloc[0]} if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8001)
- 아래 명령어 입력 후 192.168.10.201:8001/docs 접속해서 정상 작동하는지 확인
python titanic_survival_model_api.py
Docker Container 생성
- 학습된 PyCaret 모델을 Docker Container로 배포할 수 있는 환경을 자동으로 생성해줌
create_docker('titanic_survival_model_api', base_image = "python:3.10", expose_port = 8001)
docker image build -f "Dockerfile" -t titanic_test:latest . docker run -d -p 8001:8001 titanic_test docker ps # 컨테이너가 잘 실행되었는지 확인
- 192.168.10.201:8001/docs 접속해서 정상 작동하는지 확인
기타 이슈 기록
- conda는 pip와 독립된 생태계를 사용하기도 하고, 가상환경과 의존성 관리가 분리되어 있는 점이 불편했음. 또한 pipenv에서 의존성이 충돌나던 라이브러리들이 poetry에서는 해결되는 경우가 있었음
- 그러나 poetry가 모든 pip 라이브러리 버전을 지원하지는 않는 듯(pip에 등록되어있는 버전인데도 poetry로 설치되지 않는 경우가 있음)
- pycaret은 H2O, AutoGluon보다 배우기가 쉬웠고, Auto-sklearn보다 짧은 코드로 모델링할 수 있었음
- 다만 pycaret을 사용할 때, 여러 모델의 성능을 비교하기 위한 커스텀 지표가 validation과정에서는 적용이 안된다거나, 모델링 시
setup()
의use_gpu
,n_jobs
옵션이 일부 경우에 적용되지 않는 등의 문제가 있음
Share article