Machine Learning - RandomizedSearchCV, GridSearchCV 정리, 실습, 최적의 하이퍼 파라미터 구하기(Optimal hyper parameters)

David's Data Science·2021년 8월 24일
1

RandomizedSearchCV란?

분류기(Esimator)를 결정하고 해당 분류기의 최적의 하이퍼 파라미터를 찾기 위한 방법 중 하나이다.
주어진 문제에 대한 분류기들로 모델을 작성한 뒤, 성능 개선을 위한 Tuning을 하는데 일일히 모든 파라미터를 다 조율해보고, 그에 맞는 최적의 조합을 찾아보긴 힘들기 때문에, 오차값이 가장 적은 하이퍼파라미터를 찾아주는 좋은 라이브러리이다.
CV라는 이름과 같이, 자체적으로 Cross Validation도 진행하여 가장 검증된 하이퍼 파라미터 값을 얻을 수 있다.

특징:

튜닝하고싶은 파라미터를 지정하여 파라미터 값의 범위를 정하고, n_iter값을 지정하여 해당 수 만큼 Random하게 조합하여 반복하는 과정을 거쳐 최종적인 최적 파라미터 값을 가진다.

예시 모델 작성:

import pandas as pd

# 데이터셋 불러오기
df = pd.read_csv('WA_Fn-UseC_-Telco-Customer-Churn.csv')
df

RandomForest로 하이퍼파라미터 조정에 의한 성능개선 예시를 보기 위해 Kaggle의 통신사 회원 데이터를 불러왔다. 타겟은 최근 한달이내 탈퇴했는지의 여부를 파악하는 'Churn'이다.

# 타겟 설정 - 회원 탈퇴 여부
target = 'Churn'

# 타겟의 분포 확인
df[target].value_counts(normalize = True)
[output]

No     0.73463
Yes    0.26537
Name: Churn, dtype: float64

탈퇴하지 않은 회원이 73%, 탈퇴한 회원이 26%로 불균형한 클래스인 것으로 확인되어 class_weight = 'balanced'로 공통적으로 입력한 뒤, 파라미터 튜닝이 없는 기본 랜덤포레스트 모델작성을 진행해본다.



파라미터 조정 없이 RandomForest 작성

from sklearn.model_selection import train_test_split
from category_encoders import OrdinalEncoder
from sklearn.pipeline import make_pipeline
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import f1_score, confusion_matrix

train, test = train_test_split(df, test_size = 0.4, random_state = 2)
features = df.drop(columns = target).columns

X_train = train[features]
y_train = train[target]
X_test = test[features]
y_test = test[target]


#파이프라인 생성 - 인코더 / 분류기 한번에 넣기
pipe = make_pipeline(
    OrdinalEncoder(),  # 수치형으로 따로 고려없이 변경진행
    RandomForestClassifier(class_weight= 'balanced') # class weight만 blanced로 넣어 진행해본다.
)

# train set에 학습
pipe.fit(X_train, y_train)

# 정확도 점검
print('검증 정확도:',pipe.score(X_test, y_test))
y_test_pred = pipe.predict(X_test)
print('F1:',f1_score(y_test, y_test_pred, pos_label = 'Yes'))
[output]

검증 정확도: 0.7892122072391767
F1: 0.4807692307692308

검증 정확도는 78로 그럭저럭 나온것 처럼 보이지만, f1_score는 0.5도 안되는 낮은 점수이다. 이를 높이기 위해 RandomizedSearchCV를 통해 최적의 하이퍼 파라미터를 찾아보도록 한다.



RandomizedSearchCV를 통해 최적 파라미터 찾는 모델 작성

%%time
from sklearn.model_selection import RandomizedSearchCV


pipe = make_pipeline(
    OrdinalEncoder(),  
    RandomForestClassifier( class_weight= 'balanced')
)

# 최적값을 구하고 싶은 파라미터를 정리 
dists = {
    'randomforestclassifier__max_depth' : [3,5,10,15],
    'randomforestclassifier__max_features' : [3,5,10],
    'randomforestclassifier__n_estimators' : [80, 100, 150, 200]
}

# RandomizedSearchCV 작성
clf1= RandomizedSearchCV(
    pipe,
    param_distributions=dists, # 파라미터 입력
    n_iter = 500,   # random search 탐색 횟수
    cv = 5,        # cv 검증을 위한 분할 검증 횟수
    scoring='accuracy',  # 오차 평가방법
    verbose=1,     # 진행상황
    random_state = 2
  )

clf1.fit(X_train, y_train)
[output]
Fitting 5 folds for each of 48 candidates, totalling 240 fits
CPU times: user 2min 54s, sys: 767 ms, total: 2min 55s
Wall time: 2min 55s
  • 위와같이 pipe를 넣은 경우는 각 요소(Encoder 또는 Estimator 등)에 맞는 하이퍼파라미터 값을 설정한 뒤 RandomizedSearchCV를 진행한다. n_iter, cv값과 함께 찾고자 하는 파라미터값이 많을 수록 시간이 점점 늘어나게 된다.
  • scoring을 통해서 랜덤서치를 진행할때 중점적으로 보는 오차를 설정할 수 있는데, 이는 해결해야할 문제에 따라 바꿔서 진행할 수 있다.
  • %%time을 통해서는 결과를 만드는데 소요된 시간을 확인할 수 있다. GridSearchCV와 시간효율에 따른 비교를 해보도록한다.
from sklearn.metrics import accuracy_score
print('최적 하이퍼파라미터: ', clf1.best_params_)
y_test_pred = clf1.predict(X_test)
print('검증 정확도:', clf1.best_score_)
print('F1:',f1_score(y_test, y_test_pred, pos_label = 'Yes'))
[output]

최적 하이퍼파라미터:  {'randomforestclassifier__n_estimators': 80, 'randomforestclassifier__max_features': 3,'randomforestclassifier__max_depth': 3}
검증 정확도: 0.7893491124260356
F1: 0.5123558484349258

.best_params_를 통해서 최적의 하이퍼파라미터를 체크할 수 있다. 또한 clf에는 최적의 파라미터가 들어있는 해당 분류기가 이미 들어가 있으므로 .best_score_로 scoring(위에선 accuracy) 해놓은 metric에 대한 최고 성능 값을 구할 수 있다.
RandomizedSearchCV를 통해 초점을 두었던 accuracy score에서의 성능이 엄청나게 좋아졌다고 할 순 없지만, 그보다 더 전반적인 성능을 볼 수 있는 f1_score에서 좀 더 큰 상승을 확인할 수 있었다.



GridSearchCV란?

Ranomized SearchCV와 흡사한데 파라미터 범위 내 Random selection이 아니란 점이 가장 큰 차이점이다. RandomizedSearchCV에서 n_iter를 통해 random한 시도의 수 자체를 조절 가능했지만, GridSearchCV는 범위 전체에 대한 모든 조합을 다 진행하여 최적의 파라미터를 찾는다.

특징:

성능개선 면에선 Randomized보다 더 개선된 조합을 잘 찾을 수도 있겠지만, 시간적인 면에서 크게 비효율적이다. 아무래도 모든 조합의 성능을 확인하려다보니 그런 불편한 부분이 있다. 하여 나는 사실 Randomized를 더 자주 사용하게 되는것 같다.

예시 모델 작성:

GridSearchCV를 통해 최적 파라미터 찾는 모델 작성

%%time

from sklearn.model_selection import GridSearchCV

pipe = make_pipeline(
    OrdinalEncoder(),  
    RandomForestClassifier( class_weight= 'balanced')
)

# 최적값을 구하고 싶은 파라미터를 정리 
dists = {
    'randomforestclassifier__max_depth' : [3,5,10,15],
    'randomforestclassifier__max_features' : [3,5,10],
    'randomforestclassifier__n_estimators' : [80, 100, 150, 200]
}

# RandomizedSearchCV 작성
grid= GridSearchCV(
    pipe,
    param_grid=dists, # 파라미터 입력
    cv = 5,        # cv 검증을 위한 분할 검증 횟수
    scoring='accuracy',  # 오차 평가방법
    verbose=1,     # 진행상황
  )

grid.fit(X_train, y_train)
[output]

Fitting 5 folds for each of 48 candidates, totalling 240 fits
CPU times: user 3min 1s, sys: 849 ms, total: 3min 1s
Wall time: 3min 2s

그리드서치를 랜덤서치와 똑같은 조건으로 진행했다. 랜덤서치와는 달리 n_iter가 없이 모든 조건을 다 진행해보고 최적의 파라미터를 찾는 방식이다. 시간을 보아도 근소한 차이지만 랜덤서치보다 더 오래 걸린 것을 알 수 있다. 다음으로는 성능개선이 얼마나 되었는지 보도록한다.

from sklearn.metrics import accuracy_score
print('최적 하이퍼파라미터: ', grid.best_params_)
y_test_pred = grid.predict(X_test)
print('검증 정확도:', grid.best_score_)
print('F1:',f1_score(y_test, y_test_pred, pos_label = 'Yes'))
최적 하이퍼파라미터:  {'randomforestclassifier__max_depth': 15, 'randomforestclassifier__max_features': 3, 'randomforestclassifier__n_estimators': 200}
검증 정확도: 0.7888757396449704
F1: 0.5240384615384616

하이퍼 파라미터도 바뀌었고, f1 스코어도 조금더 올라간 것을 확인할 수 있다. 검증 정확도(accuracy)는 조금 떨어지긴 했지만, random_state를 고정하지 않은 점을 고려해볼 때, f1 스코어가 오른 부분이 좀 더 우리가 필요한 성능 개선과 가까워진 것을 알 수 있다.

RandomizedSearchCV / GridSearchCV 의 적용

RandomizedSearchCV: GridSearch에 비해 시간상 효율적이다. 데이터가 많고 파라미터 튜닝이 많이 필요할 떄 효과적이다.
GridSearchCV: 데이터가 많고, 튜닝해야 할 파라미터가 많은 경우 비효율적이다. 성능 개선을 위해 모든 파라미터 조합을 진행해야할 때 사용하는 것이 적절하다.

profile
데이터 사이언티스트가 되고싶은 David입니다.

0개의 댓글