앙상블과 랜덤 포레스트

어느 전문가 한 사람의 의견보다 여러 대중의 지혜를 모으는 것이 더 낫듯이, 앙상블 학습(Ensemble Learning)은 일련의 예측기로부터 예측을 수집하는 기법을 말한다. 개별 트리의 예측을 모아 가장 많은 선택을 받은 클래스를 앙상블의 예측으로 하는 것을 랜덤 포레스트라고 한다.


투표 기반 분류기

여러 분류기의 예측을 집계(투표)하여 가장 많은 표를 얻은 클래스를 예측으로 하는 기법을 직접 투표(hard voting)라고 한다. 많은 경우 약한 학습기(weak learner, 랜덤 분류기보다 조금 성능이 좋음)가 충분히 모인 앙상블은 강한 학습기(strong learner)가 될 수 있다. 이는 큰수의 법칙에서 그 근거를 찾을 수 있다.

앙상블 모델은 모든 분류기가 완벽하게 독립적일 때 최고의 성능을 발휘한다.

다음은 세 가지 분류기로 구성된 투표 기반 분류기이다.

from sklearn.datasets import make_moons
from sklearn.ensemble import RandomForestClassifier, VotingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC

X, y = make_moons(n_samples=500, noise=0.30, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)

voting_clf = VotingClassifier(
    estimators=[
        ('lr', LogisticRegression(random_state=42)),
        ('rf', RandomForestClassifier(random_state=42)),
        ('svc', SVC(random_state=42))
    ]
)
voting_clf.fit(X_train, y_train)

VotingClassifier는 모든 추정기를 복제하여 복제된 추정기를 훈련한다. 원본 추정기를 참조할 때는 estimators 속성을, 복제된 추정기를 참조할 때는 estimators_ 속성을 참조한다.

예측과 성능은 다음과 같이 하면 된다.

voting_clf.predict(X_test[:1])      # 예측 실시
[clf.predict(X_test[:1]) for clf in voting_clf.estimators_]     # 각 분류기의 예측 결과
voting_clf.score(X_test, y_test)        # 성능

간접 투표(soft voting)는 개별 분류기의 예측 확률을 평균 내어 확률이 가장 높은 클래스를 예측한다. 이는 각 분류기가 클래스의 확률을 예측할 수 있도록 해주어야 한다(즉, predict_proba() 매서드를 지원해야 함). 간접 투표는 직접 투표보다 성능이 높다.

다음 코드는 위에서 만든 직접 투표 분류기를 간접 투표로 바꾸는 것이다. 특히, SVC는 predict_proba()를 지원하지 않기 때문에 probability = True로 해주어야 한다.

voting_clf.voting = "soft"
voting_clf.named_estimators["svc"].probability = True
voting_clf.fit(X_train, y_train)
voting_clf.score(X_test, y_test)


Bagging and Pasting

위에서는 여러 알고리즘을 사용했지만, 오직 하나의 알고리즘을 사용하는 대신 train set의 subset를 랜덤으로 구성해서 분류기를 학습할 수도 있다. 중복을 허용하여 subset을 구성하면 bagging(bootstrap aggregating에서 유래), 중복을 허용하지 않으면 pasting이라고 한다.

훈련이 끝나면 집계 함수가 예측을 모아서 새로운 샘플에 대한 예측을 만든다. 분류일 때는 통계적 최빈값, 회귀에 대해서는 평균을 계산한다. 개별 예측기는 당연히 train set의 일부를 사용하므로 편향(bias)되어 있지만, 집계 함수를 통과하면 편향과 분산이 모두 줄어든다.

다음은 bagging을 실시하는 코드이다

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

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

n_estimators는 사용할 분류기의 숫자이다. n_jobs는 사용할 CPU의 코어 수를 결정한다. -1은 전부 사용하자는 뜻이다. bootstrap을 True로 하는지 False로 하는지에 따라 bagging과 pasting이 결정된다.

OOB(Out Of Bag)는 bagging에서 사용되지 않은 train set을 말한다. 당연히 중복을 허용해서 subset을 만드므로, 선택되지 않은 샘플이 생길 수 밖에 없다. 약 63%만 사용된다고 한다. 남겨진 37% 정도는 dev set으로 활용할 수 있다. oob_score를 True로 지정해야 한다.

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


랜덤 패치와 랜덤 스페이스

샘플이 아니라 특성에 대한 샘플링을 진행할 수도 있다. max_features, bootstrap_features로 지정한다.

명칭 정의 방법
랜덤 패치 방식 훈련 샘플과 특성을 모두 샘플링 bootstrap=False, max_samples=1.0
랜덤 서브스페이스 방식 훈련 샘플은 모두 사용하고 특성을 샘플링 bootstrap=Trueand/or max_features는 1보다 작게


랜덤 포레스트

앞선 코드에서는 BaggingClassifierDecisionTreeClassifier를 넣었지만, RandomForestClassifier를 사용하는 것이 더 최적화된 방법이다.

from sklearn.ensemble import RandomForestClassifier

rnd_clf = RandomForestClassifier(n_estimators=500, max_leaf_nodes=16, n_jobs=-1, random_state=42)
rnd_clf.fit(X_train, y_train)
y_pred_rf = rnd_clf.predict(X_test)

랜덤 포레스트는 트리의 노드를 분할할 때 랜덤으로 선택한 특성 후보 중에서 임곗값을 이용해 최적의 특성을 찾는다. 이는 편향을 손해보는 대신 분산을 낮추는 효과가 있다. 기본적으로 랜덤으로 $\sqrt{n}$ 개의 특성을 선택한다.

엑스트라 트리(익스트림 랜덤 트리)는 후보 특성을 사용해 노드를 랜덤으로 분할하고 그 중에서 최상의 분할을 찾는다. 보통의 결정 트리가 사용하는 최적의 임곗값을 사용하지 않는 것이다. 엑스트라 트리는 일반적인 결정 트리보다 속도가 매우 빠르지만, 무엇의 결과가 더 나을지는 직접 해봐야 한다.

랜덤 포레스트는 특성의 상대적 중요도를 측정하기 쉽다는 장점이 있다.

from sklearn.datasets import load_iris
iris = load_iris()
rnd_clf = RandomForestClassifier(n_estimators=500, random_state=42)
rnd_clf.fit(iris.data, iris.target)
for score, name in zip(rnd_clf.feature_importances_, iris.feature_names):
    print(round(score, 2), name)
     

코드 보러가기



별도의 출처 표시가 있는 이미지를 제외한 모든 이미지는 강의자료에서 발췌하였음을 밝힙니다.

댓글남기기