[Deep Learning] 베이지안 하이퍼파라미터 최적화 (Bayesian Hyperparameter Optimization)
베이지안 하이퍼파라미터 최적화 (Bayesian Hyperparameter Optimization)
베이지안 하이퍼파라미터 최적화는 그리드 검색보다 더 효율적으로 하이퍼파라미터를 찾는 방법이다. 하이퍼파라미터의 각 후보 집합은 신경망을 다시 학습시켜야 하므로 후보 집합의 수를 최소한으로 유지하는 것이 가장 좋다. 베이지안 하이퍼파라미터 최적화는 좋은 하이퍼파라미터 후보 집합을 예측하는 모델을 학습시켜 이를 달성한다.
|
# Ignore useless W0819 warnings generated by TensorFlow 2.0.
# Hopefully can remove this ignore in the future.
# See https://github.com/tensorflow/tensorflow/issues/31308
import logging, os
logging.disable(logging.WARNING)
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"
import pandas as pd
from scipy.stats import zscore
# Read the dataset
df = pd.read_csv(
"https://data.heatonresearch.com/data/t81-558/jh-simple-dataset.csv",
na_values=['NA', '?']
)
# Generate dummies for job
df = pd.concat([df, pd.get_dummies(df['job'], prefix="job")], axis=1)
df.drop('job', axis=1, inplace=True)
# Generate dummies for area
df = pd.concat([df, pd.get_dummies(df['area'], prefix="area")], axis=1)
df.drop('area', axis=1, inplace=True)
# Missing values for income
med = df['income'].median()
df['income'] = df['income'].fillna(med)
# Standardize ranges
df['income'] = zscore(df['income'])
df['aspect'] = zscore(df['aspect'])
df['save_rate'] = zscore(df['save_rate'])
df['age'] = zscore(df['age'])
df['subscriptions'] = zscore(df['subscriptions'])
# Convert to numpy - Classification
x_columns = df.columns.drop('product').drop('id')
x = df[x_columns].values
dummies = pd.get_dummies(df['product']) # Classification
products = dummies.columns
y = dummies.values
이제, 데이터를 전처리했으므로 하이퍼파라미터 최적화를 시작할 수 있다. 먼저, 세 개의 매개변수만으로 모델을 생성하는 함수를 만드는 것으로 시작한다. 베이지안 최적화는 각 레이어에 몇 개의 레이어와 뉴런이 있는지와 같은 문제가 되는 개념이 아니라 숫자 벡터에서 작동한다. 이 복잡한 뉴런 구조를 벡터로 표현하기 위해 몇 가지 숫자를 사용하여 이 구조를 설명한다.
|
이 세 가지 숫자는 신경망의 구조를 정의한다. 아래 코드의 쉼표는 프로그램이 네트워크를 구성하는 방법을 정확하게 보여준다.
import pandas as pd
import os
import numpy as np
import time
import tensorflow.keras.initializers
import statistics
import tensorflow.keras
from sklearn import metrics
from sklearn.model_selection import StratifiedKFold, StratifiedShuffleSplit, ShuffleSplit
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Activation, Dropout, InputLayer
from tensorflow.keras import regularizers
from tensorflow.keras.callbacks import EarlyStopping
from tensorflow.keras.layers import LeakyReLU, PReLU
from tensorflow.keras.optimizers import Adam
def generate_model(dropout, neuronPct, neuronShrink):
neuronCount = int(neuronPct * 5000)
model = Sequential()
layer = 0
while neuronCount > 25 and layer < 10:
if layer == 0:
model.add(Dense(neuronCount, input_dim=x.shape[1], activation=PReLU()))
else:
model.add(Dense(neuronCount, activation=PReLU()))
layer += 1
model.add(Dropout(dropout))
neuronCount = neuronCount * neuronShrink
model.add(Dense(y.shape[1], activation='softmax')) # Output
return model
이 코드를 테스트하여 이러한 세 가지 매개 변수를 기반으로 신경망을 생성하는 방법을 확인할 수 있다.
# 모델 생성 및 결과 구조 확인
model = generate_model(dropout=0.2, neuronPct=0.1, neuronShrink=0.25)
model.summary()
이제, 세 가지 파라미터를 사용하여 신경망을 평가하는 함수를 만들어 보겠다. 부트스트랩을 사용하는 이유는 한 번의 훈련 실행에 무작위 가중치가 할당되는 '불운'이 있을 수 있기 때문이다. 이 함수를 사용하여 신경망을 훈련한 다음 평가한다.
SPLITS = 2
EPOCHS = 500
PATIENCE = 10
def evaluate_network(dropout, learning_rate, neuronPct, neuronShrink):
# Bootstrap
# for Classification
boot = StratifiedShuffleSplit(n_splits=SPLITS, test_size=0.1)
# for Regression
# boot = ShuffleSplit(n_splits=SPLITS, test_size=0.1)
# Track progress
mean_benchmark = []
epochs_needed = []
num = 0
# Loop through samples
for train, test in boot.split(x, df['product']):
start_time = time.time()
num += 1
# Split train and test
x_train = x[train]
y_train = y[train]
x_test = x[test]
y_test = y[test]
model = generate_model(dropout, neuronPct, neuronShrink)
model.compile(loss='categorical_crossentropy', optimizer=Adam(learning_rate=learning_rate))
monitor = EarlyStopping(monitor='val_loss', min_delta=1e-3, patience=PATIENCE, verbose=0, mode='auto', restore_best_weights=True)
# Train on the bootstrap sample
model.fit(x_train, y_train, validation_data=(x_test, y_test), callbacks=[monitor], verbose=0, epochs=EPOCHS)
epochs = monitor.stopped_epoch
epochs_needed.append(epochs)
# Predict on the out-of-bootstrap (validation)
pred = model.predict(x_test)
# Measure this bootstrap's log loss
y_compare = np.argmax(y_test, axis=1) # For log loss calculation
score = metrics.log_loss(y_compare, pred)
mean_benchmark.append(score)
m1 = statistics.mean(mean_benchmark)
m2 = statistics.mean(epochs_needed)
mdev = statistics.pstdev(mean_benchmark)
# Record this iteration
time_took = time.time() - start_time
tensorflow.keras.backend.clear_session()
return (-m1)
세 가지 하이퍼파라미터와 학습률을 조합하여 이 네 가지 수치가 얼마나 효과적인지 확인할 수 있다. 물론, 이 네 가지 하이퍼파라미터의 다양한 조합을 수동으로 선택하는 것이 아니라 자동화를 추구하는 것이 목표이다.
print(evaluate_network(dropout=0.2, learning_rate=1e-3, neuronPct=0.2, neuronShrink=0.2))
먼저, Colab을 사용하는 경우 베이지안 최적화 패키지를 설치해야 한다.
!pip install bayesian-optimization
이제, 이 프로세스를 자동화하겠다. 이 네 가지 하이퍼파라미터 각각에 대한 경계를 정의하고 베이지안 최적화를 시작한다. 프로그램이 완료되면 발견된 최적의 하이퍼파라미터 조합이 표시된다. 최적화 함수는 프로세스를 완료하는 데 걸리는 시간에 큰 영향을 미치는 두 가지 매개변수를 허용한다
|
from bayes_opt import BayesianOptimization
import time
# Suppress NaN warnings
import warnings
warnings.filterwarnings("ignore", category=RuntimeWarning)
# Bounded region of parameters space
pbounds = {'dropout': (0.0, 0.499),
'learning_rate': (0.0, 0.1),
'neuronPct': (0.01, 1),
'neuronShrink': (0.01, 1)
}
optimizer = BayesianOptimization(
f=evaluate_network,
pbounds=pbounds,
verbose=2, # verbose = 1 prints only when a maximum
# is observed, verbose = 0 is silent
random_state=1,
)
start_time = time.time()
optimizer.maximize(init_points=10, n_iter=20,)
time_took = time.time() - start_time
print(f"Total runtime: {hms_string(time_took)}")
print(optimizer.max)
Total runtime: 1:36:11.56
{'target': -0.6955536706512794, 'params': {'dropout': 0.2504561773412203, 'learning_rate': 0.00762323467091429, 'neuronPct': 0.01264879152181182, 'neuronShrink': 0.5229748831552032}}
보시다시피 알고리즘은 총 30회의 반복을 수행했다. 이 총 반복 횟수에는 10번의 무작위 반복과 20번의 최적화 반복이 포함된다.