아키텍쳐 /머신러닝

Vertex AI : 모델 학습 및 하이퍼 패러미터 튜닝

Terry Cho 2021. 9. 10. 08:34

Vertex AI : 모델 학습 및 하이퍼 패러미터 튜닝

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

 

가장 기본적이지만 클라우드를 사용하면서 가장 효과적인 기능이 모델 학습과 하이퍼 패러미터 튜닝이다.

모델 학습을 위해서는 CPU/GPU 고사양의 컴퓨터가 필요하지만, 이 고사양의 컴퓨팅 파워가 항상 이용되는 것이 아니라. 학습때 많은 컴퓨팅 자원이 필요하기 때문에, 온프렘등에서 장비를 사놓고 학습때만 사용하고 평소에 장비를 사용하지 않는 것 보다는 학습때만 클라우드에서 컴퓨팅에서 컴퓨팅 자원을 사용하는 것이 오히려 비용 효율적이라고 볼 수 있다.

하이퍼 패러미터

모델을 학습함에 있어서 모델에는 여러가지 튜닝이 가능한 패러미터가 있다. 예를 들어 학습 속도 (Learning Rate)나, 또는 뉴럴네트워크에서 네트워크의 모양 (깊이나 폭)등을 변경해가면서 최적의 학습속도나 학습 정확도를 구할 수 있는데, 이는 반복적인 실험으로 최적의 값을 찾아내야 한다.

이를 위해서 패러미터를 바꿔가면서 반복적인 학습이 필요한데(이렇게 변경되는 패러미터를 하이퍼 패러미터라고 한다), 이는 기계적으로 반복해야 하는 작업이다.

또한 온프렘과 같은 환경에서 이런 반복적인 실험을 계속하려면 컴퓨팅 자원이 한정이 되어 있기 때문에, 실험을 동시에 여러개를 수행하는 병렬적인 처리가 어렵고 하나의 실험이 끝나면 패러미터를 변경후에 다음 실험을 하는 순차적으로 실험을 진행해야 하기 때문에 시간이 많이 소요된다.

 

그러나 이를 클라우드를 사용하게 되면, 자원을 탄력적으로 사용할 수 있기 때문에 동시에 여러개의 실험을 실행하여 시간을 줄일 수 있고, 하이퍼패러미터를 찾는 작업을 자동화 함으로써 실험에 소요되는 반복적인 기계 작업을 없앨 수 있다. 

예제

이번글은 Vertex.AI 플랫폼의 Train (학습) 플랫폼을 이용하여, 매니지드 서비스를 이용해서 모델을 학습하고, 하이퍼 패러미터를 튜닝하는 방법에 대해서 알아본다. 모델은 간단한 딥러닝 모델이고 텐서플로우로 구현되어 있다. 

 

이 시나리오에서는 학습속도(Learning Rate)와, SGD의 모멘텀 값과 뉴럴네트워크의 마지막 네트워크의 크기를 하이퍼 패러미터로 정의해서 가장 높은 정확도를 제공하는 패러미터를 찾도록 한다. 

 

이 글에서 사용하는 예제는 구글 코드랩 예제 ​​https://codelabs.developers.google.com/vertex_hyperparameter_tuning#0 를 사용하였다. 

모델 코드

이 코드는 사진에서 사람인지 말인지를 구별하는 모델로, 텐서플로우 데이터셋의 말과 사람 (horse or human) 데이터 셋을 사용하였다.

다음 데이터를 읽어서 CNN (Convolutional Neural Network)에 학습을 시키는 코드이다. 

 

import tensorflow as tf
import tensorflow_datasets as tfds
import argparse
import hypertune

NUM_EPOCHS = 10


def get_args():
  '''Parses args. Must include all hyperparameters you want to tune.'''

  parser = argparse.ArgumentParser()
  parser.add_argument(
      '--learning_rate',
      required=True,
      type=float,
      help='learning rate')
  parser.add_argument(
      '--momentum',
      required=True,
      type=float,
      help='SGD momentum value')
  parser.add_argument(
      '--num_neurons',
      required=True,
      type=int,
      help='number of units in last hidden layer')
  args = parser.parse_args()
  return args


def preprocess_data(image, label):
  '''Resizes and scales images.'''

  image = tf.image.resize(image, (150,150))
  return tf.cast(image, tf.float32) / 255., label


def create_dataset():
  '''Loads Horses Or Humans dataset and preprocesses data.'''

  data, info = tfds.load(name='horses_or_humans', as_supervised=True, with_info=True)

  # Create train dataset
  train_data = data['train'].map(preprocess_data)
  train_data  = train_data.shuffle(1000)
  train_data  = train_data.batch(64)

  # Create validation dataset
  validation_data = data['test'].map(preprocess_data)
  validation_data  = validation_data.batch(64)

  return train_data, validation_data


# 모델 코드
def create_model(num_neurons, learning_rate, momentum):
  '''Defines and complies model.'''

  inputs = tf.keras.Input(shape=(150, 150, 3))
  x = tf.keras.layers.Conv2D(16, (3, 3), activation='relu')(inputs)
  x = tf.keras.layers.MaxPooling2D((2, 2))(x)
  x = tf.keras.layers.Conv2D(32, (3, 3), activation='relu')(x)
  x = tf.keras.layers.MaxPooling2D((2, 2))(x)
  x = tf.keras.layers.Conv2D(64, (3, 3), activation='relu')(x)
  x = tf.keras.layers.MaxPooling2D((2, 2))(x)
  x = tf.keras.layers.Flatten()(x)
  x = tf.keras.layers.Dense(num_neurons, activation='relu')(x)
  outputs = tf.keras.layers.Dense(1, activation='sigmoid')(x)
  model = tf.keras.Model(inputs, outputs)
  model.compile(
      loss='binary_crossentropy',
      optimizer=tf.keras.optimizers.SGD(learning_rate=learning_rate, momentum=momentum),
      metrics=['accuracy'])
  return model


def main():
  args = get_args()
  train_data, validation_data = create_dataset()
  model = create_model(args.num_neurons, args.learning_rate, args.momentum)
  history = model.fit(train_data, epochs=NUM_EPOCHS, validation_data=validation_data)

  # DEFINE METRIC
  hp_metric = history.history['val_accuracy'][-1]

  hpt = hypertune.HyperTune()
  hpt.report_hyperparameter_tuning_metric(
      hyperparameter_metric_tag='accuracy',
      metric_value=hp_metric,
      global_step=NUM_EPOCHS)


if __name__ == "__main__":
    main()

 

코드에 하이 라이트된 부분은 하이퍼 패러미터이다.

  • Learning Rate : 학습 속도
  • Num_neurons : 뉴럴 네트워크 맨 마지막 부분의 네트워크의 뉴런의 수 (네트워크의 크기)
  • Momentum : SGD에서 관성 값

도커 패키징

코드가 완성되면 위의 코드를 ./trainer/task.py 파이썬 파일로 저장한다. Vertex.AI Train에서는 학습을 위해서는 이 코드를 도커로 패키징해서 업로드해야 한다. 도커 패키징을 위해서 Dockerfile을 아래와 같이 만든다. 

 

FROM gcr.io/deeplearning-platform-release/tf2-gpu.2-5

WORKDIR /

# Installs hypertune library
RUN pip install cloudml-hypertune

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

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

 

도커 이미지는 gcr.io/deeplearning-platform-release/tf2-gpu.2-5 를 베이스이미지로 하고, 하이퍼튠 기능을 사용할 예정이기 때문에, google cloud vertex ai의  hypertune 라이브러리를 설치한다. “RUN pip install cloudml-hypertune”

다음 docker build 명령어를 이용해서 컨테이너를 빌드하고 레지스트리에 등록한다.

%docker build ./ -t gcr.io/{프로젝트ID}/horse-human:hypertune

학습 & 패러미터 튜닝

다음 컨테이너를 Vertex.AI Training에 올려서 학습을 진행한다.

Vertex.AI Training 메뉴로 들어가서 Create를 선택한다.

데이터셋을 선택하는 메뉴가 나오는데, Vertex.AI의 DataSet (데이터 저장소)에 데이터를 저장해놓고 이 데이터를 자동으로 모델에 피딩(공급)하도록할 수 있지만, 이 예제코드에서는 학습 데이터를 Tensorflow DataSet에서 읽어오도록 되어 있기 때문에 별도로 데이터를 공급할 필요가 없다. 그래서 아래 그림과 같이 “No managed dataset”을 선택한다. 다음 학습 타입은  customer training을 선택한다. 

여기서 필요한 컨테이너 이미지 경로에는 앞에서 빌드한 컨테이너 경로 “gcr.io/{프로젝트ID}/horse-human:hypertune”를 입력한다. 

 

다음으로 하이퍼 패러미터 튜닝 설정을 해야한다. 아래와 같이 Enable hyperparameter tuning 을 선택해서 하이퍼패러미터 튜닝을 활성화 한다. 

 

우리는 3개의 하이퍼 패러미터에 대한 자동 튜닝을 설정할것인데, 먼저  learning rate를 New Hyperparameter에 입력한다. 

이 변수의 타입은 double 형이 되기 때문에, 아래와 같이 learning_rate 의 변수 타입을 설정한다. 

그리고 값의 범위를 0.01~1 사이로 설정해서 이 범위내에서 최적값을 찾도록 한다. 

매번 실험마다 Min,Max 값내에서 값을 바꿔가도록 설정하는데, Log 함수에 따라서 값을 변경하도록 한다. 작은 값에서는 크게 변화를 주고 큰 값에서는 천천히 변화를 주도록 한다. 

 

<그림. Log 함수의 값 변화>

 

같은 방식으로 momentum 은 0~1 사이의 Double로 그리고 Scaling은 linear 방식으로 설정하고, 네트워크의 노드 수 num_nuerons는 비연속 값인 Discrete로 설정하고 [64,128,512]으로 범위를 설정한다. 

 

다음 하이퍼 패러미터 튜닝의 기준을 정의해야 하는데, accuracy가 최대가 되도록 패러미터를 튜닝하도록 다음과 같이 설정한다. 

 

다음에 몇번을 실험을 통해서 튜닝을 할것인지, 한번에 몇개의 인스턴스를 사용할것인지(몇개의 실험을 동시에 진행할것인지)를 정한다. 

동시에 여러개의 모델을 실험하면 시간은 빨라지겠지만, 앞의 실험 결과를 이용해서 튜닝을 하는 것이기 때문에 너무 많은 수로 동시에 실험을 진행하게 되면, 최적의 값을 찾기가 어렵기 때문에, 15번 실험에 동시 실험수는 3개로 아래와 같이 정의한다. 

그리고, 각 실험의 결과를 비교해서 어떻게 최적값을 찾아낼지를 정해야 하는데, Grid Search, Random search 방법이 있는데, 여기서는 Default 알고리즘을 선택한다. 

마지막으로 트레이닝을 수행한 VM 인스턴스의 크기를 정한다. 여기서는 n1-standard-4에 T4 GPU를 추가하여 설정한다. 

결과

학습이 끝나면 Training 메뉴에서 “Hyperparameter tuning jobs” 탭을 보면, Job이 하나 생성된것을 볼 수 있다.  

 

이 Job을 클릭하면 아래와 같이 15번의 실험 결과를 볼 수 있고, accuray 결과 값과 함께, 하이퍼패러미터의 값을 확인할 수 있다. 

13번째 실험이 정확도 0.902로, 이때 learning_rate는 1.050.. , momentuem 은 없고, 뉴럴 네트워크의 크기는 64일때 가장 좋은 효율은 낸것으로 확인할 수 있다. 

 

그리드형

'아키텍쳐 > 머신러닝' 카테고리의 다른 글

L2 Regularization  (0) 2021.09.29
Feature Crossing  (0) 2021.09.29
구글 클라우드 Vertex.AI Model 학습 및 모델 배포&서빙  (0) 2021.09.08
피쳐 크로싱 (Feature crossing)  (1) 2019.05.21
Deep learning VM  (2) 2018.12.05