🔥알림🔥
테디노트 유튜브 -
구경하러 가기!
머신러닝 실전 앙상블 (Ensemble)과 Hyperparameter 튜닝
머신러닝 알고리즘의 끝판왕인 앙상블(Ensemble) 알고리즘에 대하여 알아보도록 하겠습니다. 앙상블 알고리즘은 방법론 적인 측면에서 Voting, Bagging, Boosting 알고리즘등으로 나뉠 수 있겠고, 앙상블의 앙상블 알고리즘인 Stacking 그리고 Weighted Blending 등의 기법도 알아보도록 하겠습니다. 앙상블 알고리즘은 단일 알고리즘 대비 성능이 좋기 때문에, 캐글(Kaggle.com)과 같은 데이터 분석 대회에서도 상위권에 항상 꼽히는 알고리즘 들입니다.
데이터의 종류, 형태에 따라 어떤 알고리즘이 우세한지는 직접 적용해보고 성능 평가를 통해 알아볼 수는 있겠지만, 최근에는 LGBM 그리고 XGBoost와 같은 부스팅 계열의 알고리즘이 대회의 상위권을 휩쓸고 있습니다. 부스팅 계열의 앙상블 알고리즘이 정형 데이터에서 강세를 보이고 있습니다.
이 튜토리얼의 후반부에는 RandomSearchCV
와 GridSearchCV
라는 Hyperparameter 튜닝기도 다루고 있습니다. 물론, 시간과 리소스가 많이 투여되는 부부이기 때문에 최근에는 bayesian optimization을 기반으로 찾는 HyperOpt
도 주목 받고 있습니다. 나중에 HyperOpt
에 대한 내용도 다뤄보도록 하겠습니다.
튜토리얼은 보스톤 집 값 데이터를 토대로 집 값 예측을 해보는 튜토리얼이며, sklearn.dataset
에 포함된 load_boston
으로 진행합니다. 따라서, 별도의 파일 데이터는 다운로드 받을 필요 없습니다.
코드
앙상블(Ensemble)
머신러닝 앙상블이란 여러개의 머신러닝 모델을 이용해 최적의 답을 찾아내는 기법이다.
- 여러 모델을 이용하여 데이터를 학습하고, 모든 모델의 예측결과를 평균하여 예측
앙상블 기법의 종류
- 보팅 (Voting): 투표를 통해 결과 도출
- 배깅 (Bagging): 샘플 중복 생성을 통해 결과 도출
- 부스팅 (Boosting): 이전 오차를 보완하면서 가중치 부여
- 스태킹 (Stacking): 여러 모델을 기반으로 예측된 결과를 통해 meta 모델이 다시 한번 예측
참고자료 (블로그)
실습을 위한 데이터셋 로드
# 튜토리얼 진행을 위한 모듈 import
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from IPython.display import Image
np.set_printoptions(suppress=True, precision=3)
SEED = 30
from sklearn.datasets import load_boston
load_boston
데이터셋 로드
data = load_boston()
컬럼 소개
속성 수 : 13
- CRIM: 범죄율
- ZN: 25,000 평방 피트 당 주거용 토지의 비율
- INDUS: 비소매(non-retail) 비즈니스 면적 비율
- CHAS: 찰스 강 더미 변수 (통로가 하천을 향하면 1; 그렇지 않으면 0)
- NOX: 산화 질소 농도 (천만 분의 1)
- RM:주거 당 평균 객실 수
- AGE: 1940 년 이전에 건축된 자가 소유 점유 비율
- DIS: 5 개의 보스턴 고용 센터까지의 가중 거리
- RAD: 고속도로 접근성 지수
- TAX: 10,000 달러 당 전체 가치 재산 세율
- PTRATIO 도시 별 학생-교사 비율
- B: 1000 (Bk-0.63) ^ 2 여기서 Bk는 도시 별 검정 비율입니다.
- LSTAT: 인구의 낮은 지위
- MEDV: 자가 주택의 중앙값 (1,000 달러 단위)
df = pd.DataFrame(data['data'], columns=data['feature_names'])
df['target'] = data['target']
df.head()
train / test 데이터를 분할 합니다.
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(df.drop('target', 1), df['target'], random_state=SEED)
x_train.shape, x_test.shape
x_train.head()
y_train.head()
모델별 성능 확인을 위한 함수
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.metrics import mean_squared_error
my_predictions = {}
my_pred = None
my_actual = None
my_name = None
colors = ['r', 'c', 'm', 'y', 'k', 'khaki', 'teal', 'orchid', 'sandybrown',
'greenyellow', 'dodgerblue', 'deepskyblue', 'rosybrown', 'firebrick',
'deeppink', 'crimson', 'salmon', 'darkred', 'olivedrab', 'olive',
'forestgreen', 'royalblue', 'indigo', 'navy', 'mediumpurple', 'chocolate',
'gold', 'darkorange', 'seagreen', 'turquoise', 'steelblue', 'slategray',
'peru', 'midnightblue', 'slateblue', 'dimgray', 'cadetblue', 'tomato'
]
def plot_predictions(name_, pred, actual):
df = pd.DataFrame({'prediction': pred, 'actual': y_test})
df = df.sort_values(by='actual').reset_index(drop=True)
plt.figure(figsize=(11, 8))
plt.scatter(df.index, df['prediction'], marker='x', color='r')
plt.scatter(df.index, df['actual'], alpha=0.7, marker='o', color='black')
plt.title(name_, fontsize=15)
plt.legend(['prediction', 'actual'], fontsize=12)
plt.show()
def mse_eval(name_, pred, actual):
global my_predictions, colors, my_pred, my_actual, my_name
my_name = name_
my_pred = pred
my_actual = actual
plot_predictions(name_, pred, actual)
mse = mean_squared_error(pred, actual)
my_predictions[name_] = mse
y_value = sorted(my_predictions.items(), key=lambda x: x[1], reverse=True)
df = pd.DataFrame(y_value, columns=['model', 'mse'])
print(df)
min_ = df['mse'].min() - 10
max_ = df['mse'].max() + 10
length = len(df) / 2
plt.figure(figsize=(9, length))
ax = plt.subplot()
ax.set_yticks(np.arange(len(df)))
ax.set_yticklabels(df['model'], fontsize=12)
bars = ax.barh(np.arange(len(df)), df['mse'], height=0.3)
for i, v in enumerate(df['mse']):
idx = np.random.choice(len(colors))
bars[i].set_color(colors[idx])
ax.text(v + 2, i, str(round(v, 3)), color='k', fontsize=12, fontweight='bold', verticalalignment='center')
plt.title('MSE Error', fontsize=16)
plt.xlim(min_, max_)
plt.show()
def add_model(name_, pred, actual):
global my_predictions, my_pred, my_actual, my_name
my_name = name_
my_pred = pred
my_actual = actual
mse = mean_squared_error(pred, actual)
my_predictions[name_] = mse
def remove_model(name_):
global my_predictions
try:
del my_predictions[name_]
except KeyError:
return False
return True
def plot_all():
global my_predictions, my_pred, my_actual, my_name
plot_predictions(my_name, my_pred, my_actual)
y_value = sorted(my_predictions.items(), key=lambda x: x[1], reverse=True)
df = pd.DataFrame(y_value, columns=['model', 'mse'])
print(df)
min_ = df['mse'].min() - 10
max_ = df['mse'].max() + 10
length = len(df) / 2
plt.figure(figsize=(9, length))
ax = plt.subplot()
ax.set_yticks(np.arange(len(df)))
ax.set_yticklabels(df['model'], fontsize=12)
bars = ax.barh(np.arange(len(df)), df['mse'], height=0.3)
for i, v in enumerate(df['mse']):
idx = np.random.choice(len(colors))
bars[i].set_color(colors[idx])
ax.text(v + 2, i, str(round(v, 3)), color='k', fontsize=12, fontweight='bold', verticalalignment='center')
plt.title('MSE Error', fontsize=16)
plt.xlim(min_, max_)
plt.show()
단일 회귀예측 모델 (지난 시간)
from sklearn.linear_model import LinearRegression
from sklearn.linear_model import Ridge
from sklearn.linear_model import Lasso
from sklearn.linear_model import ElasticNet
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import PolynomialFeatures
LinearRegression
- 기본 옵션 값을 사용하여 학습합니다.
linear_reg = LinearRegression()
linear_reg.fit(x_train, y_train)
pred = linear_reg.predict(x_test)
mse_eval('LinearRegression', pred, y_test)
Ridge
- 규제 계수인
alpha=0.1
을 적용합니다.
ridge = Ridge(alpha=0.1)
ridge.fit(x_train, y_train)
pred = ridge.predict(x_test)
mse_eval('Ridge(alpha=0.1)', pred, y_test)
Lasso
- 규제 계수인
alpha=0.01
로 적용합니다.
lasso = Lasso(alpha=0.01)
lasso.fit(x_train, y_train)
pred = lasso.predict(x_test)
mse_eval('Lasso(alpha=0.01)', pred, y_test)
ElasticNet
- 규제 계수인
alpha=0.01
,l1_ratio=0.8
을 적용합니다.
elasticnet = ElasticNet(alpha=0.01, l1_ratio=0.8)
elasticnet.fit(x_train, y_train)
pred = elasticnet.predict(x_test)
mse_eval('ElasticNet(alpha=0.1, l1_ratio=0.8)', pred, y_test)
Pipeline 학습
StandardScaler와 ElasticNet의 파이프라인 학습 합니다.
- ElasticNet 모델은 규제 계수인
alpha=0.01
,l1_ratio=0.8
을 적용합니다.
elasticnet_pipeline = make_pipeline(
StandardScaler(),
ElasticNet(alpha=0.01, l1_ratio=0.8)
)
elasticnet_pipeline.fit(x_train, y_train)
elasticnet_pred = elasticnet_pipeline.predict(x_test)
mse_eval('Standard ElasticNet', elasticnet_pred, y_test)
PolynomialFeatures
PolynomialFeatures와 ElasticNet의 파이프라인 학습을 진행합니다.
- PolynomialFeatures는
degree=2
,include_bias=False
로 적용합니다. - ElasticNet 모델은 규제 계수인
alpha=0.1
,l1_ratio=0.2
을 적용합니다.
poly_pipeline = make_pipeline(
PolynomialFeatures(degree=2, include_bias=False),
ElasticNet(alpha=0.1, l1_ratio=0.2)
)
poly_pipeline.fit(x_train, y_train)
poly_pred = poly_pipeline.predict(x_test)
mse_eval('Poly ElasticNet', poly_pred, y_test)
앙상블 (Ensemble)
보팅 (Voting) - 회귀 (Regression)
Voting은 단어 뜻 그대로 투표를 통해 결정하는 방식입니다. Voting은 Bagging과 투표방식이라는 점에서 유사하지만, 다음과 같은 큰 차이점이 있습니다.
- Voting은 다른 알고리즘 model을 조합해서 사용합니다.
- Bagging은 같은 알고리즘 내에서 다른 sample 조합을 사용합니다.
from sklearn.ensemble import VotingRegressor, VotingClassifier
반드시, Tuple 형태로 모델을 정의해야 합니다.
single_models = [
('linear_reg', linear_reg),
('ridge', ridge),
('lasso', lasso),
('elasticnet_pipeline', elasticnet_pipeline),
('poly_pipeline', poly_pipeline)
]
voting_regressor = VotingRegressor(single_models, n_jobs=-1)
voting_regressor.fit(x_train, y_train)
voting_pred = voting_regressor.predict(x_test)
mse_eval('Voting Ensemble', voting_pred, y_test)
보팅 (Voting) - 분류 (Classification)
분류기 모델을 만들때, Voting 앙상블은 1가지의 중요한 parameter가 있습니다.
voting
= {'hard', 'soft'}
hard로 설정한 경우
class를 0, 1로 분류 예측을 하는 이진 분류를 예로 들어 보겠습니다.
Hard Voting 방식에서는 결과 값에 대한 다수 class를 차용합니다.
classification을 예로 들어 보자면, 분류를 예측한 값이 1, 0, 0, 1, 1 이었다고 가정한다면 1이 3표, 0이 2표를 받았기 때문에 Hard Voting 방식에서는 1이 최종 값으로 예측을 하게 됩니다.
soft
soft vote 방식은 각각의 확률의 평균 값을 계산한다음에 가장 확률이 높은 값으로 확정짓게 됩니다.
가령 class 0이 나올 확률이 (0.4, 0.9, 0.9, 0.4, 0.4)이었고, class 1이 나올 확률이 (0.6, 0.1, 0.1, 0.6, 0.6) 이었다면,
- class 0이 나올 최종 확률은 (0.4+0.9+0.9+0.4+0.4) / 5 = 0.44,
- class 1이 나올 최종 확률은 (0.6+0.1+0.1+0.6+0.6) / 5 = 0.4
가 되기 때문에 앞선 Hard Vote의 결과와는 다른 결과 값이 최종 으로 선출되게 됩니다.
from sklearn.ensemble import VotingClassifier
from sklearn.linear_model import LogisticRegression, RidgeClassifier
models = [
('Logi', LogisticRegression()),
('ridge', RidgeClassifier())
]
voting 옵션에 대하여 지정합니다.
vc = VotingClassifier(models, voting='hard')
배깅(Bagging)
Bagging은 Bootstrap Aggregating의 줄임말입니다.
- Bootstrap = Sample(샘플) + Aggregating = 합산
Bootstrap은 여러 개의 dataset을 중첩을 허용하게 하여 샘플링하여 분할하는 방식
데이터 셋의 구성이 [1, 2, 3, 4, 5 ]로 되어 있다면,
- group 1 = [1, 2, 3]
- group 2 = [1, 3, 4]
- group 3 = [2, 3, 5]
Image('https://teddylee777.github.io/images/2019-12-17/image-20191217015537872.png')
Voting VS Bagging
- Voting은 여러 알고리즘의 조합에 대한 앙상블
- Bagging은 하나의 단일 알고리즘에 대하여 여러 개의 샘플 조합으로 앙상블
대표적인 Bagging 앙상블
- RandomForest
- Bagging
RandomForest
- DecisionTree(트리)기반 Bagging 앙상블
- 굉장히 인기있는 앙상블 모델
- 사용성이 쉽고, 성능도 우수함
from sklearn.ensemble import RandomForestRegressor, RandomForestClassifier
rfr = RandomForestRegressor()
rfr.fit(x_train, y_train)
rfr_pred = rfr.predict(x_test)
mse_eval('RandomForest Ensemble', rfr_pred, y_test)
주요 Hyperparameter
random_state
: 랜덤 시드 고정 값. 고정해두고 튜닝할 것!n_jobs
: CPU 사용 갯수max_depth
: 깊어질 수 있는 최대 깊이. 과대적합 방지용n_estimators
: 앙상블하는 트리의 갯수max_features
: 최대로 사용할 feature의 갯수. 과대적합 방지용min_samples_split
: 트리가 분할할 때 최소 샘플의 갯수. default=2. 과대적합 방지용
Image('https://teddylee777.github.io/images/2020-01-09/decistion-tree.png', width=600)