🔥알림🔥
① 테디노트 유튜브 - 구경하러 가기!
② LangChain 한국어 튜토리얼 바로가기 👀
③ 랭체인 노트 무료 전자책(wikidocs) 바로가기 🙌

3 분 소요

이번 포스팅에서는 기계 학습 모델의 성능을 극대화하기 위해 하이퍼파라미터 최적화 과정에 대해 깊게 다루려고 합니다.

특히, 최적의 하이퍼파라미터를 찾기 위해 Optuna 라는 라이브러리를 사용하는 방법에 초점을 맞춰 설명하겠습니다.

Optuna 를 활용하면 다양한 알고리즘의 파라미터를 효과적으로 탐색하면서, 교차 검증 기반으로 모델의 일반화 성능을 평가할 수 있습니다. 이러한 과정을 통해 모델의 성능을 향상시키는 데 큰 도움이 될 것입니다.

Optuna 설치

pip install optuna

파라미터 도메인 설정: trial.suggest_…

  • 참고 문서: https://optuna.readthedocs.io/en/stable/reference/generated/optuna.trial.Trial.html#optuna.trial.Trial

Optuna 라이브러리는 하이퍼파라미터 최적화를 위한 강력한 도구로, 여러 종류의 하이퍼파라미터에 대한 제안 방법을 제공합니다. trial.suggest_... 메소드를 통해 다양한 형태의 하이퍼파라미터를 제안할 수 있습니다.

  1. trial.suggest_categorical()
    • 카테고리형 하이퍼파라미터를 제안합니다.
    • 인수:
      • name (str): 하이퍼파라미터의 이름입니다.
      • choices (Sequence): 가능한 카테고리 값을 포함한 리스트나 튜플입니다.
    • 이 메소드는 주어진 choices 중 하나를 무작위로 선택하여 반환합니다.
    • 예시
       optimizer = trial.suggest_categorical("optimizer", ["SGD", "Adam", "RMSprop"])
      
  2. trial.suggest_int()
    • 정수 범위 내의 하이퍼파라미터를 제안합니다.
    • 인수:
      • name (str): 하이퍼파라미터의 이름입니다.
      • low (int): 가능한 최소값입니다.
      • high (int): 가능한 최대값입니다.
      • step (int, optional): 값 간의 거리를 지정합니다. 기본값은 1입니다.
      • log (bool, optional): 로그 스케일로 값을 제안할 것인지를 결정합니다. 기본값은 False입니다.
    • 이 메소드는 lowhigh 사이의 정수 값을 무작위로 선택하여 반환합니다. step이 주어진 경우, low에서 시작하여 step 만큼씩 증가하는 값들 중 하나를 반환합니다.
    • 예시
       n_estimators = trial.suggest_int("n_estimators", 100, 2000, step=100)
      
  3. trial.suggest_float()
    • 실수 범위 내의 하이퍼파라미터를 제안합니다.
    • 인수:
      • name (str): 하이퍼파라미터의 이름입니다.
      • low (float): 가능한 최소값입니다.
      • high (float): 가능한 최대값입니다.
      • step (float, optional): 값 간의 거리를 지정합니다. step이 주어지면 실수 대신 lowhigh 사이의 이산 값 중 하나를 제안합니다.
      • log (bool, optional): 로그 스케일로 값을 제안할 것인지를 결정합니다. 기본값은 False입니다.
    • 이 메소드는 lowhigh 사이의 실수 값을 무작위로 선택하여 반환합니다. step이 주어진 경우, low에서 시작하여 step 만큼씩 증가하는 값들 중 하나를 반환합니다.
    • 예시
       max_features = trial.suggest_float('max_features', 0.6, 0.9, step=0.1),
      

이 3가지 함수를 활용하여 다양한 유형의 하이퍼파라미터 공간을 정의하고 탐색할 수 있습니다. Optuna의 장점 중 하나는 이러한 함수들을 쉽게 조합하여 복잡한 하이퍼파라미터 탐색 공간도 쉽게 정의할 수 있다는 점입니다.

Optuna 를 활용한 파라미터 최적화

objective 함수를 정의합니다. 함수 내에서는 5-Fold Cross Validation 평가를 통해 가장 낮은 에러를 도출하도록 parameter 조정이 이루어 집니다.

알고리즘 별로 파라미터 도메인이 다를 수 있으므로, objective 함수 내의 params 는 적절히 수정하도록 합니다.

from optuna.trial import Trial
import optuna
from sklearn.model_selection import KFold
from sklearn.metrics import mean_squared_error
from sklearn.ensemble import RandomForestRegressor

def objective(trial: Trial, model, x, y, eval_metric):
    """
    Optuna의 하이퍼파라미터 튜닝을 위한 목적 함수
    
    Parameters:
    - trial: Optuna의 Trial 객체
    - model: 튜닝할 머신러닝 모델
    - x: 입력 데이터
    - y: 타겟 데이터
    - eval_metric: 모델을 평가하기 위한 메트릭
    """
    
    # 모델에 맞는 하이퍼파라미터 범위를 정의합니다.
    params = {
        'random_state': 123,
        'n_jobs': -1,
        'criterion': trial.suggest_categorical('criterion', ['squared_error', 'absolute_error']),
        'max_depth': trial.suggest_int('max_depth', 3, 20, step=1),
        'n_estimators': trial.suggest_int('n_estimators', 50, 500, step=10),
        'max_features': trial.suggest_float('max_features', 0.6, 0.9, step=0.1),
    }

    # 데이터 프레임이나 시리즈 형태의 x와 y를 numpy 배열로 변환합니다.
    x = x.values if isinstance(x, pd.DataFrame) else x    
    y = y.values if isinstance(y, pd.Series) else y

    results = dict()
    
    # 5-Fold Cross Validation을 정의합니다.
    fold = KFold(n_splits=5, shuffle=True, random_state=SEED)
    
    # 각 Fold에 대해 모델을 학습하고 평가합니다.
    for i, (train_idx, test_idx) in enumerate(fold.split(x, y)):
        x_train, y_train = x[train_idx], y[train_idx]
        x_test, y_test = x[test_idx], y[test_idx]
        
        # 현재의 하이퍼파라미터 세트로 모델을 생성하고 학습합니다.
        fold_model = model(**params)  
        fold_model.fit(x_train, y_train)
        fold_pred = fold_model.predict(x_test)
        
        # 해당 Fold의 평가 결과를 저장합니다.
        fold_error = eval_metric(y_test, fold_pred)
        
        results[i] = {
            'model': fold_model, 
            'error': fold_error
        }
    
    # 모든 Fold의 평가 결과의 평균을 반환합니다.
    errors = [v['error'] for k, v in results.items()]
    return np.array(errors).mean()

optuna.create_study() 로 최적화 객체를 생성하고 optimize() 함수를 호출하여 최적화를 진행합니다.

optimize() 함수에 전달하는 인수에 모델, x, y, 평가지표, 마지막으로 n_trials 를 적절히 전달합니다.

# 최소화 방향으로 하이퍼 파라미터 학습을 위한 스터디 객체를 생성합니다.
study = optuna.create_study(direction='minimize', study_name='randomforest_1')

# 하이퍼파라미터 튜닝을 시작합니다.
study.optimize(lambda trial: objective(trial, RandomForestRegressor, x, y, mean_squared_error), n_trials=20)

최종 결과를 출력하여 찾은 파라미터를 출력합니다.

# 최적의 하이퍼파라미터를 출력합니다.
print(study.best_params)

# 모델에 적용시
model = RandomForestRegressor(**study.best_params)
model.fit(x, y)

댓글남기기