머신러닝

[4장] 파이썬 머신러닝 완벽 가이드_분류_3

zsun 2023. 6. 2. 09:53

06. XGBoost(eXtra Gradient Boost)

 

XGBoost

  • XGBoost는 트리 기반의 앙상블 학습에서 각광받고 있는 알고리즘 중 하나
  • GBM에 기반하고 있지만, GBM의 단점인 느린 수행 시간 및 과적합 규제 부재 등의 문제 해결 가능
  • CPU 환경에서 병렬학습이 가능해서 기존 GBM보다 빠르게 학습 완료 가능

 

[주요 장점]
1. 뛰어난 예측 성능
2. GBM 대비 빠른 수행시간
3. 과적합 규제
4. Tree pruning : 더 이상 긍정 이득이 없는 분할을 가지치기 해서 분할 수를 더 줄임
5. 자체 내장된 교차 검증 : 지정된 반복 횟수가 아니라 교차 검증을 통해 평가 데이터셋의 평가값이
최적화 되면 반복을 중간에 멈출 수 있는 조기 중단 기능이 있음
6. 결손값 자체 처리

 

- XGBoost 프레임워크 기반의 XGBoost을 파이썬 래퍼 XGBoost 모듈
- 사이킷런과 연동되는 모듈을 사이킷런 래퍼 XGBoost 모듈

 

파이썬 래퍼 XGBoost 의 하이퍼 파라미터

- 일반 파라미터 : 일반적으로 실행 시 스레드의 개수나 silent 모드 등의 선택을 위한 파라미터로서 디폴트 파라미터 값을 바꾸는 경우는 거의 없음
- 부스터 파라미터 :  트리 최적화, 부스팅, regularization 등과 관련 파라미터 등을 지칭함
- 학습 테스크 파라미터 : 학습 수행 시의 객체 함수, 평가를 위한 지표 등을 설정하는 파라미터

 

XGBoost는 지정한 부스팅 반복 횟수에 도달하지 않더라도 예측 오류가 더이상 개선되지 않으면 반복을 끝까지 수행하지 않고 중지함

 

파이썬 래퍼 XGBoost 적용 – 위스콘신 유방암 예측

import xgboost as xgb
from xgboost import plot_importance
import pandas as pd
import numpy as np
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
import warnings
warnings.filterwarnings('ignore')

dataset = load_breast_cancer()
X_features= dataset.data
y_label = dataset.target

cancer_df = pd.DataFrame(data=X_features, columns=dataset.feature_names)
cancer_df['target']= y_label
cancer_df.head(3)
print(dataset.target_names)
print(cancer_df['target'].value_counts())
['malignant' 'benign']
1    357
0    212
Name: target, dtype: int64
# cancer_df에서 feature용 DataFrame과 Label용 Series 객체 추출
# 맨 마지막 칼럼이 Label임. Feature용 DataFrame은 cancer_df의 첫번째 칼럼에서 맨 마지막 두번째 칼럼까지를 :-1 슬라이싱으로 추출.
X_features = cancer_df.iloc[:, :-1]
y_label = cancer_df.iloc[:, -1]

# 전체 데이터 중 80%는 학습용 데이터, 20%는 테스트용 데이터 추출
X_train, X_test, y_train, y_test=train_test_split(X_features, y_label,
                                         test_size=0.2, random_state=156 )

# 위에서 만든 X_train, y_train을 다시 쪼개서 90%는 학습과 10%는 검증용 데이터로 분리
# 검증용 데이터셋을 분리하는 이유는 XGBoost가 제공하는 기능인 검증 성능 평가와 조기 중단 수행위함
X_tr, X_val, y_tr, y_val= train_test_split(X_train, y_train, test_size=0.1, random_state=156 )
print(X_train.shape , X_test.shape)
print(X_tr.shape, X_val.shape)
(455, 30) (114, 30)
(409, 30) (46, 30)

 

* 파이썬 래퍼 xgboost이 사이킷런과 다른점 : xgboost 만의 전용 데이터 객체인 DMatrix를 사용한다는 점
- 넘파이 외에도 데이터프레임과 시리즈 기반으로도 dmatrix 생성 가능
- DMatrix의 주요 입력 파라미터는 data와 label
- data는 피처 데이터셋, label은 분류의 경우에는 레이블 데이터셋, 회귀의 경우에는 숫자형 종속값 데이터셋

 

# 학습, 검증, 테스트용 DMatrix를 생성.
dtr = xgb.DMatrix(data=X_tr, label=y_tr)
dval = xgb.DMatrix(data=X_val, label=y_val)
dtest = xgb.DMatrix(data=X_test , label=y_test)
# xgboost의 하이퍼 파라미터 설정
# 주로 딕셔너리 형태로 입력
params = { 'max_depth':3, #최대깊이 : 3
          'eta': 0.05, # 학습률 eta=0.1
          'objective':'binary:logistic',# 예제 데이터가 0또는1 이진 분류이므로 목적함수(objective)는 이진 로지스틱
          'eval_metric':'logloss' # 오류 함수의 평가 성능 지표는 logloss
         }
num_rounds = 400 #부스팅 반복횟수

파이썬 래퍼 xgboost는 하이퍼 파라미터를 xgboost 모듈의 train()함수에 파라미터로 전달
조기 중단 기능 제공

- xgboost의 train() 함수에 early_stopping_rounds 파라미터 입력
- 조기 중단 성능 평가는 주로 별도의 검증 데이터셋 사용
- xgboost 는 학습 반복 시마다 검증 데이터셋을 이용해 성능을 평가할 수 있는 기능 제공
- early_stopping_rounds 파라미터를 설정해 조기 중간을 수행하기 위해서는 반드시 평가용 데이터셋 지정과 eval_matric을 함께 설정해야함
- xgboost는 반복마다 지정된 평가용 데이터셋에서 eval_metric의 지정된 평가 지표로 예측 오류를 측정

# 학습 데이터 셋은 'train' 또는 평가 데이터 셋은 'eval' 로 명기합니다. 
eval_list = [(dtr,'train'),(dval,'eval')] # 또는 eval_list = [(dval,'eval')] 만 명기해도 무방. 

# 하이퍼 파라미터와 early stopping 파라미터를 train( ) 함수의 파라미터로 전달
xgb_model = xgb.train(params = params , dtrain=dtr , num_boost_round=num_rounds , \
                      early_stopping_rounds=50, evals=eval_list )
[0]	train-logloss:0.65016	eval-logloss:0.66183
[1]	train-logloss:0.61131	eval-logloss:0.63609
[2]	train-logloss:0.57563	eval-logloss:0.61144
.
.
.
[173]	train-logloss:0.01285	eval-logloss:0.26253
[174]	train-logloss:0.01278	eval-logloss:0.26229
[175]	train-logloss:0.01267	eval-logloss:0.26086

500번 반복하지 않고 176번째 반복에서 완료함

 

# XGBoost를 이용해 모델의 학습이 완료되면 이를 이용해 테스트 데이터셋에 예측 수행
XGBoost는 train() 함수를 호출해 학습이 완료된 모델 객체 반환 : predict() 사용
사이킷런의 predict() 메서드는 예측 결과 클래스 값 (0,1)을 반환하지만,
xgboost의 predict()는 예측 결과를 추정할 수 있는 확률 값 반환

pred_probs = xgb_model.predict(dtest)
print('predict( ) 수행 결과값을 10개만 표시, 예측 확률 값으로 표시됨')
print(np.round(pred_probs[:10],3))

# 예측 확률이 0.5 보다 크면 1 , 그렇지 않으면 0 으로 예측값 결정하여 List 객체인 preds에 저장 
preds = [ 1 if x > 0.5 else 0 for x in pred_probs ]
print('예측값 10개만 표시:',preds[:10])
predict( ) 수행 결과값을 10개만 표시, 예측 확률 값으로 표시됨
[0.845 0.008 0.68  0.081 0.975 0.999 0.998 0.998 0.996 0.001]
예측값 10개만 표시: [1, 0, 1, 0, 1, 1, 1, 1, 1, 0]

# get_clf_eval 로 오차행렬, 정확도, 정밀도, 재현율, F1스코어 구하기

from sklearn.metrics import confusion_matrix, accuracy_score
from sklearn.metrics import precision_score, recall_score
from sklearn.metrics import f1_score, roc_auc_score

def get_clf_eval(y_test, pred=None, pred_proba=None):
    confusion = confusion_matrix( y_test, pred)
    accuracy = accuracy_score(y_test , pred)
    precision = precision_score(y_test , pred)
    recall = recall_score(y_test , pred)
    f1 = f1_score(y_test,pred)
    # ROC-AUC 추가 
    roc_auc = roc_auc_score(y_test, pred_proba)
    print('오차 행렬')
    print(confusion)
    # ROC-AUC print 추가
    print('정확도: {0:.4f}, 정밀도: {1:.4f}, 재현율: {2:.4f},\
    F1: {3:.4f}, AUC:{4:.4f}'.format(accuracy, precision, recall, f1, roc_auc))
get_clf_eval(y_test , preds, pred_probs)
오차 행렬
[[34  3]
 [ 2 75]]
정확도: 0.9561, 정밀도: 0.9615, 재현율: 0.9740,    F1: 0.9677, AUC:0.9937

 

# 피처 중요도 시각화하기

- xgboost 패키지에 내장된 시각화 기능 수행 : plot_importance()

- f스코어는 해당 피처가 트리 분할 시 얼마나 자주 사용되었는지 지표로 나타낸 값
- 이용시 유의할 점은 xgboost를 DataFrame이 아닌 넘파이 기반의 피처 데이터로 학습 시
- 넘파이에서 피처명을 제대로 알 수가 없으므로 y축의 피처명을 나열시 f0, f1과 같이 숫자 붙여 나타냄

import matplotlib.pyplot as plt
%matplotlib inline

fig, ax = plt.subplots(figsize=(10, 12))
plot_importance(xgb_model, ax=ax)
plt.savefig('p239_xgb_feature_importance.tif', format='tif', dpi=300, bbox_inches='tight')

 

 

사이킷런 래퍼 XGBoost

 

  • 사이킷런의 기본 Estimator를 그대로 상속해 만들었기 때문에 다른 Estimator와 동일하게 fit()과 predict()만으로 학습과 예측이 가능함
  • GridSearchCV, pipeline 등 사이킷런의 다른 유틸리티를 그대로 사용할 수 있기 때문에 기존의 다른 머신러닝 알고리즘으로 만들어놓은 프로그램이 있더라도 알고리즘 클래스만 xgboost로 바꿔주면 사용 가능해짐

  • 두가지 유형
    - 분류를 위한 래퍼 클래스인 XGBClassifier
    - 회귀를 위한 래퍼 클래스인 XGBRegressor

  • 사이킷런의 하이퍼 파라미터와 호환성 유지를 위해 기존 xgboost에서 사용하던 네이티브 하이퍼 파라미터 변경함
    eta -> learning_rate
    sub_sample -> subsample
    lambda -> reg_lambda
    alpha -> reg_alpha

 

# 위스콘신 유방암 데이터셋 분류를 XGBClassifier를 이용해 예측

# 사이킷런 래퍼 XGBoost 클래스인 XGBClassifier 임포트
from xgboost import XGBClassifier

xgb_wrapper = XGBClassifier(n_estimators=400, learning_rate=0.1, max_depth=3)
xgb_wrapper.fit(X_train, y_train)
w_preds = xgb_wrapper.predict(X_test)
w_pred_proba = xgb_wrapper.predict_proba(X_test)[:, 1]
get_clf_eval(y_test , w_preds, w_pred_proba)
오차 행렬
[[35  2]
 [ 1 76]]
정확도: 0.9737, 정밀도: 0.9744, 재현율: 0.9870,    F1: 0.9806, AUC:0.9951

 

# 조기중단 수행해보기

 

[조기 중단 관련 파라미터]
- 평가 지표가 향상될 수 잇는 반복 횟수를 정의하는 early_stopping_rounds : 100
- 조기 중단을 위한 평가 지표인 eval_metric : logloss
- 성능 평가를 수행할 데이터 셋인 *eval_set
(*eval_set : 파이썬 래퍼일 때와 다르게 학습과 검증을 의미하는 문자열을 넣어주지 않아도 됨)
- [(X_tr,y_tr),(X_val,y_val)] 와 같이 지정하면 맨 앞의 튜플이 학승용 데이터, 뒤의 튜플이 검증용 데이터로 자동 인식

 

from xgboost import XGBClassifier

xgb_wrapper = XGBClassifier(n_estimators=400, learning_rate=0.05, max_depth=3)
evals = [(X_tr,y_tr),(X_val,y_val)]
xgb_wrapper.fit(X_tr, y_tr, early_stopping_rounds=50, eval_metric="logloss", 
                eval_set=evals, verbose=True)

ws100_preds = xgb_wrapper.predict(X_test)
ws100_pred_proba = xgb_wrapper.predict_proba(X_test)[:, 1]
[0]	validation_0-logloss:0.65016	validation_1-logloss:0.66183
[1]	validation_0-logloss:0.61131	validation_1-logloss:0.63609
[2]	validation_0-logloss:0.57563	validation_1-logloss:0.61144
.
.
.
[174]	validation_0-logloss:0.01278	validation_1-logloss:0.26229
[175]	validation_0-logloss:0.01267	validation_1-logloss:0.26086
[176]	validation_0-logloss:0.01258	validation_1-logloss:0.26103

# 평가지표 산출

get_clf_eval(y_test , ws100_preds, ws100_pred_proba)
오차 행렬
[[34  3]
 [ 2 75]]
정확도: 0.9561, 정밀도: 0.9615, 재현율: 0.9740,    F1: 0.9677, AUC:0.9933

 

 

07. lightGBM

 

lightGBM

  • lightGBM은 XGBoost와 함께 부스팅 계열 알고리즘에서 가장 각광 받음
  • 시간이 짧게 걸림, 메모리 사용량도 상대적으로 적음
  • 예측 성능에는 다른것들과 크게 다르지 않음
  • 카테고리형 피처의 자동 변화과 최적 분할(원-핫 인코딩 등을 사용하지 않고도 카테고리형 피처를 최적으로 변환하고 이에 따른 노드 분할 수행)
  • 단점은 적은 데이터셋(일반적으로 10,000건 이하)에 적용할 경우 과적합이 발생하기 쉬움

  • LightGBM은 일반 GBM 계열의 트리 분할 방법과 다르게 리프 중심 트리 분할(Leaf wise) 방식 사용
  • 기존의 대부분 트리 기반 알고리즘은 트리 깊이를 효과적으로 줄이기 위한 균형 트리 분할(Level wise)
    - 최대한 균형 잡한 트리를 유지하면서 분할하기 때문에 트리의 깊이가 최소화 될수있기 때문
    - 오버피팅에 보다 더 강한 구조를 가질 수 있다고 알려져 있음
    - 반대로 균형을 맞추기 위한 시간이 필요하기는 함

  • LightGBM의 리프 중심 트리 분할 방식은 트리의 균형을 맞추지 않고,
    최대 손실 값(max delta loss)을 가지는 리프 노드를 지속적으로 분할하면서 트리의 깊이가 깊어지고 비대칭적 구조 가짐
    -> 이렇게 최대 손실값을 가지는 리프 노드를 지속적으로 분할해 생성된 규칙 트리는 학습을 반복할수록 결국은 균형 트리 분할 방식보다 예측 오류 손실을 최소화할 수 있음

lightGBM 하이퍼 파라미터

 

xgboost와 비슷하지만 리프 노드가 계속 분할되면서 트리의 깊이가 깊어지므로 이러한 트리 특성에 맞는 하이퍼 파라미터 설정이 필요 (ex. max_depth를 매우 크게 가짐)

 

하이퍼 파라미터 튜닝 방안

  • num_leaves의 개수를 중심으로 min_child_samples(min_data_in_leaf), max_depth를 함께 조정하면서 모델의 복잡도를 줄이는 것이 기본 튜닝 방안
  • num_leaves는 개별 트리가 가질 수 있는 최대 리프의 개수이고 lightGBM 모델의 복잡도를 제어하는 주요 파라미터임
  • 일반적으로 mun_leaves의 개수를 높이면 정확도가 높아지ㅣㅈ만, 반대로 트리의 깊이가 깊어지고 모델이 복잡도가 커져 과적합 영향도가 커짐
  • min_data_in_leaf 는 사이킷런 래퍼 클래스에서는 min_child_samples로 이름이 바뀜
  • 과적합을 개선하기 위한 중요한 파라미터임, 보통 큰 값으로 설정하면 트리가 깊어지는 것을 방지해줌
  • max_depth는 명시적으로 깊이의 크기를 제한함, 위 파라미터들과 결합하여 과적합을 개선하는데 사용
  • learning_rate 를 작게 하면서 n_estimators를 크게 하는 것은 부스팅 계열 튜닝에서 가장 기본적인 튜닝
  • n_estimators를 너무 크게 하면 과적합 발생할 수 있음
  • 과적합을 제어하기 위해 reg_lambda, reg_alpha와 같은 regularization을 적용하거나 학습 데이터에 사용할 피처의 개수나 데이터 샘플링 레코드 개수를 줄이기 위해 colsample_bytree, subsample 파라미터 적용 가능

 

LightGBM 적용 - 위스콘신 유방암 예측

# LightGBM의 파이썬 패키지인 lightgbm에서 LGBMClassifier 임포트
from lightgbm import LGBMClassifier

import pandas as pd
import numpy as np
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split

dataset = load_breast_cancer()

cancer_df = pd.DataFrame(data=dataset.data, columns=dataset.feature_names)
cancer_df['target']= dataset.target
X_features = cancer_df.iloc[:, :-1]
y_label = cancer_df.iloc[:, -1]

# 전체 데이터 중 80%는 학습용 데이터, 20%는 테스트용 데이터 추출
X_train, X_test, y_train, y_test=train_test_split(X_features, y_label, test_size=0.2, random_state=156 )

# 위에서 만든 X_train, y_train을 다시 쪼개서 90%는 학습과 10%는 검증용 데이터로 분리
X_tr, X_val, y_tr, y_val= train_test_split(X_train, y_train, test_size=0.1, random_state=156 )

# 앞서 XGBoost와 동일하게 n_estimators는 400 설정.
lgbm_wrapper = LGBMClassifier(n_estimators=400, learning_rate=0.05)

# LightGBM도 XGBoost와 동일하게 조기 중단 수행 가능.
evals = [(X_tr, y_tr), (X_val, y_val)]
lgbm_wrapper.fit(X_tr, y_tr, early_stopping_rounds=50, eval_metric="logloss", eval_set=evals, verbose=True)
preds = lgbm_wrapper.predict(X_test)
pred_proba = lgbm_wrapper.predict_proba(X_test)[:, 1]
Early stopping, best iteration is:
[61]	training's binary_logloss: 0.0532381	valid_1's binary_logloss: 0.260236
from sklearn.metrics import confusion_matrix, accuracy_score
from sklearn.metrics import precision_score, recall_score
from sklearn.metrics import f1_score, roc_auc_score

def get_clf_eval(y_test, pred=None, pred_proba=None):
    confusion = confusion_matrix( y_test, pred)
    accuracy = accuracy_score(y_test , pred)
    precision = precision_score(y_test , pred)
    recall = recall_score(y_test , pred)
    f1 = f1_score(y_test,pred)
    # ROC-AUC 추가 
    roc_auc = roc_auc_score(y_test, pred_proba)
    print('오차 행렬')
    print(confusion)
    # ROC-AUC print 추가
    print('정확도: {0:.4f}, 정밀도: {1:.4f}, 재현율: {2:.4f},\
    F1: {3:.4f}, AUC:{4:.4f}'.format(accuracy, precision, recall, f1, roc_auc))
get_clf_eval(y_test, preds, pred_proba)
오차 행렬
[[34  3]
 [ 2 75]]
정확도: 0.9561, 정밀도: 0.9615, 재현율: 0.9740,    F1: 0.9677, AUC:0.9877

 

# plot_importance( )를 이용하여 feature 중요도 시각화

from lightgbm import plot_importance
import matplotlib.pyplot as plt
%matplotlib inline

fig, ax = plt.subplots(figsize=(10, 12))
plot_importance(lgbm_wrapper, ax=ax)
plt.savefig('lightgbm_feature_importance.tif', format='tif', dpi=300, bbox_inches='tight')