Data Science/Machine Learning

Classification 2. 앙상블 학습(Ensemble Learning) - Voting과 Bagging

HJChung 2020. 10. 9. 14:52

권철민 강사님의 '파이썬 머신러닝 완벽 가이드'을 학습하고 정리한 것입니다. 배우는 중이라 잘못된  내용이 있을 수 있으며 계속해서 보완해 나갈 것입니다. :)) 

 

앙상블 학습이란, 

앙상블 학습을 통한 분류는 여러 개의 분류기(Classifier)을 생성하고 그 예측을 결합함으로써 보다 더 정확한 최종 예측을 도출하는 기법을 말한다. 특히 XGboost, LightGBM 등과 같튼 앙상블 모델을 정형 데이터의 분류나 회귀 분야에서 예측 성능이 매우 뛰어난 학습 모델을 만드는데 용이하다. 

앙상블의 특징

- 단일 모델의 약점을 다수의 모델들을 결합하여 보완

- 성능이 떨어지되, 서로 다른 유형, 계열의 모델들을 섞으면, 오히려 더 성능이 나아지기도 한다. 

예를 들어, 랜덤 포레스트나 뛰어난 부스팅 알고리즘들은 모두 결정 트리 알골리즘을 기반 알고리즘으로 적용하는데, 

결정 트리는 weak learner로써 과적합이라는 단점이 있지만, 이를 앙상블로 보완할 수 있기 때문이다. 

앙상블의 유형

1. Voting

2. Bagging - Random Forest

3. Boosting - AdaBoost, Gradient Boost, XGBoost(eXtra Gradient Boost), LightGBM(Light Gradient Boost)

4. Stacking

 

앙상블의 유형을 하나씩 살펴보자. 

1) Voting

※ Voting vs Bagging

Voting과 Bagging은 여러 개의 분류기가 투표를 통해 최종 예측 결과를 결정하는 방식이라는 점에서 유사하다. 

차이점은 Voting은 서로 다른 알고리즘을 가진 분류기가 같은 데이터셋을 기반으로 학습되고 결합하는 것이라면,  

Bagging은 같은 알고리즘 유형의 모델들이 이지만 데이터 샘플링을 다르게 하여 학습 데이터셋이 각각 다르다(단 교차 검증과 다르게 데이터 세트간 중첩을 허용). Bagging에 대해서는 뒤에 Bagging에 대해 정리할 때 더 자세히 정리해보겠다.  

 

1. Voting 유형

Voting에는 Hard voting과 Soft voting이 있다. 

Hard voting은 다수의 classifier의 예측 결과값을 다수결로 최종 class를 결정한다. 

Soft voting은 다수의 classifier의 예측 결과값간 확률을 평균하여 최종 class를 결정한다. 

일반적으로 Soft voting이 성능이 우수하여 주로 사용된다. 

출처: 파이썬 머신러닝 완벽 가이드

2. sklearn voting

class sklearn.ensemble.VotingClassifier(estimators, *, voting='hard', weights=None,
	n_jobs=None, flatten_transform=True, verbose=False)

주요 Parameter)

estimators: 리스트 형식으로 튜플 형식의 앙상블할 classifier를 넣어준다. 

voting: voting방식. Hard voting 방식이 default이다.

 

sklearn voting 코드)

사용할 데이터: sklearn 내장 데이터인 위스콘신 유방암 데이터

목적: 유방암의 악성 종양, 양성 종양 여부를 이진 분류를 통해 예측 분석

 

 

In [1]:
import pandas as pd

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
In [2]:
#load dataset
cancer_dataset = load_breast_cancer()

# cancer_dataset 그냥 찍어보니 이상하게 나옴.. dataframe화 해줘야 헸음. 
cancer_dataset_df = pd.DataFrame(cancer_dataset.data, columns=cancer_dataset.feature_names)
cancer_dataset_df.head()
Out[2]:
  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

In [3]:
# dataset split
X_train, X_test, y_train, y_test = train_test_split(cancer_dataset.data, cancer_dataset.target, test_size=0.2, random_state=121)
In [4]:
#weak learners: logistic regression, KNN
logistic_regression = LogisticRegression()
KNN = KNeighborsClassifier()

#votinng ensemble with these two weak learners
voting_ensemble = VotingClassifier(estimators=[("LogisticRegression", logistic_regression), ("KNN", KNN)],
                                  voting = 'soft')
In [5]:
# voting_ensemble model train/val/test
voting_ensemble.fit(X_train, y_train)
y_pred = voting_ensemble.predict(X_test)

print("voting 분류기 정확도 {0:.4f}".format(accuracy_score(y_test, y_pred)))
 
voting 분류기 정확도 0.9649
 
/Users/jeonghyeonjeong/opt/anaconda3/envs/pythonML/lib/python3.8/site-packages/sklearn/linear_model/_logistic.py:762: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
In [ ]:
 

 

2) Bagging

Bagging에 대한 것은 앞서 <※ Voting vs Bagging>에서 간단하게  살펴보았다. 

Bagging의 핵심은 데이터의 샘플링이다. 전체 학습 데이터에서 개별 데이터 세트들로 샘플링 하고, 대부분 결정 트리 알고리즘을 사용한다.

샘플링 된 개별 데이터 서브세트를 가지고 같은 (결정 트리) classifier이 학습을 하고, 각각의 class에 대한 확률을 평균을 내서 최종 예측값을 도출한다.

 

Bagging의 대표적인 결정 트리 알고리즘은 랜덤 포레스트(Random Forest)이다.  랜덤 포레스트 알고리즘은 앙상블 알고리즘들 중 비교적 빠른 수행 속돌를 보이며, 다양한 영역에서 높은 예측 성능을 보인다.

 

1. 랜덤 포레스트의 데이터 샘플링 방식

Bootstrapping

랜덤 포레스트의 개별적인 분류기 알고리즘은 결정 트리이지만

각각의 분류기가 학습하는 데이터 세트는 전체 데이터에서 일부가 중첩되기 생플링된 데이터세트이다.

이렇게 여러 개의 데이터 세트를 중첩되게 분리하는 것을 Bootsstraping분할 방식이라고 한다. 

 

2. sklearn Bagging-RandomForest

class sklearn.ensemble.RandomForestClassifier(n_estimators=100, *, criterion='gini',
	max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, 
    max_features='auto', max_leaf_nodes=None, min_impurity_decrease=0.0, 
    min_impurity_split=None, bootstrap=True, oob_score=False, n_jobs=None, 
    random_state=None, verbose=0, warm_start=False, class_weight=None, 
    ccp_alpha=0.0, max_samples=None)

주요 Parameter)

출처: 파이썬 머신러닝 완벽 가이드

sklearn Bagging-RandomForest 코드)

사용할 데이터: sklearn 내장 데이터인 위스콘신 유방암 데이터

목적: 유방암의 악성 종양, 양성 종양 여부를 이진 분류를 통해 예측 분석, 하이퍼 파라미터 튜닝으로 최적의 결과 찾기, feature 중요도 시각화 

 

 

In [1]:
import pandas as pd

from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.metrics import accuracy_score
In [2]:
cancer_dataset = load_breast_cancer()

cancer_dataset_df = pd.DataFrame(cancer_dataset.data, columns=cancer_dataset.feature_names)
cancer_dataset_df.head()
Out[2]:
  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

In [3]:
# dataset split
X_train, X_test, y_train, y_test = train_test_split(cancer_dataset.data, cancer_dataset.target, test_size=0.2, random_state=121)
In [4]:
bagging_rf_ensemble = RandomForestClassifier()

bagging_rf_ensemble.fit(X_train, y_train)
y_pred = bagging_rf_ensemble.predict(X_test)

print("bagging RandomForest 분류기 정확도 {0:.4f}".format(accuracy_score(y_test, y_pred)))
 
bagging RandomForest 분류기 정확도 0.9825
In [5]:
# random forest 의 hyper paramter 튜닝 - hyper parameter가 너무 많아 수동으로 하나하나 시도해보며 찾아주기가 어렵다. 
# 그래서 GridSearchCV를 이용한 후 , 최적의 것을 찾아주고 이를 이용하는 순서로 진행해본다. 
params = {
    'n_estimators': [100], #100개의 sub Dataset, Decision Tree
    'max_depth': [6, 8, 10, 12],
    'min_samples_leaf': [8, 12, 18],
    'min_samples_split': [8, 16, 20]    
    
}
In [6]:
bagging_rf_ensemble = RandomForestClassifier(random_state=0, n_jobs=-1) # n_jobs=-1로 하면 모든 cpu 코어를 이용하여 학습 가능
grid_cv = GridSearchCV(bagging_rf_ensemble, param_grid=params, cv=2, n_jobs=-1)
grid_cv.fit(X_train, y_train)
Out[6]:
GridSearchCV(cv=2, estimator=RandomForestClassifier(n_jobs=-1, random_state=0),
             n_jobs=-1,
             param_grid={'max_depth': [6, 8, 10, 12],
                         'min_samples_leaf': [8, 12, 18],
                         'min_samples_split': [8, 16, 20],
                         'n_estimators': [100]})
In [7]:
print("최적의 하이퍼 파라미터: ", grid_cv.best_params_)
print("최고의 예측 정확도: ", grid_cv.best_score_)
 
최적의 하이퍼 파라미터:  {'max_depth': 6, 'min_samples_leaf': 8, 'min_samples_split': 8, 'n_estimators': 100}
최고의 예측 정확도:  0.9472621531803076
In [8]:
#일단 이렇게 최적의 하이퍼 파라미터를 찾을 수 있다.
# 최적의 하이퍼 파라미터를 알았으니, 직관적으로 더 이점이 될 것같은 파라미터만 변경해주고 나머지는 이에 맞춰서 RandomForestClassifier를 다시 한번 학습시킨다. 

final_bagging_rf_ensemble = RandomForestClassifier(n_estimators=100, max_depth=6, min_samples_leaf=8, 
                                                   min_samples_split=8, random_state=0)

final_bagging_rf_ensemble.fit(X_train, y_train)
Out[8]:
RandomForestClassifier(max_depth=6, min_samples_leaf=8, min_samples_split=8,
                       random_state=0)
In [9]:
y_pred = final_bagging_rf_ensemble.predict(X_test)
print("예측 정확도:{0:.4f} ".format(accuracy_score(y_test, y_pred)))
 
예측 정확도:0.9737 
In [10]:
#feature 중요도도 그려볼 수 있다. 
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline

feature_importances_values = final_bagging_rf_ensemble.feature_importances_
feature_importances = pd.Series(feature_importances_values, index=cancer_dataset_df.columns)
feature_importances_top20 = feature_importances.sort_values(ascending=False)[:20] #중요한 순서로 정렬 후 top 20개만 뽑아 시각화

plt.figure(figsize=(8, 6))
plt.title('Feature Importances Top 20')
sns.barplot(x=feature_importances_top20, y=feature_importances_top20.index)
plt.show()
 
In [ ]: