[Python] 머신러닝 완벽가이드 - 04. 분류[랜덤포레스트,GBM]

Updated:

파이썬 머신러닝 완벽가이드 교재를 토대로 공부한 내용입니다.

실습과정에서 필요에 따라 내용의 누락 및 추가, 수정사항이 있습니다.


기본 세팅

import numpy as np
import pandas as pd

import matplotlib as mpl
import matplotlib.pyplot as plt
import seaborn as sns

import warnings
%matplotlib inline
%config InlineBackend.figure_format = 'retina'

mpl.rc('font', family='NanumGothic') # 폰트 설정
mpl.rc('axes', unicode_minus=False) # 유니코드에서 음수 부호 설정

# 차트 스타일 설정
sns.set(font="NanumGothic", rc={"axes.unicode_minus":False}, style='darkgrid')
plt.rc("figure", figsize=(10,8))

warnings.filterwarnings("ignore")

1. 앙상블 학습 유형

앙상블 학습 유형은 전통적으로 보팅(Voting), 배깅(Bagging), 부스팅(Boosting)의 세 가지로 나눌 수 있으며,

이외에도 스태킹 등 다양한 앙상블 학습 방법이 있다.

보팅(Voting)

  • 여러 개의 분류기가 투표를 통해 최종 예측 결과를 결정하는 방식으로, 일반적으로 서로 다른 알고리즘의 분류기를 결합

배깅(Bagging)

  • 보팅과 마찬가지로 여러 분류기로 최종 예측 결과를 결정하지만, 동일한 분류기를 데이터 샘플링을 서로 다르게 가져가면서 학습을 수행한다.

  • 데이터 샘플링 방식은 부트스트래핑(Bootstrapping)이며 교차 검증과 다르게 데이터의 중복이 허용된다.

  • 대표적인 배깅 방식으로 랜덤 포레스트가 있다.

부스팅(Bossting)

  • 여러 개의 분류기가 순차적으로 학습을 수행하되, 앞에서 학습한 분류기가 예측이 틀린 데이터에 대해서는

    올바르게 예측할 수 있도록 다음 분류기에게는 가중치를 부여하면서 학습과 예측을 진행한다.

  • 대표적인 부스팅 방식으로 GBM(Gradient Boosting Machine), XGBoost(eXtra Gradient Boost), LightGBM(Light Gradient Boost)이 있다.

2. 보팅

2.1 하드 보팅

  • 하드 보팅은 다수결 원칙과 비슷한 개념으로서 다수의 분류기가 결정한 예측값을 최종 보팅 결과값으로 선정한다.

2.2 소프트 보팅

  • 소프트 보팅은 분류기들의 레이블 값 결정 확률을 모두 더하고 평균을 구해 확률이 가장 높은 레이블 값을 최종 보팅 결과값으로 선정한다.

  • 일반적으로 소프트 보팅이 예측 성능이 좋아서 더 많이 사용된다.

2.3 breast_cancer 예제

from sklearn.ensemble import VotingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

# breast_cancer data
cancer = load_breast_cancer()

data_df = pd.DataFrame(cancer.data, columns=cancer.feature_names)
data_df.head()
mean radius mean texture mean perimeter mean area mean smoothness mean compactness mean concavity mean concave points mean symmetry mean fractal dimension ... worst radius worst texture worst perimeter worst area worst smoothness worst compactness worst concavity worst concave points worst symmetry worst fractal dimension
0 17.99 10.38 122.80 1001.0 0.11840 0.27760 0.3001 0.14710 0.2419 0.07871 ... 25.38 17.33 184.60 2019.0 0.1622 0.6656 0.7119 0.2654 0.4601 0.11890
1 20.57 17.77 132.90 1326.0 0.08474 0.07864 0.0869 0.07017 0.1812 0.05667 ... 24.99 23.41 158.80 1956.0 0.1238 0.1866 0.2416 0.1860 0.2750 0.08902
2 19.69 21.25 130.00 1203.0 0.10960 0.15990 0.1974 0.12790 0.2069 0.05999 ... 23.57 25.53 152.50 1709.0 0.1444 0.4245 0.4504 0.2430 0.3613 0.08758
3 11.42 20.38 77.58 386.1 0.14250 0.28390 0.2414 0.10520 0.2597 0.09744 ... 14.91 26.50 98.87 567.7 0.2098 0.8663 0.6869 0.2575 0.6638 0.17300
4 20.29 14.34 135.10 1297.0 0.10030 0.13280 0.1980 0.10430 0.1809 0.05883 ... 22.54 16.67 152.20 1575.0 0.1374 0.2050 0.4000 0.1625 0.2364 0.07678

5 rows × 30 columns

# 학습/검증 데이터 분리
X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target, 
                                                    test_size=0.2, random_state=11)

# 개별 분류기
lr_clf = LogisticRegression()
knn_clf = KNeighborsClassifier(n_neighbors=8)

# 소프트 보팅 기반의 앙상블 모델 분류기
vo_clf = VotingClassifier(estimators = [("LR", lr_clf), ("KNN", knn_clf)], voting = "soft")
  • 보팅 분류기 VotingClassifier()estimators에는 리스트 값으로 Classifier 객체들을 튜플 형식으로 입력한다.

  • voting은 보팅 방식을 설정하며 디폴트 값은 hard이다.

# 개별 분류기 및 보팅 분류기 학습/예측/평가
calssifiers = [vo_clf, lr_clf, knn_clf]

for classifier in calssifiers:
    classifier.fit(X_train, y_train)
    pred = classifier.predict(X_test)
    acc1 = accuracy_score(y_test, pred)
    
    class_name = classifier.__class__.__name__
    
    print(f"{class_name} 정확도:{acc1: .4f}")
VotingClassifier 정확도: 0.9737
LogisticRegression 정확도: 0.9386
KNeighborsClassifier 정확도: 0.9561
  • 보팅 분류기의 정확도가 가장 높게 나타났는데, 무조건적으로 기반 분류기보다 예측 성능이 향상되는 것은 아니다.

3. 배깅

3.1 랜덤 포레스트

  • 랜덤 포레스트는 결정 트리를 기반으로 하는 방식으로서 여러 개의 결정 트리 분류기가 전체 데이터에서 배깅 방식으로 각자의 데이터를 샘플링 한 후

    학습을 수행한 뒤 최종적으로 모든 분류기가 보팅을 통해 예측을 결정한다.

  • 부트스트래핑 방식으로 데이터를 추출하며, 만약 데이터의 갯수가 100개이고 결정 트리 분류기가 3개이면 데이터 갯수가 100개인 서브셋을 3개 생성한다.

    이 때, 각 서브셋에는 개별 데이터가 중복되어 만들어진다.

3.2 사용자 행동 인식 예제

# 피처명 중복 제거
def get_new_feature_name(old_feature_name_df):
    # cumcount()로 피처명별로 중복 존재시 숫자 부여, reset_index()로 Column_Index 생성
    feature_dup = pd.DataFrame(old_feature_name_df.groupby("Column_name").cumcount()).reset_index()
    
    # features.txt의 Column_Index는 1부터 시작이므로 동일하게 설정
    feature_dup.columns = ["Column_Index", "dup_cnt"]
    feature_dup["Column_Index"] = feature_dup["Column_Index"] + 1
    
    # Column_Index를 기준으로 Merge 후 중복컬럼명 변경
    temp = pd.merge(old_feature_name_df, feature_dup, on = "Column_Index", how="outer")
    temp["Column_name"] = temp.apply(lambda x: x.Column_name + "_" + str(x.dup_cnt) if x.dup_cnt > 0 
                                     else x.Column_name, axis=1)
    
    return temp

def get_human_dataset():
    # 피처명 불러오기
    feature_name_df = pd.read_csv("./Human_activity/features.txt", sep="\s+", header=None,
                               names = ["Column_Index", "Column_name"])
    # 피처명 중복 수정
    new_feature_name = get_new_feature_name(feature_name_df)
    
    # 리스트로 변경
    feature_name = new_feature_name["Column_name"].tolist()
    
    # train, test 피처 데이터(X), 레이블 데이터(y) 로드
    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)
    
    y_train = pd.read_csv("./Human_activity/train/y_train.txt", sep="\s+", names = ["action"])
    y_test = pd.read_csv("./Human_activity/test/y_test.txt", sep="\s+", names = ["action"])
    
    return X_train, X_test, y_train, y_test
  • 이전 포스팅 에서 정의한 사용자 행동 인식 데이터 불러오기 함수를 사용한다.
from sklearn.ensemble import RandomForestClassifier

X_train, X_test, y_train, y_test = get_human_dataset()

# 랜덤 포레스트 분류기
rf_clf = RandomForestClassifier(random_state=0)
rf_clf.fit(X_train, y_train)
pred = rf_clf.predict(X_test)
acc2 = accuracy_score(y_test, pred)

print(f"랜덤 포레스트 정확도:{acc2: .4f}")
랜덤 포레스트 정확도: 0.9253
  • 랜덤 포레스트는 사용자 행동 인식 데이터 셋에 대해 약 92.53%의 정확도를 보여준다.

3.3 랜덤 포레스트 하이퍼 파라미터

랜덤 포레스트의 하이퍼 파라미터는 대부분 결정 트리에서 사용되는 하이퍼 파라미터와 같다.

  • n_estimators: 결정 트리의 갯수를 지정한다. 디폴트는 10개이다.

  • max_features: 결정 트리에선 디폴트 값이 전체 피처인 반면, 랜덤 포레스트에선 디폴트로 전체 피처의 “sqrt”만큼 참조한다.

GridSearchCV

from sklearn.model_selection import GridSearchCV

params = {
    "n_estimators": [100], 
    "max_depth": [6, 8, 10, 12], 
    "min_samples_leaf": [8, 12, 18], 
    "min_samples_split": [8, 16, 20]
}

# 랜덤 포레스트 객체 생성 후 GridSearchCV
rf_clf = RandomForestClassifier(random_state=0, n_jobs=-1)
grid_cv = GridSearchCV(rf_clf, param_grid = params, cv=2, n_jobs = -1)

grid_cv.fit(X_train, y_train)

print("최고 예측 정확도:{0:.4f}".format(grid_cv.best_score_))
print("최적 하이퍼 파라미터:\n", grid_cv.best_params_)
최고 예측 정확도:0.9180
최적 하이퍼 파라미터:
 {'max_depth': 10, 'min_samples_leaf': 8, 'min_samples_split': 8, 'n_estimators': 100}
  • RandomForestClassifier(), GridSearchCV()에서 n_jobs = -1을 추가하면 모든 CPU 코어를 이용해 학습할 수 있다.
best_rt_clf = grid_cv.best_estimator_
rt_pred = best_rt_clf.predict(X_test)
acc = accuracy_score(y_test, rt_pred)

print(f"예측 정확도:{acc: .4f}")
예측 정확도: 0.9196
  • 앞서 확인한 최적 하이퍼 파라미터를 적용하였을 때 test 데이터 셋의 예측 정확도는 약 91.96%이다.

피처별 중요도

# 피처별 중요도 상위 20개
temp = best_rt_clf.feature_importances_
feature_importances_HAR_top20 = pd.Series(temp, index = X_train.columns).sort_values(ascending=False)[:20]
 
# 시각화
sns.barplot(x = feature_importances_HAR_top20, y = feature_importances_HAR_top20.index)
plt.show()

png

4. 부스팅

4.1 GBM(Gradient Boosting Machine)

GBM은 여러 개의 weak learner를 순차적으로 학습,예측하면서 잘못 예측한 데이터에 가중치 부여를 통해 오류를 개선해 나가면서 학습하는 방식이다.

가중치의 업데이트 방법은 경사 하강법(Gradient Descent)을 사용한다.

  • 경사 하강법(Gradient Descent)

    분류의 실제 결과값을 $y$, 피처를 $x_{1}, x_{2}, …, x_{n}$, 피처에 기반한 예측 함수를 $F(x)$ 라고 할 때,

    오류식 $h(x) = y - F(x)$을 최소화하는 방향성을 가지고 반복적으로 가중치 값을 업데이트 하는 것이다.

4.2 사용자 행동 인식 예제

from sklearn.ensemble import GradientBoostingClassifier
import time

X_train, X_test, y_train, y_test = get_human_dataset()

# GBM 수행 시간 측정
start_time = time.time()

# GBM
gb_clf = GradientBoostingClassifier(random_state=0)
gb_clf.fit(X_train, y_train)
gb_pred = gb_clf.predict(X_test)
gb_accuray = accuracy_score(y_test,gb_pred)

print(f"GBM 정확도: {gb_accuray:.4f}")
print(f"GBM 수행 시간: {time.time() - start_time:.1f}초")
GBM 정확도: 0.9389
GBM 수행 시간: 1046.9초

4.3 GBM 하이퍼 파라미터

  • loss: 경사 하강법에서 사용할 비용 함수를 지정한다. 특별한 이유가 없다면 디폴트 값 “deviance”를 적용한다.

  • learning_rate: GBM이 학습을 진행할 때마다 적용하는 학습률로서 weak learner가 순차적으로 오류 값을 보정해 나가는 데 적용하는 계수

                0~1 사이의 값을 지정할 수 있고 디폴트는 0.1이다.
                  
                  
                 값이 작을수록 최소 오류 값을 찾아 예측 성능이 높아질 가능성이 크지만 수행 시간이 오래걸린다.
    
  • n_estimators: weak learner의 갯수로 디폴트 값은 100이다.

  • subsample: weak learner가 학습에 사용하는 데이터의 샘플링 비율로 디폴트 값은 1이다.

            과적합이 염려되는 경우 1보다 작은 값으로 설정한다
    

GridSearchCV

from sklearn.model_selection import GridSearchCV

params = {"n_estimators": [100, 500], "learning_rate": [0.05, 0.1]}

# GBM 객체 생성 후 GridSearchCV
gb_clf = GradientBoostingClassifier(random_state=0)
grid_cv2 = GridSearchCV(gb_clf, param_grid = params, cv=2, verbose=1)

grid_cv2.fit(X_train, y_train)

print("최고 예측 정확도:{0:.4f}".format(grid_cv2.best_score_))
print("최적 하이퍼 파라미터:\n", grid_cv2.best_params_)
Fitting 2 folds for each of 4 candidates, totalling 8 fits


[Parallel(n_jobs=1)]: Using backend SequentialBackend with 1 concurrent workers.
[Parallel(n_jobs=1)]: Done   8 out of   8 | elapsed: 217.5min finished


최고 예측 정확도:0.9011
최적 하이퍼 파라미터:
 {'learning_rate': 0.1, 'n_estimators': 500}
  • GBM으로 간단히 최적 하이퍼 파라미터와 최고 예측 정확도를 구하였다.

  • 소요시간이 굉장히 오래 걸리는 것을 확인 할 수 있다.

best_gb_clf = grid_cv2.best_estimator_
gb_pred = best_gb_clf.predict(X_test)
acc = accuracy_score(y_test, gb_pred)

print(f"예측 정확도:{acc: .4f}")
예측 정확도: 0.9420
  • 앞서 확인한 최적 하이퍼 파라미터를 적용하였을 때 test 데이터 셋의 예측 정확도는 약 94.20%이다.

Leave a comment