수동으로 매개변수 조정
그리드 서치와 랜덤 서치
베이즈 최적화(BO; Bayesian optimization)
톱 클래스의 캐글러는 대부분 수동으로 매개변수를 조정한다. 하지만 숙련되지 않았다면 베이즈 최적화로 효율적인 탐색을 하고 그 결과를 살펴보며 경험을 쌓아가는 것을 권장한다.
- 베이스라인이 되는 매개변수로 학습
- 간단하게 조정하고 싶을 때는 1~3종류의 매개변수와 각각 2~5개 정보의 후보를 추려서 그리드 서치
- 본격적으로 매개변수 튜닝을 수행할 때는 베이즈 최적화(이 단계에서는 그리드 서치나 랜덤서치보다 베이즈 최적화를 이용하는게 효육적)
특히 모델이 GBDT일 때는 매개변수 튜닝보다 좋은 특징을 추가하는 편이 모델 성능 개선에 더 도움이 될 수 있다. 어느 정도의 매개변수 튜닝은 특징을 평가하기 위워지므로 유효하지만, 초반부터 지나치게 주력하지 않는 편이 좋다.
# hp.choice에서는 복수의 선택사항에서 고르기
# hp.uniform에서는 하한상한을 지정한 동일분포로부터 추출. 인수는 하한상한
# hp.quniform에서는 하한상한을 지정한 균등분포 중 일정 간격마다의 점으로부터 추출. 인수는 하한상한간격
# hp.loguniform에서는 하한상한을 지정한 로그을 취한 값이 균등분포를 따르는 분포로부터 추출. 인수는 하한상한의 로그를 취한 값
# 만약 moduleNotFoundError 발생시 hyperopt 설치 후 진행
# 2021/04/27 hyperopt 설치 확인 : 0.2.5
# pip install hyperopt
from hyperopt import hp
space = {
'activation': hp.choice('activation', ['prelu', 'relu']),
'dropout': hp.uniform('dropout', 0, 0.2),
'units': hp.quniform('units', 32, 256, 32),
'learning_rate': hp.loguniform('learning_rate', np.log(0.00001), np.log(0.01)),
}
from hyperopt import fmin, tpe, hp, STATUS_OK, Trials
from sklearn.metrics import log_loss
def score(params):
# 매개변수를 주었을 때, 최소화하는 평가지표를 지정
# 구체적으로는 모델에 매개변수를 지정하여 학습예측하는 경우의 점수를 반환
# max_depth의 형을 정수형으로 수정
params['max_depth'] = int(params['max_depth'])
# Model 클래스를 정의하는 것으로 함
# Model 클래스는 fit으로 학습하고 predict로 예측값의 확률을 출력
model = Model(params)
model.fit(tr_x, tr_y, va_x, va_y)
va_pred = model.predict(va_x)
score = log_loss(va_y, va_pred)
print(f'params: {params}, logloss: {score:.4f}')
# 정보를 기록
history.append((params, score))
return {'loss': score, 'status': STATUS_OK}
# 탐색할 매개변수의 공간을 지정
space = {
'min_child_weight': hp.quniform('min_child_weight', 1, 5, 1),
'max_depth': hp.quniform('max_depth', 3, 9, 1),
'gamma': hp.quniform('gamma', 0, 0.4, 0.1),
}
# hyperopt에 의한 매개변수 탐색 실행
max_evals = 10
trials = Trials()
history = []
fmin(score, space, algo=tpe.suggest, trials=trials, max_evals=max_evals)
# 기록한 정보에서 매개변수와 점수를 출력
# (trials에서도 정보를 취득할 수 있지만 매개변수의 취득이 다소 어려움)
history = sorted(history, key=lambda tpl: tpl[1])
best = history[0]
print(f'best params:{best[0]}, score:{best[1]:.4f}')
실제로 베이즈 최적화를 해보면 다음과 같은 문제로 튜닝하지 못할 때가 존재한다.
1. 계산 시간이 오래 걸리는 실행 : 에폭 수의 상한을 크게 잡지 않도록 하거나 콜백에 의해 일정 시간 내에 학습이 종료되지 않으면 종료시키는 방법을 고려
2. 매개변수 간 의존성 : 매개변수가 모델 성능에 미치는 영향은 각 매개변수에 독립적이지 않으면서 서로 어느 정도의 의존성은 가진다. 이때는 의존 관계를 매개변수 공간에 명시적으로 정의하거나, 그게 어렵다면 시행횟수를 늘리는 방법을고려할 수 있다.
3. 평가의 랜덤성에 따른 편차 : 평가의 편차가 크면 효과적으로 탐색할 수 없다. 하나의 폴드가 아닌 교차 검증에 따른 평균값으로 평가하거나 시행 횟수를 늘리는 방법을 고려할 수 있다.
eta : 학습률 -> 작게 설정한다고 성능이 떨어지는 일은 거의 없지만, 시간이 오래 걸림. 처음에는 0.1 정도의 약간 큰값으로 두었다가 추후에 0.01~0.05 정도로 줄인다.
num_round : 작성할 결정 트리의 개수 -> 1,000이나 10,000 등 충분한 값으로 두었다가 조기 종료에서 자동 종료되도록 설정되는게 좋음.(조기 종료를 관찰하는 라운드수 (early_stopping_rounds)는 50정도가 좋다.)
max_depth : 결정 트리의 깊이(모델 복잡도 조정)
min_child_weight : 잎의 분기에 필요한 잎을 구성하는 데이터수(정확히는 데이터 수가 아닌, 목적함수로의 이차 미분값이 사용)(모델 복잡도 조정)
gamma : 결정 트리를 분기시키기 위해 최소한으로 줄여야 하는 목적함수의 값(모델 복잡도 조정)
colsample_bytree : 결정 트리별로 특징으로 사용할 열을 샘플링하는 비율(랜덤성 추가하여 과적합 억제)
subsample : 결정 트리별로 학습 데이터의 행을 샘플링하는 비율(랜덤성 추가하여 과적합 억제)
alpha : 결정 트리의 잎의 가중치에 대한 L1 정규화 강도(모델 복잡도 조정)
lambda : 결정 트리의 잎의 가중치에 대한 L2 정규화 강도(모델 복잡도 조정)
max_depth가 가장 중요하고 subsample, colsample_bytree, min_child_weight도 중요하다는 의견이 많다. 한편 gamma, alpha, lambda는 개인 취향에 따라 우선도가 달라진다.
++ 기타 교재 내용
네트워크 구성
옵티마이저 선택
기타
++ 기타 교재 내용
참고 : 데이터가 뛰어노는 AI 놀이터, 캐글