AI 공부/머신러닝

(머신러닝) 앙상블

AI Sonny 2022. 9. 13. 18:14
728x90

기존 타이타닉 데이터를 이용하여 앙상블을 진행하겠다.

 

데이터 가져오기

 

import pandas as pd
import numpy as np
import seaborn as sns
from sklearn.preprocessing import OneHotEncoder
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score
df = sns.load_dataset('titanic') # 타이타닉 데이터 받아오기
cols = ["age","sibsp","parch","fare"] # 숫자니까 바로 사용
features = df[cols] # 피쳐
target = df["survived"] # 정답값
# one hot encoding
cols = ["pclass","sex","embarked"] # 범주형
enc = OneHotEncoder(handle_unknown='ignore')
tmp = pd.DataFrame(
    enc.fit_transform(df[cols]).toarray(),
    columns = enc.get_feature_names_out()
)
features = pd.concat([features,tmp],axis=1)
# 나이 결측치 채우기
features.age = features.age.fillna(features.age.median())

 

스케일링

 

from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()

features.loc[:,features.columns] = scaler.fit_transform(features) # 넘파이 형식에서 데이터프레임 형식으로 변환

 

마지막에 loc를 이용하여 넘파이 형식을 데이터프레임 형태로 바꾸었다.

 

SEED = 42

x_train,x_valid,y_train,y_valid = train_test_split(features,target,random_state=SEED,test_size=0.2)
x_train.shape,x_valid.shape,y_train.shape,y_valid.shape

=> ((712, 13), (179, 13), (712,), (179,))

 

학습데이터와 정답데이터를 분리하였다.

 

앙상블 학습(Ensemble Learning)

  • 기계학습에서 여러개의 개별 모델의 예측을 결함으로써 보다 정확한 예측을 도출하는 기법
 

앙상블 모델

 

Random Forest

  • 랜덤하게 일부 샘플들과 일부 피쳐들을 뽑아서 여러 개의 트리를 만들어서 앙상블하는 모델
  • 배깅 방식을 이용
  • 배깅(Bagging)
    • Bootstrap Aggregation의 약어
    • 샘플을 랜덤하게 여러번 뽑아 각 모델에 학습시켜 결과물을 집계하는 방법

 

from sklearn.ensemble import RandomForestClassifier

hp = {
    "random_state" : SEED,
    "max_features" : "sqrt", # None을 줄 경우 전체피쳐 사용
    "n_estimators" : 100, # 트리개수를 의미 (잘 기억해두기)
    "max_depth" : 10,
    "min_samples_split" : 10,
    "min_samples_leaf" : 3
}
model = RandomForestClassifier(**hp)
model.fit(x_train,y_train)
pred = model.predict_proba(x_valid)[:,1]
roc_auc_score(y_valid,pred)

=> 0.8952380952380953

 

배깅 적용 후

 

from sklearn.ensemble import BaggingClassifier
from sklearn.linear_model import LogisticRegression

hp = {
    "random_state" : SEED,
    "base_estimator" : LogisticRegression(random_state=SEED), # None이면 결정트리를 사용함
    "n_estimators" : 100, # base_estomator 개수
    "max_features" : 0.5 # 추출할 샘플 비율
}
model = BaggingClassifier(**hp)
model.fit(x_train,y_train)
pred = model.predict_proba(x_valid)[:,1]
roc_auc_score(y_valid,pred)

=> 0.8698841698841698

 

Voting

  • 여러 모델들의 예측값을 투표방식(hard) or 평균방식(soft)으로 앙상블

 

soft 방식

 

from sklearn.neural_network import MLPClassifier
from sklearn.ensemble import VotingClassifier

estimators = [
    ( "mlp",MLPClassifier(max_iter=1000,random_state=SEED) ),
    ( "lr", LogisticRegression(random_state=SEED) ),
    ( "rf", RandomForestClassifier(random_state=SEED) )
]

hp = {
    "estimators" : estimators,
    "voting" : "soft"
}
model = VotingClassifier(**hp)
model.fit(x_train,y_train)
pred = model.predict_proba(x_valid)[:,1]
roc_auc_score(y_valid,pred)

=> 0.9063063063063064

 

각 예측값을 평균낸다.

 

hard 방식

 

from sklearn.metrics import f1_score
from sklearn.neural_network import MLPClassifier
from sklearn.ensemble import VotingClassifier

estimators = [
    ( "mlp",MLPClassifier(max_iter=1000,random_state=SEED) ), # 0과 1의 과반수
    ( "lr", LogisticRegression(random_state=SEED) ),
    ( "rf", RandomForestClassifier(random_state=SEED) )
]

hp = {
    "estimators" : estimators,
    "voting" : "hard"
}
model = VotingClassifier(**hp)
model.fit(x_train,y_train)
pred = model.predict(x_valid)
f1_score(y_valid,pred) # 0과 1로 결정된 값

=> 0.7659574468085106

 


Stacking

  • 여러 모델들의 예측값을 최종모델(메타모델)의 학습 데이터로 사용해서 예측하는 방법
  • 과적합 방지하기 위해 내부적으로 CV를 진행한다.
from sklearn.ensemble import StackingClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.tree import DecisionTreeClassifier

estimators = [
    ( "knn",KNeighborsClassifier(n_neighbors=10,weights="distance") ),
    ( "dt", DecisionTreeClassifier(max_depth=3,random_state=SEED) ),
    ( "rf", RandomForestClassifier(random_state=SEED) )
]

hp = {
    "estimators" : estimators,
    "final_estimator" : LogisticRegression(random_state=SEED)
}

model = StackingClassifier(**hp,n_jobs = -1) # n_job -1 = cpu를 전부 다 사용하여 빠르게 나오도록한다. 있으면 무조건 사용
model.fit(x_train,y_train)
pred = model.predict_proba(x_valid)[:,1]
roc_auc_score(y_valid,pred)

=> 0.8820463320463321

 


GradientBoosting

  • 트리기반 부스팅 앙상블 모델
  • 머신러닝 알고리즘 중에서 가장 예측성능이 높다고 알려졌고, 인기있는 알고리즘

- Boosting

  • 약한 모델을 결합하여 강한 모델을 만드는 과정
  • 배깅과 다른점은 순차적으로 모델을 만들어 각 모델의 예측결과를 결합

 

혹시 모르니 데이터 복사해놓기 (튜플형태로 복사됨!)

 

data_backup = x_train.copy(), x_valid.copy(), y_train.copy(), y_valid.copy()

 

from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.metrics import mean_squared_error
from sklearn.datasets import load_diabetes
diabetes = load_diabetes()
data = diabetes.data
target = diabetes.target

data.shape, target.shape

=> ((442, 10), (442,))

 

데이터 셋 분리

 

x_train,x_valid,y_train,y_valid = train_test_split(data,target,random_state=SEED)
x_train.shape,x_valid.shape,y_train.shape,y_valid.shape

=> ((331, 10), (111, 10), (331,), (111,))

 

GradientBoostingRegressor 구현
 
hp = {
    "max_depth" : 2,
    "random_state" : SEED
}
weak_1 = DecisionTreeRegressor(**hp)
weak_1.fit(x_train,y_train)
pred = weak_1.predict(x_train)

 

residual = y_train - pred

weak_2 = DecisionTreeRegressor(**hp)
weak_2.fit(x_train,residual)
pred = weak_2.predict(x_train)

 

residual = residual - pred

weak_3 = DecisionTreeRegressor(**hp)
weak_3.fit(x_train,residual)

 

pred = weak_1.predict(x_valid) + weak_2.predict(x_valid) + weak_3.predict(x_valid)
mean_squared_error(y_valid,pred) ** 0.5 # RMSE

위 과정은 그냥 구현한 것이고, 밑에는 GradientBoostingRegressor를 사용하여 구현한 것이다.

 

그래서 결과값이 같다.

 

gbr = GradientBoostingRegressor(max_depth=2,random_state=SEED,n_estimators=3,learning_rate=1.) # 위와 같은 조건을 넣어줌!
gbr.fit(x_train,y_train)
pred = gbr.predict(x_valid)
mean_squared_error(y_valid,pred) ** 0.5

=> 61.872491186086826

 

- 복사한 타이타닉 데이터 복원하기

 

x_train, x_valid, y_train, y_valid = data_backup

 

from sklearn.ensemble import GradientBoostingClassifier

hp = {
    "random_state" : SEED,
    "max_depth" : 2,
    "n_estimators" : 100, # 수행할 부스팅 단계 수
}
model = GradientBoostingClassifier(**hp)
model.fit(x_train,y_train)
pred = model.predict_proba(x_valid)[:,1]
roc_auc_score(y_valid,pred)

=> 0.8911196911196911

 


XGBoost

  • 병렬처리가 불가능한 GBM의 단점을 보완
  • GPU를 지원
  • 내장된 교차검증과 결측치 처리같은 부가기능도 있다.
  • GBM보다 속도가 향상
  • 과적합 방지를 GBM보다 더 뛰어나게 방지한다.
  • https://xgboost.readthedocs.io/en/stable/
from xgboost import XGBClassifier,plot_importance
hp = {
    "random_state" : SEED,
    "max_depth" : 2,
    "n_estimators" : 100, # 수행할 부스팅 단계수
}

model = XGBClassifier(**hp)
model.fit(x_train,y_train)
pred = model.predict_proba(x_valid)[:,1]
roc_auc_score(y_valid,pred)

=> 0.8868082368082368

 

시각화

import matplotlib.pyplot as plt
plot_importance(model)
plt.show()

 

from xgboost import to_graphviz
to_graphviz(model)

 

 


LightGBM (많이 사용)

 

from lightgbm import LGBMClassifier, plot_importance
hp = {
    "random_state" : SEED,
    "max_depth" : 2,
    "n_estimators" : 100, # 수행할 부스팅 단계 수
}

model = LGBMClassifier(**hp)
model.fit(x_train,y_train)
pred = model.predict_proba(x_valid)[:,1]
roc_auc_score(y_valid,pred)

=> 0.8821106821106821

 

시각화

 

plot_importance(model)
plt.show()

 

from lightgbm import create_tree_digraph
create_tree_digraph(model)

 


catboost

  • 범주형 변수에 대하여 강력한 성능을 보여주는 GBM 기반 모델 
  • 범주형 변수가 많을 경우 높은 성능과 함께 속도가 lightgbm 보다 빠르다.
  • 수치형 변수가 많을 경우 매우 느림
  • 특징으로 범주형 변수를 인코딩 하지 않고 넣어도 된다.
  • https://catboost.ai/en/docs/

 

코랩에서는 catboost가 없어서 설치해야한다.

!pip install catboost

 

from catboost import CatBoostClassifier

hp = {
    "random_state" : SEED,
    "max_depth" : 2,
    "n_estimators" : 100, # 수행할 부스팅 단계 수
    "verbose" : 0 # 부스팅 단계 출력 안보이게 하기
}

model = CatBoostClassifier(**hp)
model.fit(x_train,y_train)
pred = model.predict_proba(x_valid)[:,1]
roc_auc_score(y_valid,pred)

=> 0.8787001287001287

 

catboost는 높은 성능과 함께 속도가 lightgbm 보다 빠르기 때문에

 

수치형을 범주형으로 변경해서 넣으면 좋을 수도 있다.


앙상블을 보고, 다양한 모델들을 각자 합리적인 방법으로 적용시키는 것이 신기하였다.

 

이것도 각 특징을 잘 알고, 잘써야겠다는 생각이 들었다.

728x90