Summary:

BentoML를 활용해 머신러닝 모델 서빙을 더 쉽게 하는 방법에 대해 작성한 글입니다.

image-20211017235128747


안녕하세요. 이번 포스팅에서는 Machine Learning 모델을 Serving할 때 주로 사용되는 BentoML에 대해서 살펴보도록 하겠습니다. 7월 13일에 BentoML이 정식으로 release된 이후에 많은 변화가 있었습니다. 이미 BentoML에 대한 글들은 많지만, 대부분 v1.0.0 이전 버전으로 작성된 포스팅이었습니다. 그래서 이번 글은 BentoML - v1.0.12에 맞춰서 새롭게 작성하였습니다.

저도 딥러닝 모델을 프로덕션에 배포할 때 BentoML을 사용하여 시간을 절약할 수 있었습니다. 자신이 학습 시킨 모델을 간단하게 배포할 수 있는 기회가 생겼다면, BentoML을 강력히 추천드립니다. 그 이유는 Serving API를 단순하게 구현할 수 있기 때문입니다. 다음 글에서 BentoML을 사용하여 어떻게 모델을 배포할 수 있는지 구체적으로 알아보겠습니다.

목차

  1. BentoML이란?
  2. BentoML 모델 패키징
  3. 서비스 API
  4. Bentos 만들기
  5. Bentos 배포 방법

1. BentoML이란?

image-20221224154313453

BentoML은 Machine Learning Engineer와 Data Analyst가 모델의 개발, 관리 및 배포의 과정을 복잡한 과정 없이 할 수 있도록 도와주는 Open source framework입니다. model save, serving, data drift, monitoring 등과 같은 운영 과정을 자동화하고, 이를 컨테이너화해 원활한 배포를 지원합니다. 이를 통해 MLOps 과정에서의 수동 작업과 오류를 줄이고, 개발 생명주기를 가속화할 수 있습니다. 머신러닝 모델을 프로덕션으로 패키징하고 배포하기 위한 Tool들과 Pytorch, Tensorflow 등 머신러닝 프로젝트에서 자주 사용되는 프레임워크들의 예시를 제공하여 쉽게 사용할 수 있습니다.

2. BentoML 모델 패키징

학습 진행 후 모델 저장

BentoML을 사용하여 학습된 머신러닝 모델을 저장하려면 BentoML API를 사용해야합니다. 대부분의 경우 저장을 위해서는 모델 학습 파이프라인의 마지막에 save_model을 호출하는 코드 한 줄만 추가하면 됩니다. 다음은 BentoML을 사용하여 pytorch 모델을 패키징하는 단계에 대한 설명입니다.

  • 우선 BentoML을 설치하고 Pytorch 모델을 학습시킵니다.
  • 학습이 종료되면 bentoml.pytorch.save로 학습된 모델을 저장합니다.
bentoml.pytorch.save_model(
    "demo_mnist",   # 모델 이름
    trained_model,  # 학습된 모델
    labels={    # Yatai에서 모델 관리를 위한 이름
        "owner": "nlp_team",
        "stage": "dev",
    },
    metadata={  # 학습 metadata
        "acc": acc,
        "cv_stats": cv_stats,
        "dataset_version": "20210820",
    },
    custom_objects={    # user-defined python objects 저장
        "tokenizer": tokenizer_object,
    }
)
  • labels : 모델 관리를 위한 사용자 정의 label(예: team=nlp, stage=dev).
  • metadata : 데이터셋 버전, weight, ACC와 같은 모델 훈련 정보 또는 모델 평가 지표를 저장하기 위한 사용자 정의 메타데이터.
  • custom_objects : 사용자 정의 추가 Python Object

위와 같이 저장 시 labels, metadata, custom_objects를 추가로 저장할 수 있습니다. labels는 모델을 관리할 때 사용할 수 있는 사용자 정의 레이블로, metadata는 모델 학습 배경 정보나 모델 평가 지표를 저장할 수 있는 사용자 정의 추가 정보로, custom_objects는 토크나이저 인스턴스, 전처리 함수 등을 저장할 수 있습니다.

이미 저장된 파일이 있다면, 파일을 불러온 후 BentoML 프레임워크에서 제공하는 save_model 메소드를 사용하여 BentoML의 model store에 저장할 수 있습니다. 권장하는 방법은 학습과 검증이 완료된 모델을 즉시 BentoML로 저장하는 것입니다.

모델 관리

위에서 저장된 모델은 BentoML 저장소에 보관되어 있습니다. CLI 명령어를 통해서 간단하게 검색이 가능합니다.

$ bentoml models list
 Tag                                                   Module               Size         Creation      Time
 pytorch_mnist:gmkted4dgkuoyycf  bentoml.pytorch  1.30 MiB  2022-12-24 02:25:14
 pytorch_mnist:s27yyyudgguoyycf  bentoml.pytorch  1.30 MiB  2022-12-24 02:20:52
 pytorch_mnist:gdkob74dgcuoyycf  bentoml.pytorch  1.30 MiB  2022-12-24 02:10:51

3. 서비스 API

BentoML 모델을 저장한 후에는 bentoml 명령어를 사용하여 쉽게 공유하고 배포할 수 있습니다. BentoML Service는 Runner와 API로 구성됩니다.

Runner

Runner는 BentoML 아키텍처에서 실행될 수 있는 연산의 단위로, 원격 Python 워커에서 독립적으로 스케일할 수 있습니다. GPU가 있을 경우 자동으로 GPU 사용, 스레드와 워커의 수를 자동 설정하고, 모델 시그니처를 Runnable 메서드로 변환해 줍니다. BentoML은 사용자가 Custom Runner 클래스를 정의할 수 있는 기능도 제공합니다. 저는 처음 BentoML을 적용할 때 이미 학습된 모델을 사용해야 했기 때문에 Custom Runner를 사용해서 infernece를 진행했습니다.

API

API는 서비스의 기능을 원격으로 호출할 수 있는 방법을 정의합니다. Service는 하나 이상의 API를 가질 수 있습니다. API는 input/output 명세와 콜백 함수로 구성됩니다. @svc.api로 decorate된 함수를 선언하면 이 API가 호출될 때 함수가 호출됩니다.

# service.py
...(생략)
mnist_runner = bentoml.pytorch.get("pytorch_mnist").to_runner()

svc = bentoml.Service(name="pytorch_mnist_demo", runners=[mnist_runner])

@svc.api(
    input=NumpyNdarray(dtype="float32", enforce_dtype=True),
    output=NumpyNdarray(dtype="int64"),
)
async def predict_ndarray(inp: NDArray[t.Any]) -> NDArray[t.Any]:
    # shape 체크
    assert inp.shape == (28, 28)
    # batch와 channel dimension 추가
    inp = np.expand_dims(inp, (0, 1))
    output_tensor = await mnist_runner.async_run(inp)
    return output_tensor.detach().cpu().numpy()

Bentoml serve 명령을 사용하여 API 서버를 시작할 수 있습니다. 그러면 BentoML 패키지에 정의된 API 엔드포인트가 노출됩니다. 다음은 BentoML 패키지용 API 서버를 시작하는 방법입니다.

bentoml serve service:svc

위의 명령어를 입력하면 추론 서버가 활성화 됩니다. 서버를 활성화 시킨 후에 http://localhost:3000에 접속하게 되면 아래와 같은 Swagger UI를 확인할 수 있습니다. 기본적으로 infra에 healthz, livez, readyz, metrics가 생성되고 app엔 Prediction Service Class에서 정의한 predict_ndarray가 보입니다.

image-20221224154436230

4. Bentos 만들기

image-20221224154400504

이미지 출처: towardsdatascience.com

Bento는 서비스를 실행하기 위해 필요한 모든 코드, 모델, 데이터 파일, requirements를 포함한 파일 저장소입니다. bentoml.Service를 사용하여 추론 API 정의를 표준화하고, 서비스 로직, runners 초기화, API 입력, 출력 타입 등을 포함합니다. Bento는 bentoml.Service를 생산 환경에서 실행하기 위해서 필요한 environment을 reproduce하는 방법을 표준화합니다.

간단하게 요약하면, Bento는 도시락이라는 표현에 걸맞게, 표준화된 형식으로 서비스를 패키징하고 배포하는데 도움을 줍니다.

Build

다음은 BentoML 패키지를 빌드하는 방법의 예시입니다. 패키지 빌드를 위해서 우선 yaml 파일을 정의해줘야합니다.

service: "service:svc"
description: "file: ./README.md"
labels:
  owner: pebpung
  stage: demo
include:
  - "*.py"
exclude:
  - "__pycache__"
python:
  packages:
    - scikit-learn
    - torch
    - Pillow

이후 build를 진행하게 되면 다음과 같은 log가 생성되며, 성공적으로 build가 됩니다.

$ bentoml build
Building BentoML service "pytorch_mnist_demo:46blisedkgqesycf" from build context "/home/tesser/kimin/WandB-Tutorial/4.bentoml".
Packing model "pytorch_mnist:gmkted4dgkuoyycf"
Locking PyPI package versions.

██████╗░███████╗███╗░░██╗████████╗░█████╗░███╗░░░███╗██╗░░░░░
██╔══██╗██╔════╝████╗░██║╚══██╔══╝██╔══██╗████╗░████║██║░░░░░
██████╦╝█████╗░░██╔██╗██║░░░██║░░░██║░░██║██╔████╔██║██║░░░░░
██╔══██╗██╔══╝░░██║╚████║░░░██║░░░██║░░██║██║╚██╔╝██║██║░░░░░
██████╦╝███████╗██║░╚███║░░░██║░░░╚█████╔╝██║░╚═╝░██║███████╗
╚═════╝░╚══════╝╚═╝░░╚══╝░░░╚═╝░░░░╚════╝░╚═╝░░░░░╚═╝╚══════╝

Successfully built Bento(tag="pytorch_mnist_demo:46blisedkgqesycf").

이렇게 하면 쉽게 공유하고 배포할 수 있는 pytorch_mnist_demo라는 이름의 BentoML 패키지가 생성됩니다. bentoml은 PyPI라는 pip 패키지 관리 Tool를 사용합니다. PyPI의 자세한 내용을 보고 싶다면 **Pipenv 사용법**을 보고 오시길 바랍니다.

5. Bentos 배포 방법

BentoML을 배포하는 세 가지 옵션은 다음과 같습니다.

  1. Docker: Bento를 컨테이너 이미지로 생성하여 사용자 정의 Docker 배포
  2. Yatai: Kubernetes에서 스케일링된 모델 배포
  3. bentoctl: AWS, GCP, Azure와 같은 클라우드 서비스에서도 빠른 모델 배포

Bento를 Docker 이미지로 컨테이너화하면 사용자가 쉽게 Bento를 build하고 배포할 수 있습니다. 서비스가 Bento로 구축되고 Bento 저장소에 저장되면, bentoml containerize CLI 명령으로 저장된 Bento를 컨테이너화할 수 있습니다.

Yatai는 Kubernetes에서 스케일링된 모델 서비스 작업량을 배포하는 데 도움을 줍니다. 이것은 Kubernetes에서 BentoML 배포를 표준화하고, 모든 ML 모델과 배포를 관리하는 UI와 API를 제공합니다. 또한, 고급 GitOps와 CI/CD 워크플로우를 지원합니다.

Yatai는 Kubernetes 기반이고, K8s 생태계의 다른 클라우드 네이티브 도구와 잘 연동됩니다. 시작하려면 Yatai 웹 UI에서 API 토큰을 얻고 bentoml CLI 명령에서 로그인하세요.

Containerize Bentos

다음은 프로덕션에서 BentoML API를 배포하는 것과 관련된 일반적인 단계입니다.

  1. 호스팅 플랫폼을 선택하고 지속적 배포 파이프라인을 설정합니다.
  2. **bentoml containerize** 명령을 사용하여 BentoML API용 Docker 이미지를 빌드합니다.

    bentoml containerize pytorch_mnist_demo:latest
    
  3. docker image가 잘 build 되었는지 확인합니다.

    $ docker images
    REPOSITORY                       TAG                       IMAGE ID       CREATED          SIZE
    pytorch_mnist_demo               46blisedkgqesycf          3c100fe722e6   28 seconds ago   4.03GB
    
  4. docker images를 실행합니다.

    docker run -p 3000:3000 pytorch_mnist_demo:46blisedkgqesycf
    

6. 마무리

이번 포스팅에서는 BentoML이란 어떤 것인지, 어떻게 모델을 패키징하고, 서비스 API를 구현하는지, 그리고 Bento를 어떻게 빌드하고 배포하는지에 대해서 살펴보았습니다.

BentoML은 일반적으로 머신러닝 모델을 서비스할 때 사용되는 툴입니다. 예를 들어, 이미지 인식 모델을 개발한 후, 이 모델을 웹 어플리케이션에서 쉽게 사용할 수 있도록 API 서버로 제공할 수 있습니다. 이렇게 모델을 서비스할 때 BentoML을 사용하면, 일반적으로 직접 서버를 구축할 필요 없이, 모델과 함께 필요한 소스 코드, 의존성 정보 등을 한 번에 패키징해서 배포할 수 있습니다. 만약 학습시킨 모델을 간단하게 배포할 수 있는 기회가 생겼다면, BentoML을 강력히 추천드립니다.

다음 글에서는, BentoML과 함께 Kubernetes를 사용할 수 있는 Yatai를 소개할 예정입니다. Yatai는 BentoML을 쉽게 스케일 업할 수 있는 유용한 도구입니다. 관심 있는 분들은 다음 글도 읽어보시는 걸 추천드립니다.