예전에 TED의 강연 중 한 강연에서 소를 보여주고 이 소의 무게를 측정해보라는 실험이 있었다. 이 때, 대중들은 적게 몇 kg에서 많게 t단위까지 예측 값을 제출하였다. 하지만 신기하게도 그 값들을 평균 하니 정말 소의 무게와 유사한 무게가 도출되었다. 이처럼 무작위의 여러 사람들의 대답은 한 전문가의 대답보다 나을 때가 많다. 이와 비슷한 인공지능 학습의 기법은 앙상블 학습이라고 한다.

앙상블의 기법에는 배깅(bagging), 부스팅(boosting), 스태킹(stacking) 등이 있고, 앞으로 이를 알압도록 하겠다.

투표 기반 분류기

Voting이라고 불리는 투표는 여로 개의 모델의 예측을 다수결을 적용 시켜 최종 결과를 도출하는 것을 의미한다. 코드를 통해 살펴보자.

import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier, VotingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score

iris = load_iris()
X = iris["data"][:, (2, 3)]
y = (iris["target"] == 2).astype(np.float64)

X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)

log_reg = LogisticRegression()
rnd_clf = RandomForestClassifier()
svm_clf = SVC()

voting_clf = VotingClassifier(
    estimators=[('lr', log_reg), ('rf', rnd_clf), ('svc', svm_clf)],
    voting='hard',
)

for clf in (log_reg, rnd_clf, svm_clf, voting_clf):
  clf.fit(X_train, y_train)
  y_pred = clf.predict(X_test)
  print(clf.__class__.__name__, accuracy_score(y_test, y_pred))

>> LogisticRegression 1.0
>> RandomForestClassifier 1.0
>> SVC 1.0
>> VotingClassifier 1.0

voting을 soft로 바꾸면 클래스의 확률을 뽑아낸다.

배깅과 페이스팅

훈련 세트에서 중복을 허용하여 샘플링하는 방식을 배깅이라고 부른다. 중복을 허용하지 않는 방법은 페이스팅이라고 부른다.

모든 모델이 학습을 마치면 모델들의 예측을 모아서 새로운 샘플에 대한 예측을 도출한다(일반적으로 최빈값을 도출한다).

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

bag_clf = BaggingClassifier(
    DecisionTreeClassifier(),
    n_estimators=500,
    max_samples=100,
    bootstrap=True,
    n_jobs=-1,
)
bag_clf.fit(X_train, y_train)

oob 평가

Out of bag인 oob 샘플은 샘플링되지 않은 샘플이다. 따라서 이러한 oob 샘플을 이용한 평가를 oob 평가라고 한다.

bag_clf = BaggingClassifier(
    DecisionTreeClassifier(),
    n_estimators=500,
    bootstrap=True,
    n_jobs=-1,
    oob_score=True,
)
bag_clf.fit(X_train, y_train)
print(bag_clf.oob_score_)

>> 0.9553571428571429

랜덤 포레스트

랜덤 포레스트는 배깅을 이용한 경정 트리의 앙상블이다. 이는 트리의 노드를 분할할 때 전체 feature 중에서 최선의 feature을 찾는 대신 무작위로 선택한 feature을 찾는 방법이다.

from sklearn.ensemble import RandomForestClassifier

rnd_clf = RandomForestClassifier(
    n_estimators=200,
    max_leaf_nodes=16,
    n_jobs=-1,
)

rnd_clf.fit(X_train, y_train)
print(rnd_clf.predict(X_test))

>> [0. 0. 1. 0. 0. 0. 0. 1. 0. 0. 1. 0. 0. 0. 0. 0. 1. 0. 0. 1. 0. 1. 0. 1.
 1. 1. 1. 1. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]

특성 중요도

랜덤 포레스트의 다른 장점은 feature의 상대적인 주용도를 측정하기 쉽다는 것이다. 코드를 통해 확인해보자.

iris = load_iris()
rnd_clf = RandomForestClassifier(n_estimators=500, n_jobs=-1)
rnd_clf.fit(iris.data, iris.target)
for name, score in zip(iris.feature_names, rnd_clf.feature_importances_):
  print(name, score)

>> sepal length (cm) 0.1149117945769322
>> sepal width (cm) 0.024384788200077383
>> petal length (cm) 0.4303892467011195
>> petal width (cm) 0.4303141705218709