신경망 하이퍼파라미터 튜닝하기
이 글은 <핸즈온 머신러닝>(2판)을 참고하여 만들어졌습니다.
3. 신경망 하이퍼파라미터 튜닝하기
신경망의 유연성은 단점이기도 합니다. => 조정할 하이퍼파라미터가 많기 때문입니다.
아주 복잡한 네트워크 구조에서뿐만 아니라 간단한 다층 퍼셉트론에서도 층의 개수, 층마다 있는 뉴런의 개수, 각 층에서 사용하는 활성화 함수, 가중치 초기화 전략 등 많은 것을 바꿀 수 있습니다.
어떤 하이퍼파라미터 조합이 주어진 문제에 최적일까요??
한 가지 방법은 많은 하이퍼파라미터 조합을 시도해보고 어떤 것이 검증 세트에서(또는 K-폴드 교차 검증으로) 가장 좋은 점수를 내는지 확인하는 것이다.
ex. GridSearchCV나 RandomizedSearchCV를 사용해 하이퍼파라미터 공간을 탐색할 수 있습니다.
이렇게 하려면 케라스 모델을 사이킷런 추정기처럼 보이도록 바꾸어야 합니다.
def build_model(n_hidden=1, n_neurons=30, learning_rate=3e-3, input_shape=[8]):
model = keras.models.Sequential()
model.add(keras.layers.InputLayer(input_shape=input_shape))
for layer in range(n_hidden):
model.add(keras.layers.Dense(n_neurons, activation="relu"))
model.add(keras.layers.Dense(1))
optimizer = keras.optimizers.SGD(lr=learning_rate)
model.compile(loss="mse", optimizer=optimizer)
return model
=> 이함수는 주어진 입력 크기와 은닉층 개수, 뉴런 개수로 (한 개의 출력 뉴런만 있는) 단변량(univariate) 회귀를 위한 간단한 Sequential 모델을 만듭니다. 그리고 지정된 학습률을 사용하는 SGD 옵티마이저로 모델을 컴파일합니다.
KerasRegressor 클래스 객체 만들기
keras_reg = keras.wrappers.scikit_learn.KerasRegressor(build_model)
- KerasRegressor 객체는 build_model() 함수로 만들어진 케라스 모델을 감싸는 간단한 래퍼(Wrapper)
- 기본 하이퍼파라미터를 사용
다음과 같이 fit() 메서드로 훈련하고 score() 메서드로 평가하고 predict() 메서드로 예측
from sklearn.metrics import mean_squared_error
keras_reg.fit(X_train, y_train, epochs=100,
validation_data=(X_valid, y_valid),
callbacks=[keras.callbacks.EarlyStopping(patience=10)])
y_pred = keras_reg.predict(X_test)
mse_test = mean_squared_error(y_test, y_pred)
print("테스트 세트 MSE:", mse_test)
# 새 데이터 예측
y_pred_new = keras_reg.predict(X_new)
- fit() 메서드에 지정한 모든 매개변수는 케라스 모델로 전달
- 사이킷런은 손실이 아니라 점수를 계산하기 때문에 출력 점수는 음수의 MSE입니다. (높을 수록 GOOD)
- 모델 하나를 훈련하고 평가하는 것이 아니라 수백 개의 모델을 훈련하고 검증 세트에서 최상의 모델을 선택
- 그리드 탐색보단 랜덤 탐색 사용
from scipy.stats import reciprocal
from sklearn.model_selection import RandomizedSearchCV
import numpy as np
param_distribs = {
"n_hidden" : [0, 1, 2, 3],
"n_neurons" : np.arange(1, 100),
"learning_rate" : reciprocal(3e-4, 3e-2),
}
rnd_search_cv = RandomizedSearchCV(keras_reg, param_distribs, n_iter=10, cv=3)
rnd_search_cv.fit(X_train, y_train, epochs=100,
validation_data=(X_valid, y_valid),
callbacks=[keras.callbacks.EarlyStopping(patience=10)])
- RandomizedSearchCV는 k-겹 교차 검증을 사용하기 때문에 X_valid와 y_valid를 사용 x.
- 실행이 끝나면 랜덤 탐색이 찾은 최상의 하이퍼파라미터와 훈련된 케라스 모델을 얻을 수 있습니다.
이제 이 모델을 저장하고 테스트 세트에서 평가합니다. 성능이 만족스럽다면 상용 환경에 배포합니다.
랜덤 탐색을 사용하는 것은 크게 어렵지 않으며 간단한 문제에서 잘 동작합니다.
but, 훈련에 시간이 많이 걸리면 하이퍼파라미터 공간에 제약이 생깁니다.
=> 하이퍼파라미터 값의 범위를 크게 하여 빠르게 첫 번째 랜덤 탐색을 수행하고,
첫 번째 탐색에서 찾은 최상의 하이퍼파라미터 집합을 좁혀 나갈 수 있습니다.
다양하게 효율적으로 하이퍼파라미터 공간을 탐색하는 여러 기법들이 존재한다.
- Hyperopt : (학습률과 같은 실수와 층의 개수 같은 이산적인 값을 포함하여) 모든 종류의 복잡한 탐색 공간에 대해 최적화를 수행할 수 있는 잘 알려진 라이브러리
- Hyperas, kopt, Talot : 케라스 모델을 위한 하이퍼파라미터 최적화 라이브러리
- 케라스 튜너 : 사용하기 쉬운 케라스 하이퍼파라미터 최적화 라이브러리
- Scikit-Optimize(skopt) : 범용 최적화 라이브러리입니다. BayesSearchCV 클래스는 GridSearchCV와 비슷한 인터페이스를 사용하여 베이즈(Bayesian) 최적화를 수행합니다.
- Spearmint : 베이즈 최적화 라이브러리
- Hyperband : 빠른 하이퍼파라미터 튜닝 라이브러리
- Sklearn-Deap : GridSearchCV와 비슷한 인터페이스를 가진 진화 알고리즘(evolutionary algorithm) 기반의 하이퍼파라미터 최적화 라이브러리
ex. 구글의 AutoML 서비스는 이미 클라우드로 제공
=> 진화 알고리즘은 흔한 경사 하강법을 대체하여 개별 신경망을 성공적으로 훈련
3.1 은닉층 개수
이론적으로 은닉층이 하나인 다층 퍼셉트론이더라도 뉴런 개수가 충분하면 아주 복잡한 함수도 모델링할 수 있습니다.
하지만 복잡한 문제에서는 심층 신경망이 파라미터 효율성(parameter efficiency)이 훨씬 좋습니다.
- 계층 구조는 심층 신경망이 좋은 솔루션으로 빨리 수렴하게끔 도와줄 뿐만 아니라 새로운 데이터에 일반화되는 능력도 향상시켜줍니다.
- 새로운 신경망에서 처음 몇 개 층의 가중치와 편향을 난수로 초기화하는 대신 첫 번째 신경망의 층에 있는 가중치와 편향값으로 초기화
=> 저수준 구조 학습 x(고수준 구조만 학습) => 전이 학습(transfer learning)
더 복잡한 문제라면 훈련 세트에 과대적합이 생길 때까지 점진적으로 은닉층의 수를 늘릴 수 있다.
대규모 이미지 분류나 음성 인식 같이 매우 복잡한 작업은 일반적으로 수십 개 층으로 이뤄진 네트워크가 필요함.
3.2 은닉층의 뉴런 개수
입력층과 출력층의 뉴런 개수는 해당 작업에 필요한 입력과 출력의 형태에 따라 결정됩니다.
* 은닉층의 구성 방식은 일반적으로 각 층의 뉴런을 점점 줄여서 깔때기처럼 구성합니다.
=> 저수준의 많은 특성이 고수준의 적은 특성으로 합쳐지기 때문이다.
층의 개수와 마찬가지로 네트워크가 과대적합이 시작되기 전까지 점진적으로 뉴런 수를 늘릴 수 있습니다.
하지만 더 많은 층과 뉴런을 가진 모델을 선택하고, 과대적합되지 않도록 조기 종료나 규제 기법을 사용하는 것이 간단하고 효과적입니다.
다른 한편으로는 한 층의 뉴런 수가 너무 적으면 입력에 있는 유용한 정보를 모두 유지하기 위한 충분한 표현 능력 x
3.3 학습률, 배치 크기 그리고 하이퍼파라미터
- 학습률
- 옵티마이저
- 배치 크기
- 활성화 함수
- 반복 횟수