3-steps Guide for Model-Serving with VESSL

폴더 속에 잠자고 있는 model.pt를 꺼내고 싶은 당신에게..

Doeun
베슬에이아이 <VESSL AI>
20 min readJan 9, 2023

--

Table of Content

모델의 진정한 성과는 운영에서 나온다.
모델을 서빙하는 일반적인 방법
VESSL에서 MNIST 모델 서빙하기
준비물 & 결과물
0. Model Repository에 모델 생성
1. 모델 등록
2. 모델 서빙
3. 모델 추론 및 모니터링

VESSL GitHub를 방문하시면, 코드를 확인하실 수 있습니다.

모델의 진정한 성과는 운영에서 나온다.

모델의 진정한 성과는 모델을 운영을 통해 나온다고 볼 수 있습니다. 아무리 잘 학습된 모델이라도, 제대로 운영되지 못해 고객에게 제대로된 서비스를 제공할 수 없다면 무용지물이 되고 맙니다. 그러기 위해서 모델은 학습환경과 데이터 스토리지에서 벗어나, production 환경으로 전달되어야 하는데, 이를 “모델 서빙(Model Serving)”이라고 합니다. 현재, 모델 서빙은 Machine Learning/AI 업계에서 가장 주목받는 그리고 떠오르는 키워드 중에 한 가지입니다. 최근에는 비교적 많은 ML 엔지니어와 데이터 사이언티스트들이 서빙의 효율화를 위한 지식을 공유하고 있는데요. 그럼에도 불구하고, 여전히 많은 데이터 사이언티스트, ML 엔지니어들은 모델 학습에 집중한 나머지, 혹은 DevOps 영역에서의 어려움으로 인해 실시간 데이터 전달과 모델 서빙에 신경을 못쓰는 경우가 많습니다. (1)

(Medium에 “Model Serving”을 키워드로 검색한 이미지)

모델을 서빙하는 일반적인 방법

모델을 서빙하는데는 여러가지 방법들이 있습니다. 1) SW엔지니어링 팀에 맡긴다 2) Normal Application처럼 Web Framework를 이용해 API처럼 구성한다. 3) Kubernetes, Cloud Service등을 이용해 Machine Learning Application의 형태로 구성한다.(2) 각 방법들 모두 생각보다 많은 과정과 SW Engineering 지식이 필요하다보니, DevOps와 SW Engineering에 익숙하지 않은 데이터 사이언티스트와 ML엔지니어에겐 말처럼 쉬운 방법들이 아닙니다. 또 익숙하다고 하더라도, 매번 이를 준비하고 배포하는 일은 생각보다 더 번거로운 일입니다.

먼저, 엔지니어링 팀에 맡기게 되는 경우를 생각해보겠습니다. Data ETL부터 모델 서빙까지를 담당해주는 팀이 있다는 것은 분명 ML Research 팀에게는 매우 희소식입니다. 하지만, 데이터 사일로를 발생시키지 않고 유기적으로 소통해야 하는 일은 결코 쉬운 일이 아닙니다. 그리고, 이런 담당 조직이 준비되어 있지 않은 경우에는 결국, ML팀이 이를 전담해야하는 상황을 마주하게 됩니다.(2) (3)

두번째로 Web Framework으로 일반 API처럼 통신하는 방식을 고민해볼 수 있을 것입니다. 일반적으로 가장 많이 사용되는 서빙의 방식으로 실제로도 가장 많은 자료를 찾아볼 수 있을 것입니다.(4) (5) 하지만 매번 모델에 맞추어 새로운 서버를 구축하는 일은 굉장히 번거로울 뿐더러, SW Engineering에 익숙하지 않은 데이터 사이언티스트의 경우에는 또 다른 난관이 될 것입니다.

마지막으로 Kubernetes, Cloud 등의 Microservice 그리고 ML application에 특화된 서비스들을 이용해 구축하는 것이 있습니다. 이 방법은 시스템 안정성, 운영 지속성등의 관점에서 아마 가장 효과적으로 Data ETL과 Model의 실시간 서비스를 구축하는 방법일 수 있습니다. 하지만, 가장 어려운 방법이기도 합니다. Kubernetes와 Cloud 서비스, CI/CD 솔루션 등 수많은 러닝커브들이 존재합니다.(2) 이를 보다 쉽게 사용할 수 있게 하기 위해 VESSL AI를 포함한 수많은 MLOps 솔루션, Cloud 솔루션에서 Kubernetes를 보다 쉽게 사용할 수 있도록 많은 서비스를 제공하고 있습니다. (6) (7) (8)

VESSL에서 MNIST 모델 서빙하기

VESSL을 이용해 모델을 서빙하는 방법은 생각보다 매우 간단합니다. 1) 모델 등록, 2) 모델 서빙, 3) 모델 모니터링 이 3단계만 거친다면 여러분이 개발하신 모델을 실제 production 환경으로 옮겨 서비스할 수 있습니다. 그렇다면, MNIST 프로젝트의 모델 서빙을 함께 진행해보실까요?

준비물 & 결과물

[ 준비물 ]

  • mnist Dataset (in VESSL Datasets)
  • mnist-image-classification-example (in VESSL Projects)
  • register_model.py 코드
  • model.pt
  • 000000_num7.png (추론할 데이터)

[ 결과물 ]

  • vessl.manifest.yaml
  • vessl.runner.pkl
  • Serving return results

0. Model Repository에 모델 생성

이번에 저희가 서빙할 모델은 여러분의 VESSL Projects에서 보실 수 있는 mnist example 프로젝트에 있습니다.
(MNIST 모델과 코드는 VESSL Projects “mnist-Image-classification-example” 과 연결된 “vessl-ai/examples” Github Repository에서 확인할 수 있습니다.)

모델을 서빙하기 위해서는 먼저 Model Repository에 모델을 생성해야 합니다.

(https://vessl.ai/{UserName}/mnist-image-classification-example)

그러기 위해 먼저 Projecst에 들어가셔서 4번 실험(Experiments)을 선택해주세요. 이번에 우리가 서빙할 모델이 있는 실험입니다. 해당 모델을 클릭해서 Summary에 들어가보시면, 해당 모델에 대한 더 자세한 정보들을 확인할 수 있습니다.

(“mnist-image-classification-example” 프로젝트의 대시보드)
(4번 실험 Summary 페이지)

해당 페이지의 우측에 “Actions” 버튼을 클릭하고, 아래와 같이 “Create Model” 버튼을 클릭해주세요.

(“Create Model” 버튼 클릭)

그리고 가지고 계신 Model Repository를 선택해주세요.

(기보유한 Model Repository 선택, 없을 경우 생성필요)

그리고 아래 두 사진과 같이 모델 생성에 관한 정보를 입력하신 후, “Create” 버튼을 눌러 모델을 생성해주세요. 여기서 추천드릴 팁은 모델이 어떤 Python 버전으로 작성되었는지, Tags 기능으로 기록해놓는 것을 추천드립니다. 모델이 작성된 Python 버전과 추후에 등장할 “모델 등록” 코드가 실행될 Python 버전이 일치해야 하기 때문입니다. 저희가 사용할 mnist 4번 실험은 “Python 3.6” 버전으로 작성되었습니다.

(모델 생성 팝업)
(생성할 모델에 관한 Source 정보 확인)

아래 사진과 같이 모델이 생성된 것을 보실 수 있습니다.

(Model Repository에 생성된 모델)

1. 모델 등록

다음으로 모델을 서빙하기 위해서는 Model Repository에 보관되어 있는 모델을 등록해야합니다. 여기서 “등록”이란 모델을 어떻게 Serving 서버에 셋업(set-up)할 것인지, 요청과 응답을 어떻게 처리할 것인지를 지정해놓는 일을 이야기합니다.

  1. 먼저 MNIST 모델을 만들 때 사용되었던 CNN Network를 한번 더 정의해주어야 합니다.
    아래의 RunnerBase에 작성될 predict() 함수 즉, 추론 함수에 해당 Network가 호출되기 때문입니다. CNN Network는 아래와 같이 작성해주시면 됩니다.
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv = nn.Conv2d(1, 32, 3, 1)
self.relu = nn.ReLU()
self.pool = nn.MaxPool2d(2)
self.drop1 = nn.Dropout2d(0.25)
self.drop2 = nn.Dropout2d(0.5)
self.fc1 = nn.Linear(5408, 128)
self.fc2 = nn.Linear(128, 10)
self.softmax = nn.LogSoftmax(1)

def forward(self, x):
x = self.relu(self.conv(x))
x = self.pool(x)
x = self.drop1(x)
x = torch.flatten(x, 1)
x = self.fc1(x)
x = self.relu(x)
x = self.drop2(x)
x = self.fc2(x)
return self.softmax(x)

2. 다음으로는 모델 서빙에 필요한 정보들을 vessl.RunnerBase SDK를 활용해 정의해주기 위해 “register_model.py”파일을 작성합니다.
RunnerBase는 서빙 모델 등록 작업을 하는데 필요한 Base Class 입니다. 해당 클래스는 아래코드처럼

i) 모델을 로드하는 법(load_model)
ii) 데이터를 전처리 하는 법(preprocess_data)
iii) 데이터를 추론하는 법 (predict)
iv)예측한 결과 값을 어떻게 API로 값을 전달할 지(postprocess_data)

까지 정의하여 선언해주는 클래스입니다.
마지막으로 model에 어떻게 서빙할지를 vessl.register_model 함수를 활용해아래 코드와 같이 정의해주세요.

class MyRunner(vessl.RunnerBase):
@staticmethod
def load_model(props, artifacts):
model = Net()
model.load_state_dict(torch.load("model.pt"))
model.eval()
return model

@staticmethod
def preprocess_data(data):
image = Image.open(BytesIO(img_data))
pix = np.array(image)
pix = pix/255.0
data = pix.reshape(-1, 28, 28)
infer_torch_data = torch.utils.data.TensorDataset(torch.from_numpy(data).unsqueeze(1))
infer_dataloader = torch.utils.data.DataLoader(infer_torch_data, batch_size=128, shuffle=False)
for data in infer_dataloader:
infer_data = data[0].float()
return infer_data

@staticmethod
def predict(model, data):
with torch.no_grad():
return model(data).argmax(dim=1, keepdim=True)

@staticmethod
def postprocess_data(data):
return {"result": data.item()}

vessl.configure()
vessl.register_model(
repository_name="my-model",
model_number=20,
runner_cls=MyRunner,
requirements=["torch"],
)

1) load_model(props, artifacts)

Model Repository에 보관되어 있고, 서빙을 위해 서버로 옮겨올 모델 파일은 아직 서빙을 위해 실제 서버에 배포된 상태가 아닙니다. 실제 모델이 데이터 추론을 진행하기 위해서 모델을 서버에 배포해주는 함수입니다. 코드는 아래와 같습니다.

@staticmethod
def load_model(props, artifacts):
model = Net()
model.load_state_dict(torch.load("model.pt"))
model.eval()
return model

2) preprocess_data(data)

데이터 추론을 위해 클라이언트에서 요청되는 데이터를 실시간으로 전처리 해주기 위해 필요한 함수입니다. mnist 이미지 파일을 전달하고, 모델이 예측할 수 있도록 변경해주는 코드를 아래와 같이 작성해보았습니다.

@staticmethod
def preprocess_data(data):
image = Image.open(BytesIO(img_data))
pix = np.array(image)
pix = pix/255.0
data = pix.reshape(-1, 28, 28)
infer_torch_data = torch.utils.data.TensorDataset(torch.from_numpy(data).unsqueeze(1))
infer_dataloader = torch.utils.data.DataLoader(infer_torch_data, batch_size=128, shuffle=False)
for data in infer_dataloader:
infer_data = data[0].float()
return infer_data

3) predict(model, data)

모델이 실시간으로 들어온 요청 데이터를 추론하기 위해 호출되는 함수입니다. 실제 추론을 수행하는 함수라고 생각하시면 됩니다.

    @staticmethod
def predict(model, data):
with torch.no_grad():
return model(data).argmax(dim=1, keepdim=True)

4) postprocess_data(data)

추론이 완료된 데이터를 다시 클라이언트가 이해할 수 있는 데이터 형태로 후처리 해주는 함수입니다. 이번 글에서는 json 형태의 방식으로 전달되게 작성했습니다.

    @staticmethod
def postprocess_data(data):
return {"result": data.item()}

3. vessl.configure(), vessl.register_model()을 작성하여 모델 등록 절차를 마무리해주세요. 각 파라미터에 대한 설명은 아래와 같습니다.

vessl.configure()
vessl.register_model(
repository_name="my-model",
model_number=20,
runner_cls=MyRunner,
requirements=["torch"],
)

5) vessl.configure()

vessl 유저 인증 및 configure 확인을 위해 꼭 호출되어야 합니다.

6) vessl.register_model()

마지막으로 vessl.register_model 함수를 호출해 모델을 등록합니다. 저희가 사용한 “my-model”을 모델 리포지토리(model-repository)로, 이 모델이 등록될 숫자(20)을 모델 넘버(model-number)로, 위에 열심히 작성한 “MyRunner”를 러너 클래스(runner_cls)로 작성해주세요. 마지막으로 이 모델이 로드, 사용되기에 꼭 필요한 패키지들을 requirements에 작성해주세요.

4. 그리고 위와 같이 작성한 파일을 실행하여 모델을 등록해주세요. 그러면 여러분의 Model 페이지에 아래와 같이 “Serving” 탭에 보시면 모델이 서빙할 수 있도록 등록되어 있고, “Deploy” 버튼이 활성화 되어 있는 것을 확인하실 수 있습니다.

(“Models > Serving” Tab에서 “Deploy” 버튼이 활성화된 경우, Serving할 준비가 되었습니다.)

2. 모델 서빙

위의 준비가 모두 끝났다면 모델을 서빙할 준비가 모두 완료되었습니다. 본격적으로 서빙을 진행해보기 전에, 해당 모델이 서빙을 위한 준비를 모두 잘 마쳤는지 확인해보도록 하겠습니다.

  1. 해당 모델을 클릭해서 “Files” 탭을 클릭해보시면, 아래와 같은 “vessl.manifest.yaml” 과 “vessl.runner.pkl” 파일들이 보이신다면, 모델이 VESSL에서 서빙되기 위한 준비를 잘 마쳤다는 것을 의미합니다.
(vessl.manifest.yaml 과 vessl.runner.pkl 파일 이미지)

vessl.manifest.yaml

artifacts: {}
model:
save_output: null
save_path: null
save_type: user-defined
requirements:
- torch
runner:
save_path: vessl.runner.pkl
save_type: cloudpickle
use_default: []
version: v1
  • artifacts: load_model() 정의시에 사용되었던 추가 artifacts의 목록입니다. register_model()함수의 인자로 정의해주셨다면, 여기에서 해당 파일이름을 확인하실 수 있습니다.
  • model : model에 대한 정보들을 확인하실 수 있습니다. 이중에서 주의깊게 확인해보셔야 하는 부분은 “save_type”입니다.
    “save_type”은 load_model 함수 정의 여부에 따라 다르게 표시됩니다. “user-defined”인 경우, 유저가 모델 로드 방식을 정의했음을 의미하고, “cloudpickle”인 경우에는 자동으로 로드 방식이 정의되어있음을 의미합니다.
  • requirements: register_model() 작성할 때, 작성했던 파이썬 requirements 패키지들이 나열되어 있을 겁니다.
  • runner: 모델 등록 시 작성한 RunnerBase에 대한 정보를 담고 있는 vessl.runner.pkl의 경로와 type에 대한 정보를 의미합니다.

2. 그럼 서빙을 위해 그 옆의 “Serving” 탭을 클릭해주세요.

(Serving 탭 이미지)

3. 모델이 정상적으로 서빙의 준비를 마쳤다면 우측의 “Deploy” 버튼을 클릭해주세요.

(활성화된 “Deploy” 버튼)

4. “Deploy” 버튼을 클릭하시면, 아래와 같은 팝업창을 마주하게 되실 겁니다. 여러분의 모델이 배포될 환경을 결정하게 되는 과정입니다. 원하시는 Cluster 와 Resource를 선택해주시면 됩니다. 하지만, 여기서 Image를 선택하실 때는 주의하실 점이 있습니다. 반드시, 위의 모델 등록 코드를 작성할 때 사용했던 Python의 버전과 같은 버전의 Python 이미지를 선택해주셔야 합니다. 이번 mnist 모델은 코드 작성과 생성 모두 위에서 밝힌 것과 같이 python 3.6 버전으로 작성되었습니다. 따라서, “Python 3.6 (All Packages)”를 선택하시면 됩니다.

5. 모두 선택 후, “Deploy” 버튼을 클릭해주시면, 아래와 같은 화면을 만나게 되실겁니다.

짜잔! 서빙이 완료되었습니다. 이제 모델을 모니터링하면서 실제로 모델이 추론하는 모습을 확인해보세요!

(Serving Summary 페이지)

3. 모델 추론 및 모니터링

모델이 배포된 뒤, 같은 웹 페이지 화면 하단에서 모델에 대한 Log를 확인할 수 있을 뿐 아니라, 모델이 배포된 환경의 시스템 매트릭까지 모두 확인할 수 있습니다. 페이지에 제공되는 Example Request로 모델에 HTTP Request를 날려서 prediction을 받아보세요. 아래와 같은 POST curl 커맨드만으로 모델이 결과값을 잘 내고 있는지 확인할 수 있습니다.

(서빙 서버 Log와 Example Request 페이지)

Example Request

Service Status

  • Pending : VESSL 클러스터 환경에 서빙 서버 컨테이너를 배포하기 위한 준비를 진행하는 단계입니다.
  • Initializing : 컨테이너가 배포되고, 서빙 서버가 모델을 로드하고 준비하는 단계입니다.
  • Running : 서빙 서버가 동작을 시작하여 추론을 진행할 수 있는 단계입니다.
  • Failed : 서버 배포 혹은 모델 로드에 실패한 상태입니다.

Authentication Token — 유저 인증을 위한 토큰으로 서빙 모델 별로 달라집니다.
Service Endpoint — 모델 웹 서비스를 위한 진입점의 URL입니다.

저희가 서빙한 MNIST 모델의 curl 커맨드에 추론 데이터 경로를 적어넣으면, 아래와 같은 커맨드가 완성되게 됩니다. 추론 데이터가 있는 환경에서 아래 커맨드를 모델에 요청해보세요. 여러분의 모델은 어떤 예측을 내놓았나요?

curl -X POST -H "X-AUTH-KEY:[MY-AUTHENTICATION-TOKEN]" --data-binary @[MNIST IMAGE] [MY-SERVING-ENDPOINT]
(000000-num7.png curl 커맨드 실행 결과)

Try VESSL Model Serving

위의 모델 서빙(Model Serving)에 대한 자세한 코드가 궁금하시다면, VESSL GitHub에 방문하시면, 코드를 확인하실 수 있습니다.

복잡하기만 해보이는 모델 서빙(Model Serving), VESSL과 함께 간단하고 보다 쉽게 도전해보세요!

VESSL은 머신러닝 팀에서 모델을 더 빠르게 구축, 교육 및 배포할 수 있도록 지원하는 end-to-end MLOps 플랫폼입니다. 궁금한 점은 언제든지 growth@vessl.aiVESSL AI Community로 연락주세요!

Reference

  1. Why 90 percent of all machine learning models never make it into production
  2. Several Ways for Machine Learning Model Serving (Model as a Service)
  3. 데이터 시대에 MLOps가 필요한 이유
  4. Deploying Machine Learning Models, Part 2: model serving
  5. Quick ML model serving with Java
  6. ML Ops:Kubernetes as Service
  7. Microsoft Azure Platform
  8. Google Cloud Platform

Doeun, Growth at VESSL AI

--

--