Keras Tuner

keras에서 하이퍼파라미터를 튜닝할 때는 keras tuner를 사용하는 것이 좋다.

Keras tuner를 사용할 때는 먼저 다음과 같이 keras 모델을 구축, 컴파일하여 변환하는 함수를 작성한다.

import keras_tuner as kt

def build_model(hp):
    n_hidden = hp.Int("n_hidden", min_value=0, max_value=8, default=2)
    n_neurons = hp.Int("n_neurons", min_value=16, max_value=256)
    learning_rate = hp.Float("learning_rate", min_value=1e-4, max_value=1e-2,
                             sampling="log")
    optimizer = hp.Choice("optimizer", values=["sgd", "adam"])
    if optimizer == "sgd":
        optimizer = tf.keras.optimizers.SGD(learning_rate=learning_rate)
    else:
        optimizer = tf.keras.optimizers.Adam(learning_rate=learning_rate)

    model = tf.keras.Sequential()
    model.add(tf.keras.layers.Flatten())
    for _ in range(n_hidden):
        model.add(tf.keras.layers.Dense(n_neurons, activation="relu"))
    model.add(tf.keras.layers.Dense(10, activation="softmax"))
    model.compile(loss="sparse_categorical_crossentropy", optimizer=optimizer,
                  metrics=["accuracy"])
    return model

위 함수는 은닉 층 수(n_hidden), 은닉 층의 뉴런 수(n_neurons), 학습률(learning_rate), optimizer(optimizer) 등을 하이퍼파라미터로 정의한다. hp.Int("n_hidden", min_value=0, max_value=8, default=2) 는 다음과 같은 의미를 가진다.

  • n_hidden이라는 하이퍼파라미터가 hp객체 안에 있는지 확인. 있으면 그 값 반환.
  • 없으면 가능 범위가 0~8인 n_hidden이라는 새로운 정수 하이퍼파라미터를 등록하고 기본 값(default) 2 반환(기본 값이 없다면 min_value 반환).


랜덤 서치

기본적인 랜덤서치를 수행하려면 kt.RandomSearch 튜너를 만들고 search() 메서드를 호출한다.

random_search_tuner = kt.RandomSearch(
    build_model, objective='val_accuracy', max_trials=5, overwrite=True,
    directory='my_fashion_mnist', project_name='my_rnd_search', seed=42
)
random_search_tuner.search(X_train, y_train, epochs=10, validation_data=(X_valid, y_valid))

위 코드는 다음과 같은 순서로 작동한다.

  1. 모든 하이퍼파라미터 사양을 수집하기 위해 빈 Hyperparameters 객체로 build_model() 한 번 호출.
  2. 아래 과정 5번 시도(max_trials=5).
    1. 범위 내에서 랜덤하게 샘플링된 하이퍼파라미터를 이용해 모델을 생성.
    2. 해당 모델을 10 epoch 동안 훈련.
    3. ‘my_fashion_mnist/my_rnd_search’의 서브 디렉터리에 저장. (overwrite=True이므로 훈련 시작 전에 ‘my_rnd_search’ 디렉터리가 삭제됨.)

만약 위 코드를 max_trials=10, overwrite=True로 바꾸어서 실행하면, 5번 실행된 기존 디렉터리가 삭제되지 않기 때문에 뒤 이어 5번만 추가로 더 실행될 것이다.

최상의 모델, 하이퍼파라미터는 다음과 같이 얻는다.

# best model
top3_models = random_search_tuner.get_best_models(num_models=3)
best_model = top3_models[0]

# best hyperparemeters
top3_params = random_search_tuner.get_best_hyperparameters(num_trials=3)
top3_params[0].values

각 튜너는 oracle의 안내를 받는다. 튜너는 시도 전에 oracle에 다음 시도가 무엇인지 알려달라고 요청한다. Oracle은 모든 시도를 기록하고 있으며, 최상의 시도가 무엇인지 정보를 요청할 수 있다. 다음은 oracle을 이용해 최상의 시도를 요약한 것이다.

best_trial = random_search_tuner.oracle.get_best_trials(num_trials=1)[0]
best_trial.summary()


model.fit() 미세 튜닝

간혹 model.fit() 매개 변수를 미세 튜닝해야할 수도 있다. 데이터 전처리 하이퍼파라미터나 배치 크기를 조정하는 경우가 이에 해당한다. 이를 위해서는 kt.HyperModel의 서브 클래스를 만들고 직접 build() 메서드와 fit() 메서드를 정의해주어야 한다.

다음 코드는 데이터를 학습하기 전에 훈련 데이터를 normalize할지 여부를 결정하는 기능을 추가한 것이다.

class MyClassificationHyperModel(kt.HyperModel):
    def build(self, hp):
        return build_model(hp)

    def fit(self, hp, model, X, y, **kwargs):
        if hp.Boolean("normalize"):
            norm_layer = tf.keras.layers.Normalization()
            X = norm_layer(X)
        return model.fit(X, y, **kwargs)


Hyperband

kt.Hyperband 튜너는 몇 번의 epoch 동안 여러 모델을 훈련하고, 최악의 모델을 제거한 뒤 상위 1 / factor 개(e.g. factor=3이라면 상위 33%)의 모델만 남긴다. 이 과정을 단일 모델이 남을 때까지 반복한다.

hyperband_tuner = kt.Hyperband(
    MyClassificationHyperModel(), objective='val_accuracy', seed=42,
    max_epochs=10, factor=3, hyperband_iterations=2, overwrite=True,
    directory='my_fashion_mnist', project_name='hyperband'
)

max_epochs는 최상의 모델을 훈련할 최대 epoch 수이며, 전체 과정은 두 번 반복(hyperband_iterations=2)된다. 각 hyperhand 반복에서 전체 모델의 총 훈련 수는 ‘max_epochs * (log(max_epochs) / log(factor))**2’이다.

Hyperband 튜너는 다음과 같이 실행한다. 다음 코드는 tensorboard callback과 early stopping callback도 같이 사용하고 있다.

root_logdir = Path(hyperband_tuner.project_dir) / "tensorboard"
tensorboard_cb = tf.keras.callbacks.TensorBoard(root_logdir)
early_stopping_cb = tf.keras.callbacks.EarlyStopping(patience=2)
hyperband_tuner.search(X_train, y_train, epochs=10, validation_data=(X_valid, y_valid),
                       callbacks=[early_stopping_cb, tensorboard_cb])

Hyperband는 리소스를 할당하는 방식에서 순수 랜덤 서치보다는 똑똑하지만, 여전히 랜덤으로 탐색하기 때문에 빠르지만 듬성듬성하다.


BayesianOptimization

kt.BayesianOptimization는 가우스 과정 확률 모델을 적용하여, 하이퍼파라미터 공간의 어느 영역이 가장 유망한지 점진적으로 학습한다.

bayesian_opt_tuner = kt.BayesianOptimization(
    MyClassificationHyperModel(), objective='val_accuracy', seed=42,
    max_trials=10, alpha=1e-4, beta=2.6,
    overwrite=True, directory='my_fashion_mnist', project_name='baysian_opt'
)
bayesian_opt_tuner.search(X_train, y_train, epochs=10,
                          validation_data=(X_valid, y_valid),
                          callbacks=[early_stopping_cb])

유망한 공간을 집중 탐색한다는 것이 장점이지만, alphabeta라는 추가 하이퍼파라미터가 등장한다는 점은 단점이다.

  • alpha: 여러 번의 시도에 걸친 성능 측정에서 예상되는 잡음 수준
  • beta: 하이퍼파라미터 공간에서 알려진 좋은 영역을 단순히 활용하는 대신, 알고리즘이 얼마나 공간을 탐색할지 지정.


하이퍼파라미터 가이드라인

  1. 은닉 층 개수
    하나 혹은 두 개의 은닉 층으로도 충분한 경우가 많지만, 보통의 경우 심층 신경망이 얕은 신경망보다 성능이 더 좋다. 필요하다면 다른 신경망의 저수준 구조를 가져온 뒤, 고수준 구조만 학습할 수도 있다(전이 학습).

  2. 은닉 층의 뉴런 개수
    과거에 신경망은 보통 깔떄기 형태처럼 뉴런의 수를 점차 줄여 나가는 구조(300 > 200 > 100)로 구성되는 경우가 많았다. 하지만 요즘에는 은닉 층에 같은 수의 뉴런을 사용해도 성능 차이가 나지 않거나 오히려 성능이 더 나은 경우가 많다. 첫 번째 은닉 층을 다른 은닉 층보다는 크게 하는 것이 도움이 될 때도 있다. 신경망이 과대적합되기 전까지 점진적으로 뉴런의 개수를 늘릴 수도 있고, 필요한 것보다 많은 뉴런과 층을 가진 모델을 선택한 다음 과대적합되지 않도록 조기 종료하거나 규제 기법을 사용해도 된다.

  3. 학습률
    매우 낮은 학습률에서 시작해서 높은 학습률까지 모델을 수백 번 훈련하는 것도 좋은 방법이다. 또는 반복마다 일정한 값을 학습률에 곱하는 것도 가능하다.

  4. Optimizer
    고전적인 경사 하강법보다 더 좋은 optimizer가 많으니 잘 선택하자.

  5. batch 크기
    큰 batch를 사용하면 GPU 같은 하드웨어 가속기를 효율적으로 활용할 수 있지만, 훈련 초기에 종종 불안정하게 훈련될 수 있다. 일반화 성능이 떨어지는 것이다. 적절한 batch 크기는 연구자들 사이에서도 의견이 다르다. 한 가지 방법은 학습률 예열(작은 학습률로 시작해서 학습률을 증가시킴)을 사용하여 큰 batch 크기를 시도하고, 좋지 않다면 크기를 줄이는 것이다.

  6. 활성화 함수
    ReLU가 일반적으로는 가장 좋은 기본 값이다. 수행하는 작업에 따라 달라진다.

  7. 반복 횟수
    따로 튜닝할 필요 없이 조기 종료를 사용하자.

코드 보러가기



별도의 출처 표시가 있는 이미지를 제외한 모든 이미지는 강의자료에서 발췌하였음을 밝힙니다.

댓글남기기