입문자를 위한 머신러닝 - GBM

Page content

공지

  • 본 소스코는 교재 파이썬 머신러닝 완벽 가이드 코드를 제 수업을 드는 학생들이 보다 편하게 구글 코랩에서 사용할 수 있도록 만든 예제입니다.
  • 책 구매하세요!

Gradient Boosting Machine

  • 이제 GBM에 대해 학습하도록 합니다.
  • GBM에 대해 이해하기 위해서는 경사하강법에 대해 배워야 합니다.
    • 경사하강법은 쉽게 말하면 가장 적은 오차를 찾아가는 방법론 중이 하나입니다.
    • 자세한 내용은 유투 강의를 들어주시기를 바랍니다. (Gradient Descent, Step-by-Step)
    • 위 이론을 sklearn에서 구현한 것이며, 이 이론을 기반으로 다양한 알고리즘이 개발 되어 있습니다.

데이터 불러오기

  • 데이터를 불러오도록 합니다.
!wget https://archive.ics.uci.edu/ml/machine-learning-databases/00240/UCI%20HAR%20Dataset.zip
!unzip 'UCI HAR Dataset.zip'
!mv UCI\ HAR\ Dataset human_activity # 폴더 이름을 변경하는 터미널 명령어 입니다. 
--2020-11-27 05:48:03--  https://archive.ics.uci.edu/ml/machine-learning-databases/00240/UCI%20HAR%20Dataset.zip
Resolving archive.ics.uci.edu (archive.ics.uci.edu)... 128.195.10.252
Connecting to archive.ics.uci.edu (archive.ics.uci.edu)|128.195.10.252|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 60999314 (58M) [application/x-httpd-php]
Saving to: ‘UCI HAR Dataset.zip’

UCI HAR Dataset.zip 100%[===================>]  58.17M  31.3MB/s    in 1.9s    

2020-11-27 05:48:05 (31.3 MB/s) - ‘UCI HAR Dataset.zip’ saved [60999314/60999314]

Archive:  UCI HAR Dataset.zip
   creating: UCI HAR Dataset/
  inflating: UCI HAR Dataset/.DS_Store  
   creating: __MACOSX/
   creating: __MACOSX/UCI HAR Dataset/
  inflating: __MACOSX/UCI HAR Dataset/._.DS_Store  
  inflating: UCI HAR Dataset/activity_labels.txt  
  inflating: __MACOSX/UCI HAR Dataset/._activity_labels.txt  
  inflating: UCI HAR Dataset/features.txt  
  inflating: __MACOSX/UCI HAR Dataset/._features.txt  
  inflating: UCI HAR Dataset/features_info.txt  
  inflating: __MACOSX/UCI HAR Dataset/._features_info.txt  
  inflating: UCI HAR Dataset/README.txt  
  inflating: __MACOSX/UCI HAR Dataset/._README.txt  
   creating: UCI HAR Dataset/test/
   creating: UCI HAR Dataset/test/Inertial Signals/
  inflating: UCI HAR Dataset/test/Inertial Signals/body_acc_x_test.txt  
   creating: __MACOSX/UCI HAR Dataset/test/
   creating: __MACOSX/UCI HAR Dataset/test/Inertial Signals/
  inflating: __MACOSX/UCI HAR Dataset/test/Inertial Signals/._body_acc_x_test.txt  
  inflating: UCI HAR Dataset/test/Inertial Signals/body_acc_y_test.txt  
  inflating: __MACOSX/UCI HAR Dataset/test/Inertial Signals/._body_acc_y_test.txt  
  inflating: UCI HAR Dataset/test/Inertial Signals/body_acc_z_test.txt  
  inflating: __MACOSX/UCI HAR Dataset/test/Inertial Signals/._body_acc_z_test.txt  
  inflating: UCI HAR Dataset/test/Inertial Signals/body_gyro_x_test.txt  
  inflating: __MACOSX/UCI HAR Dataset/test/Inertial Signals/._body_gyro_x_test.txt  
  inflating: UCI HAR Dataset/test/Inertial Signals/body_gyro_y_test.txt  
  inflating: __MACOSX/UCI HAR Dataset/test/Inertial Signals/._body_gyro_y_test.txt  
  inflating: UCI HAR Dataset/test/Inertial Signals/body_gyro_z_test.txt  
  inflating: __MACOSX/UCI HAR Dataset/test/Inertial Signals/._body_gyro_z_test.txt  
  inflating: UCI HAR Dataset/test/Inertial Signals/total_acc_x_test.txt  
  inflating: __MACOSX/UCI HAR Dataset/test/Inertial Signals/._total_acc_x_test.txt  
  inflating: UCI HAR Dataset/test/Inertial Signals/total_acc_y_test.txt  
  inflating: __MACOSX/UCI HAR Dataset/test/Inertial Signals/._total_acc_y_test.txt  
  inflating: UCI HAR Dataset/test/Inertial Signals/total_acc_z_test.txt  
  inflating: __MACOSX/UCI HAR Dataset/test/Inertial Signals/._total_acc_z_test.txt  
  inflating: __MACOSX/UCI HAR Dataset/test/._Inertial Signals  
  inflating: UCI HAR Dataset/test/subject_test.txt  
  inflating: __MACOSX/UCI HAR Dataset/test/._subject_test.txt  
  inflating: UCI HAR Dataset/test/X_test.txt  
  inflating: __MACOSX/UCI HAR Dataset/test/._X_test.txt  
  inflating: UCI HAR Dataset/test/y_test.txt  
  inflating: __MACOSX/UCI HAR Dataset/test/._y_test.txt  
  inflating: __MACOSX/UCI HAR Dataset/._test  
   creating: UCI HAR Dataset/train/
   creating: UCI HAR Dataset/train/Inertial Signals/
  inflating: UCI HAR Dataset/train/Inertial Signals/body_acc_x_train.txt  
   creating: __MACOSX/UCI HAR Dataset/train/
   creating: __MACOSX/UCI HAR Dataset/train/Inertial Signals/
  inflating: __MACOSX/UCI HAR Dataset/train/Inertial Signals/._body_acc_x_train.txt  
  inflating: UCI HAR Dataset/train/Inertial Signals/body_acc_y_train.txt  
  inflating: __MACOSX/UCI HAR Dataset/train/Inertial Signals/._body_acc_y_train.txt  
  inflating: UCI HAR Dataset/train/Inertial Signals/body_acc_z_train.txt  
  inflating: __MACOSX/UCI HAR Dataset/train/Inertial Signals/._body_acc_z_train.txt  
  inflating: UCI HAR Dataset/train/Inertial Signals/body_gyro_x_train.txt  
  inflating: __MACOSX/UCI HAR Dataset/train/Inertial Signals/._body_gyro_x_train.txt  
  inflating: UCI HAR Dataset/train/Inertial Signals/body_gyro_y_train.txt  
  inflating: __MACOSX/UCI HAR Dataset/train/Inertial Signals/._body_gyro_y_train.txt  
  inflating: UCI HAR Dataset/train/Inertial Signals/body_gyro_z_train.txt  
  inflating: __MACOSX/UCI HAR Dataset/train/Inertial Signals/._body_gyro_z_train.txt  
  inflating: UCI HAR Dataset/train/Inertial Signals/total_acc_x_train.txt  
  inflating: __MACOSX/UCI HAR Dataset/train/Inertial Signals/._total_acc_x_train.txt  
  inflating: UCI HAR Dataset/train/Inertial Signals/total_acc_y_train.txt  
  inflating: __MACOSX/UCI HAR Dataset/train/Inertial Signals/._total_acc_y_train.txt  
  inflating: UCI HAR Dataset/train/Inertial Signals/total_acc_z_train.txt  
  inflating: __MACOSX/UCI HAR Dataset/train/Inertial Signals/._total_acc_z_train.txt  
  inflating: __MACOSX/UCI HAR Dataset/train/._Inertial Signals  
  inflating: UCI HAR Dataset/train/subject_train.txt  
  inflating: __MACOSX/UCI HAR Dataset/train/._subject_train.txt  
  inflating: UCI HAR Dataset/train/X_train.txt  
  inflating: __MACOSX/UCI HAR Dataset/train/._X_train.txt  
  inflating: UCI HAR Dataset/train/y_train.txt  
  inflating: __MACOSX/UCI HAR Dataset/train/._y_train.txt  
  inflating: __MACOSX/UCI HAR Dataset/._train  
  inflating: __MACOSX/._UCI HAR Dataset  
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('./human_activity/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('./human_activity/train/X_train.txt',sep='\s+', names=feature_name )
    X_test = pd.read_csv('./human_activity/test/X_test.txt',sep='\s+', names=feature_name)
    
    # 학습 레이블과 테스트 레이블 데이터을 DataFrame으로 로딩하고 컬럼명은 action으로 부여
    y_train = pd.read_csv('./human_activity/train/y_train.txt',sep='\s+',header=None,names=['action'])
    y_test = pd.read_csv('./human_activity/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

(옵션 체크) 구글 코랩 개발환경 확인

  • 간단하게 구글 코랩 개발환경을 확인한다.
!cat /proc/cpuinfo
processor	: 0
vendor_id	: GenuineIntel
cpu family	: 6
model		: 79
model name	: Intel(R) Xeon(R) CPU @ 2.20GHz
stepping	: 0
microcode	: 0x1
cpu MHz		: 2200.000
cache size	: 56320 KB
physical id	: 0
siblings	: 2
core id		: 0
cpu cores	: 1
apicid		: 0
initial apicid	: 0
fpu		: yes
fpu_exception	: yes
cpuid level	: 13
wp		: yes
flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm rdseed adx smap xsaveopt arat md_clear arch_capabilities
bugs		: cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs taa
bogomips	: 4400.00
clflush size	: 64
cache_alignment	: 64
address sizes	: 46 bits physical, 48 bits virtual
power management:

processor	: 1
vendor_id	: GenuineIntel
cpu family	: 6
model		: 79
model name	: Intel(R) Xeon(R) CPU @ 2.20GHz
stepping	: 0
microcode	: 0x1
cpu MHz		: 2200.000
cache size	: 56320 KB
physical id	: 0
siblings	: 2
core id		: 0
cpu cores	: 1
apicid		: 1
initial apicid	: 1
fpu		: yes
fpu_exception	: yes
cpuid level	: 13
wp		: yes
flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm rdseed adx smap xsaveopt arat md_clear arch_capabilities
bugs		: cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs taa
bogomips	: 4400.00
clflush size	: 64
cache_alignment	: 64
address sizes	: 46 bits physical, 48 bits virtual
power management:

GBM 모형 학습

  • GBM 수행 시간 측정을 위해 시작 시간을 설정합니다.
# GBM 수행 시간 측정을 위함. 시작 시간 설정.
start_time = time.time()

gb_clf = GradientBoostingClassifier(n_estimators=10, # 기본값은 100이지만, 수업 시간을 고려하여 10으로 지정하였음
                                    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        8274.1239           48.73s
         2        6858.4625           46.31s
         3        5818.7911           41.54s
         4        4981.6935           35.95s
         5        4331.9874           30.14s
         6        3790.2625           24.27s
         7        3336.1237           18.25s
         8        2960.1395           12.17s
         9        2626.5821            6.10s
        10        2341.0659            0.00s
GBM 정확도: 0.8907
GBM 수행 시간: 61.1 초 

하이퍼 파라미터 및 튜닝

  • 튜닝이란, 모형이 가지고 있는 여러 조건들을 변형 시키는 것
  • 대표적인 파라미터 소개
    • n_estimators: weak learner의 개수 지정, 디폴트는 100개이며, 많이 설정할수록 좋은 성능 기대 가능, 그러나 시간도 오래 소요됨
    • max_features: 최적의 분할을 위해 고려할 피처의 개수
      • int 형으로 지정 시, 대상 피처의 개수, float형으로 지정 시, 대상 피처의 퍼센트
      • 만약 전체 피처가 16개라면 분할 위해 4개 참조
    • max_depth: 트리의 최대 깊이 규정
    • min_samples_split: 자식 규칙 노드를 분할해 만들기 위한 최소한의 샘플 데이터 개수
    • min_samples_leaf: 말단 노드(Leaf)가 되기 위한 최소한의 샘플 데이터 수
    • loss: 경사 하강법에서 사용할 비용 함수 지정
    • learning_rate: GBM이 학습 진행 시 마다의 학습률. Weak Learner가 순차적으로
    • subsample: weak learner가 학습에 사용하는 데이터의 샘플링 비율, 기본값은 1이며, 이는 전체학습 데이터를 기반으로 학습한다는 의미.

그리드 서치

  • GridSearchCV API를 활용하여 모형에 사용되는 하이퍼 파라미터를 순차적으로 입력하며서 편리하게 최적의 파라미터를 도출할 수 있는 방안 제공
  • 즉, 랜덤포레스트의 파라미터를 순차적으로 변경 및 조정하면서 모형을 학습시키는 방법
  • 이를 통해, 머신러닝 모형 개발자의 코드량을 줄여주는 매우 편리한 기법
    • estimator: 머신러닝 모형의 객체가 온다.
    • param_grid: 딕셔너리 형태로 조정하며, estimator의 튜닝을 위해 파라미터명과 사용될 여러 파라미터 값 지정, 이 부분은 각 머신러닝 모형의 Manual을 참조한다.
    • scoring: 예측 성능을 측정할 평가 방법 지정하지만, 대개 별도의 성능 평가 지표 함수 활용
    • cv: 교차 검증을 위해 분할되는 학습/테스트 세트의 개수 지정
  • 이 때에는 시간이 다소 소요될 수 있다. (5-10분)
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


[Parallel(n_jobs=-1)]: Using backend LokyBackend with 2 concurrent workers.
[Parallel(n_jobs=-1)]: Done   8 out of   8 | elapsed:  4.2min finished


      Iter       Train Loss   Remaining Time 
         1        8274.1239            1.75m
         2        6858.4625            1.78m
         3        5818.7911            1.72m
         4        4981.6935            1.63m
         5        4331.9874            1.54m
         6        3790.2625            1.44m
         7        3336.1237            1.34m
         8        2960.1395            1.24m
         9        2626.5821            1.14m
        10        2341.0659            1.04m
        20         940.8021            0.00s
최적 하이퍼 파라미터:
 {'learning_rate': 0.1, 'n_estimators': 20}
최고 예측 정확도: 0.8921
GBM 수행 시간: 380.2 초 

# 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

소결

  • 학습 시간의 속도면에서 타 알고리즘보다 매우 느리기 때문에, 경진대회 및 현업에서 사용하는 것을 추천하지는 않습니다.
  • 다만, 간단한 개념에 대한 이해 정도로 위 포스트를 학습하는 것을 추천하며, 향후에는 LightGBM 또는 XGBoost를 학습하는 것을 추천합니다.