아키텍쳐 /머신러닝

구글 클라우드 Vertex.AI Model 학습 및 모델 배포&서빙

조대협 2021. 9. 8. 07:36

Vertex.AI Model 학습 및 모델 배포&서빙

조대협 (http://bcho.tistory.com)

 

머신러닝 환경에서, 학습을 수행하기 위해서는 프레임웍에 맞는 환경 (파이썬,텐서플로우)등을 설치하고, 필요한 컴퓨팅 리소스 (CPU,GPU)등을 프로비저닝 한후, 학습을 진행해야 한다. 학습이 완료되면 서빙을 위해서 모델을 export 하고, 서빙을 위한 API 서버를 설치 한 후에, 모델을 배포해서 서빙을 해야 한다. 

서빙시에는 학습시 데이터와 서빙 요창에 들어온 데이터가 크게 차이가 나지 않는지 (training & serving detection), 또는 서빙 요청이 들어온 데이터가 이전 서빙 요청이 들어온 데이터와 크게 차이가 나지 않는지 (data drift detection)등의 체크를 한 후에, 데이터의 분포가 크게 차이가 날 경우 알람을 보내서, 모델을 다시 학습하도록 해야 한다.

이러한 인프라를 일일이 구성하는 것은 많은 노력이 필요한데, 이러한 기능들을 매니지드 서비스 형태로 제공해주는 것이 Vertex AI의 Training, Model, Endpoint (서빙), Monitoring 이다.

 

이 예제에서는 Vertex.AI에서 모델을 학습하고, 학습된 모델을 Endpoint를 이용해서 서빙하는 방법에 대해서 알아보도록 한다. 이 예제는 구글 클라우드 코드랩 https://codelabs.developers.google.com/vertex_custom_training_prediction#5 을 참고 하였다.  상세한 스탭은 위의 링크를 참고하기 바란다. 

학습용 모델 및 컨테이너 빌드

먼저 Vertex.AI의 Notebook에서 Tensorflow Enpterise 2.3 을 이용해서 노트북을 생성한다.

Vertex.AI Training 에서, 모델을 학습하려면 모델 코드를 도커 컨테이너로 패키징 해서 업로드 해야 한다. 또한 학습이 완료된 모델을 Vertex.AI Model에 업로드 하기 위해서는 학습된 모델을 컨테이너에 저장하는 것이 아니라, 컨테이너 외부의 저장소를 사용해야 하는데, 코드상에서 학습된 모델을 GCS (Google Cloud Storage)에 저장하도록 해야 한다.

먼저 Dockerfile을 아래와 같이 준비한다.

FROM gcr.io/deeplearning-platform-release/tf2-cpu.2-3
WORKDIR /root

WORKDIR /

# Copies the trainer code to the docker image.
COPY trainer /trainer

# Sets up the entry point to invoke the trainer.
ENTRYPOINT ["python", "-m", "trainer.train"]

 

이 docker file은 /trainer 디렉토리에 모델 코드를 복사하고 컨테이너를 빌드하는 코드이다.

다음, 모델 코드를 구현해야 하는데, 모델 코드는 /trainer/train.py 파일로 저장한다. 

코드를 살펴보면 다음과 같다. 

 

import numpy as np
import pandas as pd
import pathlib
import tensorflow as tf

from tensorflow import keras
from tensorflow.keras import layers

print(tf.__version__)

## 데이터 전처리 부분
dataset_path = keras.utils.get_file("auto-mpg.data", "http://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data")
dataset_path
column_names = ['MPG','Cylinders','Displacement','Horsepower','Weight',
                'Acceleration', 'Model Year', 'Origin']
dataset = pd.read_csv(dataset_path, names=column_names,
                      na_values = "?", comment='\t',
                      sep=" ", skipinitialspace=True)

dataset.tail()
dataset.isna().sum()
dataset = dataset.dropna()

dataset['Origin'] = dataset['Origin'].map({1: 'USA', 2: 'Europe', 3: 'Japan'})
dataset = pd.get_dummies(dataset, prefix='', prefix_sep='')
dataset.tail()

### Split the data into train and test
train_dataset = dataset.sample(frac=0.8,random_state=0)
test_dataset = dataset.drop(train_dataset.index)

train_stats = train_dataset.describe()
train_stats.pop("MPG")
train_stats = train_stats.transpose()
train_stats

train_labels = train_dataset.pop('MPG')
test_labels = test_dataset.pop('MPG')

### Normalize the data
def norm(x):
  return (x - train_stats['mean']) / train_stats['std']
normed_train_data = norm(train_dataset)
normed_test_data = norm(test_dataset)

### 모델 정의


def build_model():
  model = keras.Sequential([
    layers.Dense(64, activation='relu', input_shape=[len(train_dataset.keys())]),
    layers.Dense(64, activation='relu'),
    layers.Dense(1)
  ])

  optimizer = tf.keras.optimizers.RMSprop(0.001)

  model.compile(loss='mse',
                optimizer=optimizer,
                metrics=['mae', 'mse'])
  return model

### 모델 학습

model = build_model()

EPOCHS = 1000

early_stop = keras.callbacks.EarlyStopping(monitor='val_loss', patience=10)
early_history = model.fit(normed_train_data, train_labels,
                    epochs=EPOCHS, validation_split = 0.2,
                    callbacks=[early_stop])


# 학습된 모델은 GCS에 export
model.save(BUCKET + '/mpg/model')

 

앞의 대부분은 모델 데이터 전처리 부분이고, 64x64X1  네트워크를 구성한다.

모델이 학습이 된후, 모델을 저장하는 코드가 model.save인데,

# 학습된 모델은 GCS에 export
model.save(BUCKET + '/mpg/model')

 

경로를 보면 GCS 버킷이 경로로 되어 있는 것을 볼 수 있다. 즉 모델 학습이 컨테이너에 의해서 완료가 되면 컨테이너는 사라지게 되는데, 모델이 컨테이너 안에 있다면 모델도 같이 사라지게 된다. 이를 막기 위해서 외부 GCS 리포지토리에 모델을 저장한다.

 

모델 코드가 완료되었으면, 컨테이너를 빌드한후에, 컨테이너 레지스트리로 푸쉬한다.

 

IMAGE_URI="gcr.io/$PROJECT_ID/mpg:v1" # 컨테이너 이름 지정
docker build ./ -t $IMAGE_URI # 컨테이너 빌드
docker push $IMAGE_URI #빌드된 컨테이너를 푸쉬

 

Vertex.AI Training을 이용한 모델 학습

이제, 이렇게 개발된 모델을 Vertex.AI의 Training에서 학습하도록 해본다. 

Vertex.AI > Training 메뉴로 가서 Create를 하여 새로운 Training Job을 등록한다.

이때 DataSet은 managed data set을 사용하는 것이 아니라, 코드내에서 자체적으로 데이터를 읽을 수 있도록 되어 있기 때문에, No managed dataset을 선택한다. 다음 아래 그림과 같이 Custom training을 선택한다. 이때 advanced option을 선택한다. 

다음 training container에서 Custom container를 선택하고, 앞서 빌드한 컨테이너의 경로를 “Container image”에 넣는다. 

그 다음 4 단계는  Continue로 스킵하고, 5단계 에서 컴퓨팅 리소스(학습용)를 선택할 수 있는데, 아래와 같이 n1-standard-4를 선택한다

 

다음 학습이 완료된 모델을 서빙 형태로 제공할 수 있도록 Prediction container 메뉴를 설정한다.

먼저 아래와 같이 베이스 컨테이너를 Pre-built container 로 선택하고, Tensorflow 2.5로 작성된 코드이기 때문에 아래 그림과 같이 Model framework version을 Tensorflow 2.5 로 선택한다. 

Prediction 컨테이너에 탑재할 서빙용 모델을 선택해야 하는데, 아래 “Model directory” 부분에 모델이 export 되서 저장되는 디렉토리를 선택한다. 앞의 모델 코드에서 model.save를 이용해서 지정한 GCS 버킷을 지정하면 된다. 



모든 설정이 끝나면, 학습일 시작한다.

학습이 진행되면 Training 메뉴로 가면, 아래 그림과 같이 학습 진행중인 모델의 상태를 볼 수 있다. 

학습이 끝난 모델을 배포하기 위해서, 서빙용 모델을 확인해보자. Vertex.AI에서 Models 메뉴로 가보면 아래 그림과 같이 학습이 끝난 모델을 등록되어 있음을 확인할 수 있다. 



 

학습이 완료된 모델에 대해서는 Models 메뉴에서 테스팅을 해볼 수 있는데, 해당 모델을 선택하면 아래 그림과 같이 Test your model 이라는 메뉴가 나오고, JSON 형태로 Input 데이터를 넣으면 결과를 화면에서 바로 확인이 가능하다. 

 

 

모델 배포

다음으로 학습된 모델을 Production에 배포해볼 수 있다. 

Model 메뉴로 들어간 후에, 아래 그림과 같이 Add to endpoint라는 메뉴를 선택하면 서빙 모델을 배포할 수 있다. 

 

모델을 배포할때는 새로운 Endpoint를 만들어서 배포할 수 도 있고, 아니면 기존에 배포되어 있는 Endpoint가 있는 경우, 기존 Endpoint에 새로운 모델을 배포하고 기존 모델과 새 모델 사이에 트래픽을 분할하여 A/B 테스팅을 진행할 수 있다. 

이 예제에서는 새로운 모델을 처음 배포하는 것이기 때문에, Create new endpoint를 선택한다. 



그리고 서빙용 머신 타입을 지정할 수 있는데, n1-standard-2를 선택하도록 한다.

다음에 모델을 배포하게 되면 인터넷을 통한 서빙이 가능하게 된다.

아래와 같이 Endpoints 메뉴에서 서빙용 Endpoints가 배포된것을 확인할 수 있다. 

다음 실제로 배포된 Endpoints를 이용해서 Serving request를 보내본다.

아래는 notebook 코드이다. 

Tensorflow serving을 직접호출하지 않고 Endpoints로 Wrapping된 SDK 를 사용한다. JSON 형식으로 request 를 보낼 수 있는 형태로 구현이 되어 있기 때문에, 손쉽게 serving request를 보낼 수 있다. 

!pip3 install google-cloud-aiplatform --upgrade --user



from google.cloud import aiplatform

endpoint = aiplatform.Endpoint(
    endpoint_name="projects/{프로젝트 넘버}/locations/us-central1/endpoints/{End point Id}"
)

test_mpg = [1.4838871833555929,
 1.8659883497083019,
 2.234620276849616,
 1.0187816540094903,
 -2.530890710602246,
 -1.6046416850441676,
 -0.4651483719733302,
 -0.4952254087173721,
 0.7746763768735953]

response = endpoint.predict([test_mpg])

print('API response: ', response)

print('Predicted MPG: ', response.predictions[0][0])

 

수행을 하고 나면, Predicted MPG 값으로 서빙 결과가 출력된다. 

 

지금까지 Vertex.AI에서 모델을 개발하고, 이를 managed 환경에서 학습한후에, 서빙 환경으로 배포하는 과정까지를 살펴보았다.