본문 바로가기
ML.DL

[ML경진대회] 안전 운전자 예측-성능 개선(2)

by 권미정 2022. 11. 25.

<musthave 머신러닝딥러닝 문제해결 전략> 8장을 참고해 실습한 내용입니다.


지난 글에서는 "Porto Seguro's Safe Driver Prediction(안전 운전자 예측)" 경진대회에 참가해서 베이스라인 모델을 만들고, 성능 개선 1: LightGBM모델을 실습하여 블로깅했었습니다.

이번에는 성능 개선 2번째 XGBoost모델부터, 마지막 앙상블까지 진행해 보겠습니다.

 

3. 성능 개선 2: XGBoost 모델

이번에는 모델만 XGBoost로 바꿔 보겠습니다. XGBoost는 성능이 우수한 트리 기반 부스팅 알고리즘으로, 결정 트리를 병렬로 배치하는 랜덤 포레스트와 달리 직렬로 배치해 사용합니다.

XGBoost 기반으로 바꾸려면 지니계수 반환값, 데이터셋 객체, 모델 하이퍼파라미터명을 수정해야 합니다.

3.1 피처 엔지니어링

피처 엔지니어링까지의 코드는 앞 절과 같은데, gini() 함수만 다릅니다.

#LightGBM용 gini() 함수
def gini(preds, dtrain):
    labels = dtrain.get_label()
    return 'gini', eval_gini(labels, preds), True

XGBoost용 지니계수 계산 함수는 반환값이 평가지표명과 평가점수 두 개입니다. 사라진 '평가점수가 높으면 좋은지 여부'는 XGBoost 모델 객체의 train() 메서드에 따로 전달하면 됩니다.

#XGBoost용 gini() 함수
def gini(preds, dtrain):
    labels = dtrain.get_label()
    return 'gini', eval_gini(labels, preds)

 

3.2 하이퍼파라미터 최적화

① 데이터셋 준비

XGBoost는 xgb.DMatrix()로 베이지안 최적화 수행용 데이터셋을 만듭니다.

import xgboost as xgb
from sklearn.model_selection import train_test_split

#8:2 비율로 훈련 데이터, 검증 데이터 분리(베이지안 최적화 수행용)
X_train, X_valid, y_train, y_valid = train_test_split(X, y, test_size=0.2, random_state=0)

#베이지안 최적화용 데이터셋
bayes_dtrain = xgb.DMatrix(X_train, y_train)
bayes_dvalid = xgb.DMatrix(X_valid, y_valid)

 

② 하이퍼파라미터 범위 설정

#베이지안 최적화를 위한 하이퍼파라미터 범위
param_bounds = {'max_depth':(4,8),
                'subsample':(0.6,0.9),
                'colsample_bytree':(0.7,1.0),
                'min_child_weight':(5,7),
                'gamma':(8,11),
                'reg_alpha':(7,9),
                'reg_lambda':(1.1, 1.5),
                'scale_pos_weight':(1.4, 1.6)}

#값이 고정된 하이퍼파라미터
fixed_params = {'objective':'binary:logistic',
                'learning_rate':0.2,
                'random_state':1991}

이진분류 문제라서 objective는 binary:logistic으로 설정했습니다.

 

③ (베이지안 최적화용) 평가지표 계산 함수 작성

베이지안 최적화에 사용하기 위한 eval_function() 함수를 살펴보겠습니다. 큰 흐름은 LightGBM용과 비슷한데, 네 가지 다른 점이 있습니다.

1. 하이퍼파라미터명

2. train() 메서드 내 검증 데이처 전달 방식

3. trian() 메서드 내 maximize 파라미터

4. predict() 메서드에 DMatrix 타입을 전달하는 점

def eval_function(max_depth, subsample, colsample_bytree, min_child_weight, 
                  reg_alpha, gamma, reg_lambda, scale_pos_weight):
    #베이지안 최적화를 수행할 하이퍼파라미터 ⓐ
    params = {'max_depth':int(round(max_depth)),
              'subsample':subsample,
              'colsample_bytree':colsample_bytree,
              'min_child_weight':min_child_weight,
              'gamma':gamma,
              'reg_alpha':reg_alpha,
              'reg_lambda':reg_lambda,
              'scale_pos_weight':scale_pos_weight}
    
    #값이 고정된 하이퍼파라미터도 추가
    params.update(fixed_params)
    
    print('하이퍼파라미터 :', params)
    
    #XGBoost 모델 훈련 ⓑ
    xgb_model = xgb.train(params=params,
                          dtrain=bayes_dtrain,
                          num_boost_round=2000,
                          evals=[(bayes_dvalid, 'bayes_dvalid')], #ⓒ
                          maximize=True, #ⓓ
                          feval=gini,
                          early_stopping_rounds=200,
                          verbose_eval=False)
    
    best_iter = xgb_model.best_iteration #최적 반복 횟수 ⓔ
    #검증 데이터로 예측 수행 ⓕ
    preds = xgb_model.predict(bayes_dvalid, #ⓖ
                              iteration_range=(0,best_iter)) #ⓗ
    
    #지니계수 계산
    gini_score = eval_gini(y_valid, preds)
    print(f'지니계수 : {gini_score}\n')
    
    return gini_score

코드를 살펴보겠습니다.

ⓐ params에 저장된 하이퍼파라미터명, ⓑ train() 메서드, ⓕ predict() 메서드에서 사용하는 파라미터명이 다릅니다.

XGBoost는 ⓔ 성능이 가장 좋을 때의 부스팅 횟수를 ⓗ 예측 시 iteration_range 파라미터로 명시해 줘야 최적 반복 횟수로 훈련된 모델을 활용해 예측합니다.

 evals는 검증 데이터를 전달받는 파라미터로, 검증 데이터와 검증 데이터 이름의 쌍을 튜플로 묶어서 전달했습니다.

 maximize 파라미터에는 True를 전달했는데, 이건 평가점수가 클수록 좋다는 뜻입니다.

ⓕ XGBoost의 predict()에는 데이터를 DMatrix 타입으로 전달해야 하기 때문에, bayes_dvalid를 전달했습니다.

 

④ 최적화 수행

from bayes_opt import BayesianOptimization

#베이지안 최적화 객체 생성
optimizer = BayesianOptimization(f=eval_function,
                                 pbounds=param_bounds,
                                 random_state=0)

#베이지안 최적화 수행
optimizer.maximize(init_points=3, n_iter=6)

 

 

 

⑤ 결과 확인

#평가함수 점수가 최대일 때 하이퍼파라미터
max_params = optimizer.max['params']
max_params

 

여기서 max_depth는 트리 깊이를 의미하는데, 정수형이어야 하기 때문에 정수형으로 바꾸고 고정 하이퍼파라미터도 추가하겠습니다.

#정수형 하이퍼파라미터 변환
max_params['max_depth'] = int(round(max_params['max_depth']))
#값이 고정된 하이퍼파라미터 추가
max_params.update(fixed_params)
max_params

 

3.3 모델 훈련 및 성능 검증

최적 하이퍼파라미터를 이용해 XGBoost 모델을 훈련해 보겠습니다.

from sklearn.model_selection import StratifiedKFold

#층화 K 폴드 교차 검증기 생성
folds = StratifiedKFold(n_splits=5, shuffle=True, random_state=1991)

#OOF 방식으로 훈련된 모델로 검증 데이터 타깃값을 예측한 확률을 담을 1차원 배열
oof_val_preds = np.zeros(X.shape[0])
#OOF 방식으로 훈련된 모델로 테스트 데이터 타깃값을 예측한 확률을 담을 1차원 배열
oof_test_preds = np.zeros(X_test.shape[0])

#OOF 방식으로 모델 훈련, 검증, 예측
for idx, (train_idx, valid_idx) in enumerate(folds.split(X,y)):
    #각 폴드를 구분하는 문구 출력
    print('#'*40, f'폴드 {idx+1} / 폴드 {folds.n_splits}', '#' * 40)
    
    #훈련용 데이터, 검증용 데이터 설정
    X_train, y_train = X[train_idx], y[train_idx]
    X_valid, y_valid = X[valid_idx], y[valid_idx]
    
    #XGBoost 전용 데이터셋 생성
    dtrain = xgb.DMatrix(X_train, y_train)
    dvalid = xgb.DMatrix(X_valid, y_valid)
    dtest = xgb.DMatrix(X_test)
    #XGBoost 모델 훈련
    xgb_model = xgb.train(params=max_params,
                          dtrain=dtrain,
                          evals=[(dvalid, 'valid')],
                          maximize=True,
                          feval=gini,
                          early_stopping_rounds=200,
                          verbose_eval=100)
    
    #모델 성능이 가장 좋을 때의 부스팅 반복 횟수 저장
    best_iter = xgb_model.best_iteration
    #테스트 데이터를 활용해 OOF 예측
    oof_test_preds += xgb_model.predict(dtest, iteration_range=(0,best_iter)) / folds.n_splits
    
    #모델 성능 평가를 위한 검증 데이터 타깃값 예측
    oof_val_preds[valid_idx] += xgb_model.predict(dvalid, iteration_range=(0, best_iter))
    
    #검증 데이터 예측 확률에 대한 정규화 지니계수
    gini_score = eval_gini(y_valid, oof_val_preds[valid_idx])
    print(f'폴드 {idx+1} 지니계수 : {gini_score}\n')

 

 

OOF 검증 데이터 지니계수를 출력해 봅시다.

print('OOF 검증 데이터 지니계수 :', eval_gini(y, oof_val_preds))

 

 

3.4 예측 및 결과 제출

최종 예측 확률을 제출해서 프라이빗 점수를 살펴보겠습니다.

submission['target'] = oof_test_preds
submission.to_csv('submission.csv')

 

4. 성능 개선 3: LightGBM과 XGBoost 앙상블

두 모델의 예측값을 결합해서 더 좋은 점수를 얻을 수 있는데, 이 방식을 앙상블이라고 합니다. 앙상블 기법으로 점수를 높여 보겠습니다.

4.1 앙상블 수행

LightGBM으로 예측한 확률값을 oof_test_preds_lgb라 하고, XGBoost로 예측한 확률값을 oof_test_preds_xgb라고 하겠습니다. 각각 공평하게 50%씩의 가중치를 주어서 구한 가중평균을 최종 예측 확률로 정하겠습니다.

oof_test_preds = oof_test_preds_lgb * 0.5 + oof_test_preds_xgb * 0.5

 

4.2 예측 및 결과 제출

제출용 파일을 만들고 커밋 후 제출해서 점수가 어떻게 달라지는지 살펴보겠습니다.

submission['target'] = oof_test_preds
submission.to_csv('submission.csv')

 

 

이렇게 베이스라인 모델에서 성능 개선 3가지를 통해 프라이빗 점수를 올려 보았습니다!

댓글