상세 컨텐츠

본문 제목

[ML/python] 앙상블 (voting, bagging, boosting, stacking, 랜덤포레스트, XGBoost, LightGBM)

테크/ML

by fiftyline 2025. 2. 15. 20:31

본문

 

앙상블

앙상블(Ensemble model)은 여러 개의 모델을 조합하여 성능을 향상시키는 기법이다.

단일 모델에 비해 일반화 성능이 높고, 과적합 가능성을 줄일 수 있다.

앙상블 방법에는 voting, bagging, boosting, stacking이 있으며, 이를 활용한 대표적인 모델로 랜덤포레스트, XGBoost, LightGBM 등이 있다.

 

대표적인 앙상블 모델은 기본 모델로 깊이가 얕은 결정트리를 사용한다.

이유 1) 얕은 결정 트리는 적은 데이터로 학습하더라도 과적합될 가능성이 매우 작다. 특히 배깅은 붓스트랩된 작은 데이터로 모델을 학습하므로 과적합 위험이 적은 모델을 사용해야 한다.

이유 2) 시드가 다르면 같은 데이터로 학습한 결정 트리일지라도 다르게 분지될 수 있다.

 

 

Voting (보팅)

여러 개의 서로 다른 모델을 학습시켜 평균 또는 최빈값으로 최종 예측한다.

같은 데이터로 여러 모델을 만들기에 각 모델의 예측 결과가 비슷해질 수 있다는 단점이 있다. 따라서 보팅을 사용할 때에는 서로 크게 다른 알고리즘을 조합하는 것이 성능 향상에 효과적이다.

  • Hard Voting: 각 모델이 예측한 클래스 중 가장 많이 선택된 클래스가 최종 예측값
  • Soft Voting: 각 모델이 출력한 확률 값(예측 확률의 평균)을 이용하여 최종 예측
더보기

python code 예시

from sklearn.ensemble import VotingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.tree import DecisionTreeClassifier

# 개별 모델 생성
model1 = LogisticRegression()
model2 = SVC(probability=True)  # Soft Voting을 위해 probability=True 설정
model3 = DecisionTreeClassifier()

# Voting Classifier (Soft Voting)
voting_clf = VotingClassifier(estimators=[('lr', model1), ('svm', model2), ('dt', model3)], voting='soft')

 

Bagging (배깅)

붓스트랩(Bootstrap)을 통해 샘플링한 데이터로 여러개의 독립적인 모델(약한 학습기)을 학습하여 개별 모델의 평균 또는 최빈값으로 결합 후 최종 예측한다. 붓스트랩은 복원 랜덤 샘플링으로 원 데이터에서 행과 열을 임의로 골라 작은 데이터를 샘플링하는 방법이다.

개별 모델이 독립적이기때문에 모델 병렬 학습이 가능하므로 속도가 빠르고, 분산이 감소하여 과적합을 방지할 수 있다.

더보기

python code 예시

from sklearn.ensemble import BaggingClassifier
from sklearn.tree import DecisionTreeClassifier

# 배깅을 적용한 의사결정트리 모델
bagging_clf = BaggingClassifier(base_estimator=DecisionTreeClassifier(), n_estimators=10)

 

  • 랜덤포레스트

기본 모델이 결정트리인 배깅 모델이다.

주요 하이퍼파라미터는 기본 모델 개수와 붓스트랩 관련 하이퍼파라미터 제외 시, 결정 트리의 하이퍼파라미터와 같다.

기본 모델의 개수가 많을수록 모델이 복잡해지지만 각 모델이 학습에 사용하는 데이터 크기는 비슷하므로 과적합 위험이 적다.

# 데이터 로드 및 분할
df1 = pd.read_csv("../../data/classification/optdigits.csv")
df2 = pd.read_csv("../../data/regression/baseball.csv")
X1 = df1.drop('y', axis = 1)
y1 = df1['y']
X1_train, X1_test, y1_train, y1_test = train_test_split(X1, y1, random_state = 50)
X2 = df2.drop('y', axis = 1)
y2 = df2['y']
X2_train, X2_test, y2_train, y2_test = train_test_split(X2, y2, random_state = 50)

# 결정트리와 랜덤포레스트 비교 (분류)
from sklearn.tree import DecisionTreeClassifier as DTC, DecisionTreeRegressor as DTR
from sklearn.ensemble import RandomForestClassifier as RFC, RandomForestRegressor as RFR
from sklearn.metrics import *
dtc = DTC(max_depth = 10, random_state = 50).fit(X1_train, y1_train)
rfc = RFC(max_depth = 5, random_state = 50).fit(X1_train, y1_train)
dtc_pred = dtc.predict(X1_test)
rfc_pred = rfc.predict(X1_test)
dtc_acc = accuracy_score(y1_test, dtc_pred)
rfc_acc = accuracy_score(y1_test, rfc_pred)
print(dtc_acc, rfc_acc)  #정확도 비교

# 시드가 다른 결정트리 사용 확인
print(rfc[0])
print(rfc[50])

# 결정트리와 랜덤포레스트 비교 (회귀)
dtr = DTR(max_depth = 10, random_state = 50).fit(X2_train, y2_train)
rfr = RFR(max_depth = 5, random_state = 50).fit(X2_train, y2_train)
dtr_pred = dtr.predict(X2_test)
rfr_pred = rfr.predict(X2_test)
dtr_mae = mean_absolute_error(y2_test, dtr_pred)
rfr_mae = mean_absolute_error(y2_test, rfr_pred)
print(dtr_mae, rfr_mae) #MAE 비교

 

 

Boosting (부스팅)

약한 학습기를 순차적으로 학습하는 방식으로, 이전 모델이 틀린 부분을 보완하여 다음 모델에서 순차적으로 학습한다. (이전 모델이 틀린 샘플에 가중치를 더 부여)

오차가 큰 샘플에 집중하여 학습하므로 정확도가 향상된 모델이지만, 연산량이 많아 시간이 오래걸리고 과적합 가능성이 있다.

더보기

python code 예시

from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier

# AdaBoost 모델
adaboost_clf = AdaBoostClassifier(base_estimator=DecisionTreeClassifier(), n_estimators=50)

 

  • XGBoost (Extreme Gradient Boosting)

Gradient Boosting(오차 기울기를 활용하여 가중치 조정)을 개선한 알고리즘으로, 트리 내부에서 병렬 연산을 수행하여 속도를 높였다.

균형 분할(level wise)를 사용하며, 트리 깊이를 조절하며 성능을 높인다.

각 모델을 학습할 때 이전 모델의 오차를 얼마나 반영할지를 나타내는 하이퍼파라미터로 학습률이 있다. 학습률이 클수록 이전 트리의 오차를 과하게 반영하므로 과적합 위험이 높아진다.

손실함수

 

f_k (k=1,2,...,K) : 각 결정나무

E_i : 샘플 i에 대한 오차

Φ(f_k) : f_k의 모델 보잡도(잎 노드 개수 등으로 계산)

k개 모델에 대한 샘플i 예측결과

 

이전 k-1개의 모델로 예측한 값과 f_k가 샘플 i의 라벨을 예측한 값 f_k(x^i)을 더해서

샘플 i에 대한 k개 모델에 대한 예측 결과를 종합

 

# 결정트리와 XGBoost 비교 (분류, 회귀)
from xgboost import XGBClassifier as XGBC, XGBRegressor as XGBR
xgbc = XGBC(random_state = 50, learning_rate = 0.1, max_depth = 5).fit(X1_train, y1_train)
xgbr = XGBR(random_state = 50, learning_rate = 0.1, max_depth = 5).fit(X2_train, y2_train)
xgbc_pred = xgbc.predict(X1_test)
xgbr_pred = xgbr.predict(X2_test)
acc = accuracy_score(y1_test, xgbc_pred)
mae = mean_absolute_error(y2_test, xgbr_pred)
print(acc, mae)

 

  • LightGBM

XGBoost와 다르게 잎노드 중심의 비균형 분할(leaf wise)을 사용해 깊고 비대칭적인 트리를 만든다.

불필요한 분지를 하지 않으므로 예측 오차가 적고 모델이 가볍고 빠르다. 그렇기에 과적합 가능성이 있다.

잎 노드가 깊이에 비례하지 않으므로 최대 깊이보다는 최개 잎 노드 개수를 설정하는 것이 좋다.

메모리 사용량이 적고 GPU 학습을 지원해 대규모 데이터 학습에 최적화되어있다.

# 결정트리와 LightGBM 비교 (분류, 회귀)
from lightgbm import LGBMClassifier as LGBC, LGBMRegressor as LGBR
lgbc = LGBC(random_state = 50, learning_rate = 0.1, num_leaves = 32).fit(X1_train, y1_train)
lgbr = LGBR(random_state = 50, learning_rate = 0.1, num_leaves = 32).fit(X2_train, y2_train)
lgbc_pred = lgbc.predict(X1_test)
lgbr_pred = lgbr.predict(X2_test)
acc = accuracy_score(y1_test, lgbc_pred)
mae = mean_absolute_error(y2_test, lgbr_pred)
print(acc, mae)

 

 

Staking (스태킹)

서로 다른 모델의 예측 결과를 입력받는 메타모델을 사용해 최종 예측한다.

개별 모델의 예측값을 특징으로 사용하고 데이터의 라벨을 그대로 라벨에 사용해 학습한다.

다양한 모델을 조합 가능하지만 연산 비용이 크다.

더보기

python code 예시

from sklearn.ensemble import StackingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.svm import SVC

# 개별 모델 (기초 학습기)
estimators = [('dt', DecisionTreeClassifier()), ('svm', SVC(probability=True))]

# 메타 모델 (최종 학습기)
stacking_clf = StackingClassifier(estimators=estimators, final_estimator=LogisticRegression())




 

 

 

 

참고

GIL's LAB, 「파이썬을 활용한 머신러닝 자동화 시스템 구축」, 위키북스(2022)

관련글 더보기