TOP
class="layout-aside-left paging-number">
본문 바로가기
[파이썬 Projects]/<파이썬 머신러닝>

[머신러닝] 분류 - 베이지안 최적화 (1)

by 기록자_Recordian 2024. 7. 7.
728x90
반응형
시작에 앞서
해당 내용은 '<파이썬 머신러닝 완벽 가이드> 권철민 지음. 위키북스' 를 토대로 작성되었습니다. 보다 자세한 내용은 해당 서적에 상세히 나와있으니 서적을 참고해 주시기 바랍니다.

베이지안 최적화

 

XGBoost나 LightGBM은 성능이 매우 뛰어난 알고리즘이며 Grid Search 방식을 적용한 알고리즘이다.

그러나, Grid Search 방식은 한 가지 주요한 단점이 있는데, 튜닝해야 할 하이퍼 파라미터 개수가 많을 경우 최적화 수행 시간이 오래 걸린다는 것이다. 거기에 개별 하이퍼 파라미터 값의 범위가 넓거나 학습 데이터가 대용량 일 경우에는 최적화 시간이 더욱 늘어나게 된다.

 

이때 기하급수적으로 늘어나는 하이퍼 파라미터 최적화 시간 때문에 어쩔 수 없이 하이퍼 파라미터 범위를 줄이거나 개수를 줄여야 하는데, 이러한 경우 모델 성능을 향상시켜야 한다면 아쉬운 상황이 발생할 수 있다.

 

때문에 실무의 대용량 학습 데이터에 XGBoost나 LightGBM의 하이퍼 파라미터 튜닝 시에 Grid Search 방식보다는 다른 방식을 적용하는데, 그 대표적인 기법 중 하나가 베이지안 최적화 기법이다.

출처: DEEPLINK CORE Lab_

 

베이지안 최적화는 목적 함수 식을 제대로 알 수 없는 블랙 박스 형태의 함수에서 최대 또는 최소 함수 반환 값을 만드는 최적 입력값을 가능한 적은 시도를 통해 빠르고 효과적으로 찾아주는 방식이며, 베이지안 확률에 기반을 두고 있는 최적화 기법이다.

베이지한 확률이 새로운 사건의 관측이나 새로운 샘플 데이터를 기반으로 사후 확률을 개선해 나가듯이, 베이지안 최적화는 새로운 데이터를 입력받았을 때 최적 함수를 예측하는 사후 모델을 개선해 나가면서 최적 함수 모델을 만들어 낸다.

예를 들어, 함수 식 자체가 복잡하고, 입력값들의 개수가 많거나 범위가 넓은 경우에는 입력값을 순차적으로 대입해서는 결코 짧은 시간 안에 최적 입력값을 찾을 수가 없을 때, 베이지안 최적화를 이용하면 쉽고 빠르게 최적 입력값을 찾을 수 있다.

 

[베이지안 최적화를 구성하는 두 가지 중요 요소]

- 대체 모델(Surrogate Model): 획득 함수로부터 최적 함수를 예측할 수 있는 입력값을 추천 받은 뒤 이를 기반으로 최적 함수 모델을 개선 (획득 함수가 계산한 하이퍼 파라미터를 입력받으면서 점차적으로 개선됨)

- 획득 함수(Acquisition Function): 개선된 대체 모델을 기반으로 입력값은 하이퍼 파라미터가 됨. (더 정확한 하이퍼 파라미터를 계산 할 수 있게 됨)

 

[베이지안 최적화 구성 단계]

- 1단계: 최초에는 랜덤하게 하이퍼 파라미터들을 샘플링하고 성능 결과를 관측.

- 2단계: 관측된 값을 기반으로 대체 모델은 최적 함수를 추정.

- 3단계: 추정된 최적 함수를 기반으로 획득 함수는 다음으로 관측할 하이퍼 파라미터 값 계산.

- 4단계: 획득 함수로부터 전달된 하이퍼 파라미터를 수행하여 관측된 값을 기반으로 대체 모델은 갱신되어 다시 최적 함수를 예측 추정.

▶ 3단계와 4단계를 특정 횟수만큼 반복하게 되면 대체 모델의 불확실성이 개선되고 점차 정확한 최적 함수 추정이 가능하게 됨.

※ 대체 모델은 최적 함수 추정 시 다양한 알고리즘을 사용할 수 있는데 일반적으로는 가우시안 프로세스를 적용.

HyperOpt는 트리 파르젠 Estimator 사용.


HyperOpt 사용하기

 

먼저 anaconda prompt에서 pip를 이용해서 HyperOpt 설치

pip install hyperopt

 

설치 후에 주피터 노트북을 실행하여 사용하면 된다.

 

[HyperOpt 활용 주요 로직]

- 입력 변수명과 입력값의 검색 공간 설정

- 목적 함수의 설정

- 목적 함수의 반환 최솟값을 가지는 최적 입력값 유추 (주의!)

 

◆ hp 모듈

HyperOpt의 hp 모듈을 이용하여 입력 변수명과 입력값의 검색 공간 설정.

- 입력 변수명과 입력값 검색 공간은 파이썬 딕셔너리 형태로 설정되어야 한다.

- 키(Key)값으로 입력 변수명, 밸류(Value) 값으로 해당 입력 변수의 검색 공간이 주어진다.

- hp모듈은 입력값의 검색 공간을 다양하게 설정할 수 있도록 다양한 함수 제공.

ex) hp.quniform('x', -10, 10, 1): 입력 변수 x는 -10부터 10까지 1의 간격을 가지는 값들을 가진다.

 

from hyperopt import hp

# -10에서 10까지 1간격을 가지는 입력 변수 x와 -15~15까지 1간격으로 입력 변수 y 설정
search_space = {'x':hp.quniform('x', -10, 10, 1), 'y':hp.quniform('y', -15, 15, 1)}
[입력값의 검색 공간을 제공하는 대표적인 함수]
- hp.quniform(label, low, high, q): label로 지정된 입력값 변수 검색 공간을 최솟값 low에서 최댓값 high까지 q의 간격을 가지고 설정
- hp.uniform(label, low, high): 최솟값 low에서 최댓값 high까지 정규 분포 형태의 검색 공간 설정
- hp.loguniform(label, low, high): exp(uniform(low, high) 값을 반환하며, 반환 값의 log 변환 된 값은 정규 분포 형태를 가지는 검색 공간 설정
- hp.choice(label, options): 검색 값이 문자열 또는 문자열과 숫자값이 섞여 있을 경우 설정. Options는 리스트나 튜플 형태로 제공되며 hp.choice('tree_criterion', ['gini', 'entropy'])과 같이 설정하면 입력 변수 tree-criterion의 값을 'gini'와 'entropy'로 설정하여 입력

 

◆ 목적 함수 생성

목적 함수는 반드시 변숫값과 검색 공간을 가지는 딕셔너리를 인자로 받고, 특정 값을 반환하는 구조로 만들어져야 한다.

목적 함수의 반환값은 숫자형 단일값 외에도 딕셔너리 형태로 반환할 수 있다.

딕셔너리 형태로 반환할 경우에는 {'loss':retval, 'status':STATUS_OK} 와 같이 loss와 status 키 값을 설정해서 반환해야 한다.

from hyperopt import STATUS_OK

# 목적 함수 생성. 변숫값과 검색 공간을 가지는 딕셔너릴 인자로 받고, 특정 값 반환
def objective_func(search_space):
    x = search_space['x']
    y = search_space['y']
    retval = x**2 - 20*y

    return retval

 

◆ 최적의 입력값 찾기

입력값의 검색 공간과 목적 함수를 설정했으면 목적 함수의 반환값이 최소가 될 수 있는 최적의 입력값을 베이지안 최적화 기법에 기반하여 찾아 줘야 한다.

HyperOpt는 이러한 기능을 수행할 수 있도록 fmin(objective, space, algo, max_evals, trials) 함수 제공

[fmin() 함수 인자]
- fn: 위에서 생성한 objective_func와 같은 목적 함수
- space: 위에서 생성한 search_space와 같은 검색 공간 딕셔너리
- algo: 베이지안 최적화 적용 알고리즘. 기본적으로 tpe.suggest 이며, 이는 HyperOpt의 기본 최적화 알고리즘인 TPE(Tree of Parzen Estimator 의미)
- max_evals: 최적 입력값을 찾기 위한 입력값 시도 횟수
- trials: 최적 입력값을 찾기 위해 시도한 입력값 및 해당 입력값의 목적 함수 반환값 결과를 저장하는 데 사용.
Trials 객체는 함수의 반복 수행 시마다 입력되는 변숫값들과 함수 변환값을 속성으로 가지고 있다.

해당 객체의 주요 속성은 results와 vals가 있다.
- rstate: fmin()를 수행할 때마다 동일한 결괏값을 가질 수 있도록 설정하는 랜덤 시드 값
▶ 일반적으로 잘 적용하지 않음.

 

search_space 검색 공간에서 목적 함수 objective_func 의 최솟값을 반환하는 최적 입력 변숫값을 찾을 수 있도록 베이지안 최적화 수행

- HyperOpt의 fmin() 함수를 호출하되, 5번의 입력값 시도로 찾아낼 수 있도록 max_evals =5,

- 목적 함수인 fn 인자로는 objective_func, 검색 공간 인자인 space에는 search_space,

- 최적화 알고리즘 algo 인자로는 기본값인 tpe.suggest,

- trials의 인자값으로는 Trials() 객체, rstate는 임의의 랜덤 시드값 입력.

import numpy as np
from hyperopt import fmin, tpe, Trials
# 입력 결괏값을 저장한 Trials 객체값 생성
trial_val = Trials()

# 목적 함수의 최솟값을 반환하는 최적 입력 변수값을 5번의 입력값 시도(max_evals=5)로 찾아냄
best_01 = fmin(fn=objective_func, space=search_space, algo=tpe.suggest, max_evals=5,
               trials=trial_val, rstate=np.random.default_rng(seed=0))
print('best:', best_01)

▶ best_01 변숫값을 확인하면 x가 -4.0, y가 12.0으로 되어있다.

입력 변수 x의 공간 -10~10, y의 공간 -15~15에서 목적 함수의 반환값을 x**2 -20*y로 설정했으므로 x는 0에 가까울수록, y는 15에 가까울수록 반환값이 최소로 근사될 수 있다.

 

※ max_evals=20으로 설정하여 20번의 수행으로 어떤 최적값을 반환하는지 분석

trial_val = Trials()

# max_evals=20으로 하여 테스트 재수행
best_02 = fmin(fn=objective_func, space=search_space, algo=tpe.suggest, max_evals=20,
               trials=trial_val, rstate=np.random.default_rng(seed=0))
print('best:', best_02)

▶ 20회 반복 시 x는 2로, y는 15로 목적 함수의 최적 최솟값을 근사할 수 있는 결과 도출.

입력값 x가 -10~10까지 21개의 경우의 수, y가 -15~15까지 31개의 경우의 수를 가질 수 있기에, 만일 Grid Search와 같이 순차적으로 x, y 변숫값을 입력해서 최소 함수 반환값을 찾는다면 최대 651(21*31) 회의 반복이 필요할 수도 있는데 반해, 베이지안 최적화를 이용해서는 20회의 반복만으로 일정 수준의 최적값을 근사해낼 수 있다.

 

즉, 베이지안 최적화 방식으로 상대적으로 최적 값을 찾는 시간을 많이 줄여줄 수 있다는 것을 알 수 있다.


Trials 객체의 주요 속성 - results, vals

 

Trials 객체 속성 중 result는 함수의 반복 수행 시마다 반환되는 반환값을, vals는 반복 수행 시마다 입력되는 입력값을 가진다.

results는 파이썬 리스트 형태이며 리스트 내의 개별 원소는 {'loss':함수 반환값, 'status':반환 상태값}과 같은 딕셔너리로 가지고 있다.

# fmin() 인자로 들어가는 Trails 객체의 results 속성에 파이썬 리스트로 목적 함수 반환값들이 저장됨
# 리스트 내부의 개별 원소는 {'loss':함수 반환값, 'status':반환 상태값}와 같은 딕셔너리
print(trial_val.results)

▶ max_eval=20으로 fmin() 함수는 20회의 반복 수행을 했으므로 results 속성은 loss와 status를 키 값으로 가지는 20개의 딕셔너리를 개별 원소를 가지는 리스트로 구성되어 있음을 알 수 있다.

 

vals 속성은 딕셔너리 형태로 값을 가진다. {'입력변수명': 개별 수형 시마다 입력된 값의 리스트}와 같은 형태로 가지고 있다.

# Trials 객체의 vals 속성에 {'입력변수명': 개별 수형 시마다 입력된 값의 리스트} 형태로 저장
print(trial_val.vals)

▶ 입력 변수 x와 y를 키 값으로 가지며, x와 y키 값의 밸류는 20회의 반복 수행 시마다 사용되는 입력값들을 리스트 형태로 가지고 있는 것을 알 수 있다.

 

그러나 이 값들을 그대로 보기보다는 DataFrame으로 만들어서 좀 더 직관적으로 값을 확인해 보도록 한다.

- 입력 변수 x 값은 x 칼럼으로 생성한 뒤 vals 속성의 x 키값에 해당하는 밸류를 칼럼값으로 생성

- 입력 변수 y 값은 y 칼럼으로 생성한 뒤 vals 속성의 y 키값에 해당하는 밸류를 칼럼값으로 생성

- 함수 반환 값은 loss 칼럼으로 생성하고 results 속성에서 loss 키값에 해당하는 밸류들을 추출하여 칼럼값으로 생성

import pandas as pd

# results에서 loss 키 값에 해당하는 밸류들을 추출하여 list로 생성
losses = [loss_dict['loss'] for loss_dict in trial_val.results]

# DataFrame 생성
result_df = pd.DataFrame({'x': trial_val.vals['x'], 'y': trial_val.vals['y'], 'losses':losses})
result_df

▶ 생성된 DataFrame을 통해 좀 더 직관적으로 수행 횟수별 입력 변수 x, y와 반환값 loss를 확인할 수 있다.

 

다음에는 HyperOpt를 이용하여 ML 모델의 하이퍼 파라미터를 어떻게 최적화하는지 파악해본다.


다음 내용

 

[파이썬] 분류 > 베이지안 최적화 (2)

시작에 앞서해당 내용은 ' 권철민 지음. 위키북스' 를 토대로 작성되었습니다. 보다 자세한 내용은 해당 서적에 상세히 나와있으니 서적을 참고해 주시기 바랍니다.이전 내용

puppy-foot-it.tistory.com

 

728x90
반응형