파이썬 머신러닝 완벽 가이드 (권철민 저)을 요약정리했습니다.
LightGBM
LightGBM
은 XGBoost 보다 학습에 걸리는 시간도 훨씬 짧고, 메모리 사용량도 적은 모델이다.LightGBM
의 Light
는 이러한 장점 때문에 붙여졌다.
LightGBM은 리프 중심 트리 분할 방식으로 트리의 균형을 맞추지 않고,
최대 손실값을 가지는 리프 노드를 지속적으로 분할하면서 트리의 깊이가 깊어지고, 비대칭적인 규칙 트리가 생성 된다.
이렇게 최대 손실값을 가지는 리프 노드를 지속적으로 분할해 생성된 규칙 트리는 학습을 반복할수록 결국은 균형 트리 분할 방식보다 예측 오류 손실을 최소화 할 수 있다는 것이 LightGBM의 구현 사상이다.
LightGBM의 XGBoost 대비 장점
- XGBoost 대비 더 빠른 학습과 예측 수행 시간
- 더 적은 메모리 사용량
- 카테고리형 피처의 자동 변환과 최적 분할
- 원-핫 인코딩 등을 사용하지 않고도 카테고리형 피처를 최적으로 변환하고 이에 따른 노드 분할 수행.
LightGBM의 하이퍼 파라미터
하이퍼 파라미터 튜닝 방안
num_leaves의 개수를 중심으로 min_child_samples
, max_depth
를 함께 조정하면서 모델의 복잡도를 줄이는 것이 기본 튜닝 방안이다.
- num_leaves는 개별 트리가 가질 수 있는 최대 리프의 개수이고 LightGBM 모델의 복잡도를 제어하는 주요 파라미터이다. 일반적으로 num_leaves의 개수를 높이면 정확도가 높아지지만, 반대로 트리의 깊이가 깊어지고 모델의 복잡도가 커서 과적합이 될 우려가 증가한다.
- min_child_samples는 과적합을 개선하기 위한 중요한 파라미터이다. num_leaves와 학습 데이터의 크기에 따라 달라지지만, 보통 큰 값으로 설정하면 트리가 깊어지는 것을 방지 한다.
- max_depth는 명시적으로 깊이의 크기를 제한한다. 과적합 방지에 활용한다.
learning_rate를 작게 하면서 n_estimators를 크게 하는 것은 부스팅 계열 튜닝에서 가장 기본적인 튜닝 방안이므로 이를 적용하는 것도 좋다.
물론 n_estimators를 너무 크게 하는 것은 과적합으로 오히려 성능이 저하 될 수 있다.
이밖에 과적합을 제어하기 위해서 reg_lambda, reg_alpha와 같은 규제를 적용하거나 학습 데이터에 사용할 피처의 개수나 데이터 샘플링 레코드 개수를 줄이기 위해 colsample_bytres, subsample 파라미터를 적용할 수 있다.
LightGBM 실습 – 위스콘신 유방암 예측
LightGBM을 이용해 유방암 예측을 해보자. LightGBM도 early stopping
기능 을 가지고 있는데 그것을 활용 한다.
# 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()
ftr = dataset.data
target = dataset.target
# 전체 데이터 중 80%는 학습용 데이터, 20%는 테스트용 데이터 추출
X_train, X_test, y_train, y_test=train_test_split(ftr, target, test_size=0.2, random_state=156 )
# 앞서 XGBoost와 동일하게 n_estimators는 400 설정.
lgbm_wrapper = LGBMClassifier(n_estimators=400)
# LightGBM도 XGBoost와 동일하게 조기 중단 수행 가능.
evals = [(X_test, y_test)]
lgbm_wrapper.fit(X_train, y_train, early_stopping_rounds=100, eval_metric="logloss",
eval_set=evals, verbose=True)
preds = lgbm_wrapper.predict(X_test)
pred_proba = lgbm_wrapper.predict_proba(X_test)[:, 1]
[1] valid_0's binary_logloss: 0.565079 valid_0's binary_logloss: 0.565079
Training until validation scores don't improve for 100 rounds.
[2] valid_0's binary_logloss: 0.507451 valid_0's binary_logloss: 0.507451
[3] valid_0's binary_logloss: 0.458489 valid_0's binary_logloss: 0.458489
[4] valid_0's binary_logloss: 0.417481 valid_0's binary_logloss: 0.417481
[5] valid_0's binary_logloss: 0.385507 valid_0's binary_logloss: 0.385507
[6] valid_0's binary_logloss: 0.355846 valid_0's binary_logloss: 0.355846
[7] valid_0's binary_logloss: 0.330897 valid_0's binary_logloss: 0.330897
[8] valid_0's binary_logloss: 0.306923 valid_0's binary_logloss: 0.306923
[9] valid_0's binary_logloss: 0.28776 valid_0's binary_logloss: 0.28776
[10] valid_0's binary_logloss: 0.26917 valid_0's binary_logloss: 0.26917
...
[133] valid_0's binary_logloss: 0.179856 valid_0's binary_logloss: 0.179856
[134] valid_0's binary_logloss: 0.179251 valid_0's binary_logloss: 0.179251
[135] valid_0's binary_logloss: 0.18315 valid_0's binary_logloss: 0.18315
[136] valid_0's binary_logloss: 0.184656 valid_0's binary_logloss: 0.184656
[137] valid_0's binary_logloss: 0.187475 valid_0's binary_logloss: 0.187475
[138] valid_0's binary_logloss: 0.188721 valid_0's binary_logloss: 0.188721
[139] valid_0's binary_logloss: 0.188542 valid_0's binary_logloss: 0.188542
[140] valid_0's binary_logloss: 0.18817 valid_0's binary_logloss: 0.18817
[141] valid_0's binary_logloss: 0.185899 valid_0's binary_logloss: 0.185899
[142] valid_0's binary_logloss: 0.185452 valid_0's binary_logloss: 0.185452
[143] valid_0's binary_logloss: 0.186084 valid_0's binary_logloss: 0.186084
[144] valid_0's binary_logloss: 0.185302 valid_0's binary_logloss: 0.185302
[145] valid_0's binary_logloss: 0.187856 valid_0's binary_logloss: 0.187856
[146] valid_0's binary_logloss: 0.190334 valid_0's binary_logloss: 0.190334
[147] valid_0's binary_logloss: 0.192769 valid_0's binary_logloss: 0.192769
Early stopping, best iteration is:
[47] valid_0's binary_logloss: 0.126108 valid_0's binary_logloss: 0.126108
조기 중단으로 147번 반복까지만 수행하고 학습을 종료했다.
이제 학습된 LighGBM모델을 기반으로 예측 성능을 평가해 보자.
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)
오차 행렬
[[33 4]
[ 2 75]]
정확도: 0.9474, 정밀도: 0.9494, 재현율: 0.9740, F1: 0.9615, AUC:0.9926
정확도가 약 94.74% 입니다.
앞 예제에서 XGBoost 인 경우 96.64% 보다는 작지만, 워낙 학습 데이터 셋과 테스트 데이터 셋 크기가 작아서 알고리즘간 성능 비교는 큰 의미가 없습니다.
LightGBM 파이썬 패키지인 lightgbm은 XGBosst 파이썬 패키지 와 동일하게 피처 중요도를 시각화 할 수 있는 API를 제공합니다 .
# 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)
lightgbm에 내장된 plot_importance() 역시 넘파이로 피처 데이터를 학습할 경우 피처명을 알 수 없기에 Column_뒤에 피처 순서대로 숫자를 붙여서 X축에 나열합니다. (즉 Column_0은 첫 번째 피처)