분산 하이퍼파라미터 튜닝

KerasTuner로 분산 하이퍼파라미터 튜닝

저자 : Tom O’Malley, Haifeng Jin
생성일 : 2019/10/24
최종 편집일 : 2021/06/02
설명 : 모델의 하이퍼파라미터를 다중 GPU 및 다중 머신에서 튜닝하기.

!pip install keras-tuner -q

소개

KerasTuner는 분산된 하이퍼파라미터 검색을 쉽게 수행할 수 있도록 해줍니다. 코드를 변경하지 않고도 로컬에서 단일 스레드로 실행하는 것에서, 수십 또는 수백 개의 작업자(worker)에서 병렬로 실행하는 것으로 확장할 수 있습니다. 분산된 KerasTuner는 chief-worker 모델을 사용합니다. Chief는 서비스 역할을 하며, worker들은 결과를 보고하고 다음으로 시도할 하이퍼파라미터를 요청합니다. Chief는 단일 스레드 CPU 인스턴스에서 실행해야 하며, 대안적으로는 worker 중 하나에서 별도의 프로세스로 실행할 수 있습니다.

분산 모드 구성

KerasTuner의 분산 모드를 구성하려면, 세 가지 환경 변수를 설정하기만 하면 됩니다:

  • KERASTUNER_TUNER_ID:

    • Chief 프로세스에는 “chief"로 설정해야 합니다.
    • 다른 worker에는 고유한 ID가 부여되어야 합니다. (관례적으로 “tuner0”, “tuner1” 등)
  • KERASTUNER_ORACLE_IP:

    • Chief 서비스가 실행될 IP 주소 또는 호스트명을 설정합니다.
    • 모든 worker는 이 주소를 resolve하고 접근할 수 있어야 합니다.
  • KERASTUNER_ORACLE_PORT:

    • Chief 서비스가 실행될 포트를 설정합니다.
    • 이 포트는 다른 worker들이 접근할 수 있는 포트여야 하며, 자유롭게 선택할 수 있습니다.
    • 인스턴스들은 gRPC 프로토콜을 통해 통신합니다.

모든 worker에서 동일한 코드를 실행할 수 있습니다. 분산 모드와 관련된 추가 고려 사항은 다음과 같습니다:

  • 모든 worker는 결과를 기록할 수 있는 중앙화된 파일 시스템에 접근할 수 있어야 합니다.
  • 모든 worker는 튜닝에 필요한 트레이닝 및 검증 데이터를 사용할 수 있어야 합니다.
  • 내결함성을 지원하기 위해, overwriteTuner.__init__에서 False로 유지해야 합니다. (기본값은 False입니다)

Chief 서비스에 대한 예시 bash 스크립트 (run_tuning.py의 샘플 코드 하단 참고):

export KERASTUNER_TUNER_ID="chief"
export KERASTUNER_ORACLE_IP="127.0.0.1"
export KERASTUNER_ORACLE_PORT="8000"
python run_tuning.py

worker들에 대한 예시 bash 스크립트:

export KERASTUNER_TUNER_ID="tuner0"
export KERASTUNER_ORACLE_IP="127.0.0.1"
export KERASTUNER_ORACLE_PORT="8000"
python run_tuning.py

tf.distribute로 데이터 병렬 처리

KerasTuner는 tf.distribute를 통해, 데이터 병렬 처리도 지원합니다. 데이터 병렬 처리와 분산 튜닝을 결합할 수 있습니다. 예를 들어, 4개의 GPU가 있는 10개의 worker가 있을 때, 각 worker가 4개의 GPU에서 학습하는 10개의 병렬 실험을 실행할 수 있으며, 이때 tf.distribute.MirroredStrategy를 사용할 수 있습니다. 또한 tf.distribute.TPUStrategy를 사용하여, 각 실험을 TPU에서 실행할 수 있습니다. 현재 tf.distribute.MultiWorkerMirroredStrategy는 지원되지 않지만, 향후 지원할 예정입니다.

예제 코드

아래 환경 변수가 설정된 상태에서, 아래 예시는 분산 튜닝을 실행하고, 각 실험에서 tf.distribute를 사용하여, 데이터 병렬 처리를 진행합니다. 이 예시는 tensorflow_datasets에서 MNIST를 로드하고, Hyperband를 사용하여 하이퍼파라미터 검색을 수행합니다.

import keras
import keras_tuner
import tensorflow as tf
import numpy as np


def build_model(hp):
    """컨볼루션 모델을 빌드합니다."""
    inputs = keras.Input(shape=(28, 28, 1))
    x = inputs
    for i in range(hp.Int("conv_layers", 1, 3, default=3)):
        x = keras.layers.Conv2D(
            filters=hp.Int("filters_" + str(i), 4, 32, step=4, default=8),
            kernel_size=hp.Int("kernel_size_" + str(i), 3, 5),
            activation="relu",
            padding="same",
        )(x)

        if hp.Choice("pooling" + str(i), ["max", "avg"]) == "max":
            x = keras.layers.MaxPooling2D()(x)
        else:
            x = keras.layers.AveragePooling2D()(x)

        x = keras.layers.BatchNormalization()(x)
        x = keras.layers.ReLU()(x)

    if hp.Choice("global_pooling", ["max", "avg"]) == "max":
        x = keras.layers.GlobalMaxPooling2D()(x)
    else:
        x = keras.layers.GlobalAveragePooling2D()(x)
    outputs = keras.layers.Dense(10, activation="softmax")(x)

    model = keras.Model(inputs, outputs)

    optimizer = hp.Choice("optimizer", ["adam", "sgd"])
    model.compile(
        optimizer, loss="sparse_categorical_crossentropy", metrics=["accuracy"]
    )
    return model


tuner = keras_tuner.Hyperband(
    hypermodel=build_model,
    objective="val_accuracy",
    max_epochs=2,
    factor=3,
    hyperband_iterations=1,
    distribution_strategy=tf.distribute.MirroredStrategy(),
    directory="results_dir",
    project_name="mnist",
    overwrite=True,
)

(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()

# 이미지의 채널 차원을 가지도록, 이미지를 reshape 합니다.
x_train = (x_train.reshape(x_train.shape + (1,)) / 255.0)[:1000]
y_train = y_train.astype(np.int64)[:1000]
x_test = (x_test.reshape(x_test.shape + (1,)) / 255.0)[:100]
y_test = y_test.astype(np.int64)[:100]

tuner.search(
    x_train,
    y_train,
    steps_per_epoch=600,
    validation_data=(x_test, y_test),
    validation_steps=100,
    callbacks=[keras.callbacks.EarlyStopping("val_accuracy")],
)
결과
Trial 2 Complete [00h 00m 18s]
val_accuracy: 0.07000000029802322
Best val_accuracy So Far: 0.07000000029802322
Total elapsed time: 00h 00m 26s