yngie blog
creatorjo blog


통계분석_5 - GBM(Gradient Boosting Machine)

3-1) 사전 정리

부스팅(Boosting) : 앙상블 방법 중 하나

Adaboost(Adaptive Boosting)

약한 모델(Weak Model)

3-2) GBM(Gradient Boosting Machine)

3-3) sklearn의 ensemble class - GradientBoostingRegressor

Gradient Boost Machine(GBM)

GBM의 학습 방식

Gradient Boosting Algorithm (GBM)은 회귀분석 또는 분류 분석을 수행할 수 있는 예측모형이며 예측모형의 앙상블 방법론 중 부스팅 계열에 속하는 알고리즘입니다. Gradient Boosting Algorithm은 Tabular format 데이터 (엑셀형태와 같이 X-Y Grid로 되어있는 데이터)에 대한 예측에서 엄청난 성능을 보여주고, 머신러닝 알고리즘 중에서도 가장 예측 성능이 높다고 알려진 알고리즘입니다. 그렇기 때문에 Gradient Boosting Algorithm을 구현한 패키지들이 많습니다. LightGBM, CatBoost, XGBoost 같은 파이썬 패키지들이 모두 Gradient Boosting Algorithm을 구현한 패키지들입니다. GBM은 계산량이 상당히 많이 필요한 알고리즘이기 때문에, 이를 하드웨어 효율적으로 구현하는 것이 필요한데, 위 패키지들은 모두 GBM을 효율적으로 구현하려고한 패키지들이라고 볼 수 있습니다.

출처: https://3months.tistory.com/368 [Deep Play]

GBM(Gradient Boosting Machine)은 AdaBoost(에이다부스트)와 유사하지만 가중치를 업데이트 할때 경사 하강법(Gradient Descent)을 이용하는 것이 큰! 차이 입니다.

경사 하강법(Gradient Descent): 분류의 실제 결과값을 Y, 피처를 X1, X2, … , Xn 그리고 이 피처들에 기반한 예측 함수를 F(x) 함수라고 하면 오류식은 h(x) = y - F(x)라고 할 수 있습니다. 이 오류식을 최소화하는 방향성을 가지고 반복적으로 가중치의 값을 업데이터 해나가는 방식이 경사 하강법입니다.

사이킷런에서는 GradientBoostingClassifier클래스를 제공하여 GBM 기반의 분류를 실행할 수 있게 해줍니다. 마찬가지로 사용자 행동 데이터 세트를 통해 예측 분류를 해보도록 하겠습니다.

import os
os.chdir('C:/Users/Administrator/Documents/Python/211120_ADP스터디/ADP/')
print(os.getcwd())

C:\Users\Administrator\Documents\Python\211120_ADP스터디\ADP
import pandas as pd

def get_new_feature_name_df(old_feature_name_df):
    feature_dup_df = pd.DataFrame(data=old_feature_name_df.groupby('column_name').cumcount(),
                                  columns=['dup_cnt'])
    feature_dup_df = feature_dup_df.reset_index()
    new_feature_name_df = pd.merge(old_feature_name_df.reset_index(), feature_dup_df, how='outer')
    new_feature_name_df['column_name'] = new_feature_name_df[['column_name', 'dup_cnt']].apply(lambda x : x[0]+'_'+str(x[1]) 
                                                                                         if x[1] >0 else x[0] ,  axis=1)
    new_feature_name_df = new_feature_name_df.drop(['index'], axis=1)
    return new_feature_name_df

def get_human_dataset( ):
    
    # 각 데이터 파일들은 공백으로 분리되어 있으므로 read_csv에서 공백 문자를 sep으로 할당.
    feature_name_df = pd.read_csv('./data/UCI HAR Dataset/features.txt',sep='\s+',
                        header=None,names=['column_index','column_name'])
    
    # 중복된 피처명을 수정하는 get_new_feature_name_df()를 이용, 신규 피처명 DataFrame생성. 
    new_feature_name_df = get_new_feature_name_df(feature_name_df)
    
    # DataFrame에 피처명을 컬럼으로 부여하기 위해 리스트 객체로 다시 변환
    feature_name = new_feature_name_df.iloc[:, 1].values.tolist()
    
    # 학습 피처 데이터 셋과 테스트 피처 데이터을 DataFrame으로 로딩. 컬럼명은 feature_name 적용
    X_train = pd.read_csv('./data/UCI HAR Dataset/train/X_train.txt',sep='\s+', names=feature_name )
    X_test = pd.read_csv('./data/UCI HAR Dataset/test/X_test.txt',sep='\s+', names=feature_name)
    
    # 학습 레이블과 테스트 레이블 데이터을 DataFrame으로 로딩하고 컬럼명은 action으로 부여
    y_train = pd.read_csv('./data/UCI HAR Dataset/train/y_train.txt',sep='\s+',header=None,names=['action'])
    y_test = pd.read_csv('./data/UCI HAR Dataset/test/y_test.txt',sep='\s+',header=None,names=['action'])
    
    # 로드된 학습/테스트용 DataFrame을 모두 반환 
    return X_train, X_test, y_train, y_test
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import accuracy_score
import pandas as pd
import time
import warnings
warnings.filterwarnings('ignore')

# 결정 트리에서 사용한 get_human_dataset( )을 이용해 학습/테스트용 DataFrame 반환
X_train, X_test, y_train, y_test = get_human_dataset()

print("## 학습 피처 데이터 정보 ##")
print(X_train.info())
## 학습 피처 데이터 정보 ##
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 7352 entries, 0 to 7351
Columns: 561 entries, tBodyAcc-mean()-X to angle(Z,gravityMean)
dtypes: float64(561)
memory usage: 31.5 MB
None
# GBM 수행 시간 측정을 위함. 시작 시간 설정.
start_time = time.time()

gb_clf = GradientBoostingClassifier(n_estimators=10, # 기본값은 100
                                    validation_fraction=0.2,
                                    n_iter_no_change=5, tol=0.01, verbose = 1,
                                    random_state=0)
    
gb_clf.fit(X_train , y_train)
gb_pred = gb_clf.predict(X_test)
gb_accuracy = accuracy_score(y_test, gb_pred)

print('GBM 정확도: {0:.4f}'.format(gb_accuracy))
print("GBM 수행 시간: {0:.1f} 초 ".format(time.time() - start_time))
      Iter       Train Loss   Remaining Time 
         1           1.4069           48.19s
         2           1.1662           45.24s
         3           0.9894           40.88s
         4           0.8471           35.55s
         5           0.7366           29.59s
         6           0.6445           23.66s
         7           0.5673           17.69s
         8           0.5033           11.76s
         9           0.4466            5.89s
        10           0.3981            0.00s
GBM 정확도: 0.8907
GBM 수행 시간: 58.9 초 

정확도는 89.07%이다.

시간이 오래 걸리기 때문에 간략하게 n_estimators를 10, 20으로 learning_rate를 0.05, 0.1로 설정하도록 하겠습니다.

from sklearn.model_selection import GridSearchCV

start_time = time.time()

params = {
    'n_estimators':[10, 20], # 원 코드 100, 500
    'learning_rate' : [ 0.05, 0.1]
}
grid_cv = GridSearchCV(gb_clf , param_grid=params , cv=2 ,verbose=1, n_jobs = -1)
grid_cv.fit(X_train , y_train)
print('최적 하이퍼 파라미터:\n', grid_cv.best_params_)
print('최고 예측 정확도: {0:.4f}'.format(grid_cv.best_score_))

print("GBM 수행 시간: {0:.1f} 초 ".format(time.time() - start_time))
Fitting 2 folds for each of 4 candidates, totalling 8 fits
      Iter       Train Loss   Remaining Time 
         1           1.4069            1.61m
         2           1.1662            1.62m
         3           0.9894            1.59m
         4           0.8471            1.52m
         5           0.7366            1.43m
         6           0.6445            1.34m
         7           0.5673            1.25m
         8           0.5033            1.15m
         9           0.4466            1.06m
        10           0.3981           57.65s
        20           0.1600            0.00s
최적 하이퍼 파라미터:
 {'learning_rate': 0.1, 'n_estimators': 20}
최고 예측 정확도: 0.8921
GBM 수행 시간: 348.3 초 

최적 하이퍼 파리미터는 n_estimators가 10, learning_rate가 0.1일때로 나왔습니다. 최고 예측 정확도는 약 89.21%정도 나왔습니다. 이 파라미터를 가지고 테스트 데이터 세트에 적용을 하여 예측 정확도를 알아보겠습니다.

하이퍼 파라미터 및 튜닝

튜닝이란, 모형이 가지고 있는 여러 조건들을 변형 시키는 것

대표적인 파라미터 소개

# GridSearchCV를 이용하여 최적으로 학습된 estimator로 predict 수행. 
gb_pred = grid_cv.best_estimator_.predict(X_test)
gb_accuracy = accuracy_score(y_test, gb_pred)
print('GBM 정확도: {0:.4f}'.format(gb_accuracy))
GBM 정확도: 0.9077

테스트 데이터 세트에서는 약 90%정도가 나온것을 확인하였습니다. GBM은 과적합에 뛰어난 예측 성능을 가지지만 수행 시간이 오래 걸리는 단점이 있습니다.



통계분석_6 - XGBoost (for Regression)

reference:

Gradient Boosting

XGBoost 무엇이 다른가?

  1. Regularized boosting
    일반적인 GBM은 과적합을 방지하는 기능이 없지만 XGBoost는 모델 자체에 과적합을 방지하는 기능이 존재
  2. 결측값을 자동으로 처리
    XGBoost에 결측값이 있는 데이터를 넣으면 그 결측값을 하나의 값으로 인식하는 듯함. 결측값이 존재할 때의 최적 결과값도 파악
  3. Tree Pruning
    GBM은 노드를 나눌 때 negative loss를 만나면 멈추도록 되어있는데, XGBoost는 하이퍼파라미터로 설정한 max_depth까지만 수행하고 Tree의 깊이를 완성시키면 다시 노드 가지치기를 수행한다. 이런 방법으로도 과적합 방지에 기여한다
  4. 내장된 Cross Validation
  5. Tree building 단계에서 병렬처리를 통해 더 빠른 결과처리 가능 Tree building 단계의 pseudo code가 아래와 같다면, XGBoost는 생성된 노드 아래에 each feature에 해당되는 부분에서 병렬처리를 해서 모델 생성 단계에 걸리는 시간이 단축된다.
    For each leaf node:
     For each feature:
         Sort the instances in the node by the feature value
         Linear scan to decide the best split on the feature
     Take the best split and do it
    

참고) 파이썬 래퍼 기반의 XGB와 사이킷런 래퍼 기반의 XGB xgboost 패키지를 불러와서 그 자체를 모델로 사용하는 경우 ( model = xgboost() ) 가 파이썬 래퍼에 해당하고, xgboost 안에서 XGBClassifier나 XGBRegressor를 꺼내 사용하는 게 사이킷런 래퍼에 해당. 솔직히 사용하는 입장에선 별 차이 없고 파라미터의 __default 값이나 메소드__가 달라진다.

###XGBoost Regression 모델링 (사이킷런 래퍼 이용)

## xgboost 패키지는 adp 제공 패키지에 포함
## 이번에 보는 건 xgboost regression이라서 분류 부분은 없습니다! 머신러닝 완벽가이드에는 분류 예시로 나와있으니 참고해주세요!
import xgboost
from xgboost import XGBRegressor
from xgboost import plot_importance ##분류와 회귀에 사용된 변수들의 기여도를 그래프로 그려줌
import pandas as pd
import numpy as np
from sklearn.model_selection import RepeatedKFold
from sklearn.model_selection import cross_val_score
import warnings
warnings.filterwarnings('ignore')
## 학습 데이터 : Housing price 데이터
from pandas import read_csv
from matplotlib import pyplot
url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/housing.csv'
dataframe = read_csv(url, header=None)
### 데이터 탐색
print(dataframe.shape) ## 13개 feature와 504개 observation
print(dataframe.head())
(506, 14)
        0     1     2   3      4      5   ...  8      9     10      11    12    13
0  0.00632  18.0  2.31   0  0.538  6.575  ...   1  296.0  15.3  396.90  4.98  24.0
1  0.02731   0.0  7.07   0  0.469  6.421  ...   2  242.0  17.8  396.90  9.14  21.6
2  0.02729   0.0  7.07   0  0.469  7.185  ...   2  242.0  17.8  392.83  4.03  34.7
3  0.03237   0.0  2.18   0  0.458  6.998  ...   3  222.0  18.7  394.63  2.94  33.4
4  0.06905   0.0  2.18   0  0.458  7.147  ...   3  222.0  18.7  396.90  5.33  36.2

[5 rows x 14 columns]
dataframe.head()
0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 0.00632 18.0 2.31 0 0.538 6.575 65.2 4.0900 1 296.0 15.3 396.90 4.98 24.0
1 0.02731 0.0 7.07 0 0.469 6.421 78.9 4.9671 2 242.0 17.8 396.90 9.14 21.6
2 0.02729 0.0 7.07 0 0.469 7.185 61.1 4.9671 2 242.0 17.8 392.83 4.03 34.7
3 0.03237 0.0 2.18 0 0.458 6.998 45.8 6.0622 3 222.0 18.7 394.63 2.94 33.4
4 0.06905 0.0 2.18 0 0.458 7.147 54.2 6.0622 3 222.0 18.7 396.90 5.33 36.2
## X,Y값 나누기
X = dataframe.iloc[:, :-1]
y = dataframe.iloc[:,-1]
## 가장 기본적인 XGB Regressor를 만들고 평가하기
model = XGBRegressor() ## 하이퍼파라미터를 설정하려면 XGBRegreesor()의 괄호 안에 넣으면 됨
cv = RepeatedKFold(n_splits=10, n_repeats=3, random_state=1)
scores = cross_val_score(model, X, y, scoring='neg_mean_absolute_error', cv=cv, n_jobs=-1)
scores = abs(scores)
print('Mean MAE: %.3f (%.3f)' % (scores.mean(), scores.std()) )
Mean MAE: 2.146 (0.328)

참고) 모델 평가 스코어에서 쓴 ‘neg_mean_absolute_error’는 우리가 배운 정확성 측정 방법 중 ‘MAE(Mean Absolute Error)’에 해당한다. 그러나 scikit-learn에서는 내부 작동 방식 때문에 MAE를 구하면 음수값으로 도출된다고 하고, 그래서 scikitlearn 상에서 MAE 앞에 negative를 붙이는 것 같다. 이 링크에서 더 많은 정보를 확인
이 예제에서는 사이킷런으로 구한 MAE에 다시 절대값을 씌워주는 방식을 사용함

XGBoost의 하이퍼파라미터

머신러닝 완벽가이드 p.231 참고

feature중요도산정

## node에 사용된 feature들의 중요도 계산하기
model.fit(X,y)
model.feature_importances_
[23:36:41] WARNING: /workspace/src/objective/regression_obj.cu:152: reg:linear is now deprecated in favor of reg:squarederror.



array([0.0189575 , 0.00462131, 0.00827119, 0.008848  , 0.04185903,
       0.22672848, 0.01063313, 0.03859667, 0.01044884, 0.02304217,
       0.04435536, 0.01205312, 0.5515853 ], dtype=float32)
import matplotlib.pyplot as plt
plt.barh(dataframe.columns[:-1], model.feature_importances_)
<BarContainer object of 13 artists>

png