핸즈온머신러닝&딥러닝

앙상블 학습 & 랜덤 포레스트

threegopark 2021. 6. 16. 16:59
728x90

앙상블 학습

  • 일련의 모델들(=앙상블)로부터 결괏값들을 수집 및 활용하여 최적의 결괏값을 도출하는 방법 (=앙상블 학습)
  • 예를 들어, 일련의 결정 트리들로부터 최적의 결괏값을 찾아내는 방법을 랜덤 포레스트라고 함

 

투표 기반 분류기

  • 정확도가 괜찮은 분류기 여러 개를 훈련시켜서 가장 많이 선택된 클래스를 예측 (= 다수결 투표, 직접 투표 분류기)
  • 약한 학습기 여러 개를 훈련시켜 예측하면 이는 강한 학습기와 같은 성능을 냄 (= 큰 수의 법칙)
  • 단, 각 학습기들끼리는 가능한 한 서로 독립의 관계(오차에 상관관계가 없어야 함)를 만족해야 최적의 성능을 냄

다음은 여러 분류기를 조합하여 사이킷런의 투표 기반 분류기를 만들고 훈련시키는 예제임. (moons dataset)

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

X, y = make_moons(n_samples=10000, noise=0.15)

X_train, X_test, y_train, y_test = train_test_split(X,y, test_size=0.2)

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

voting_clf = VotingClassifier(
estimators = [('lr', log_clf), ('rf', rnd_clf), ('svc', svm_clf)],
voting='hard')
voting_clf.fit(X_train,y_train)

from sklearn.metrics import accuracy_score

for clf in (log_clf, 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))

  • 투표 기반 분류기가 다른 개별 분류기보다 성능이 조금 더 높은 것을 알 수 있음
  • voting = "hard" --> voting = "soft"로 바꾸면 간접 투표 방식으로 예측이 가능
  • 간접 투표 방식은 개별 분류기의 예측을 평균 내어 확률이 가장 높은 클래스를 예측하는 방식 (직접 투표보다 좋은 성능)

 

배깅과 페이스팅

 

다양한 분류기를 만드는 두 가지 방법

  1. 각기 다른 훈련 알고리즘 훈련시켜 voting 하는 방식
  2. 같은 알고리즘을 사용하지만 훈련 세트의 서브셋을 무작위로 구성하여 분류기를 각기 다르게 학습시키는 방식

여기서 2의 방식을 사용하되, 훈련 세트에서 중복을 허용하여 샘플링하는 방식을 배깅, 중복을 허용하지 않고 샘플링하는 방식을 페이스팅이라고 한다. 중복 허용하여 무작위로 샘플을 추출하는 배깅은 서브셋에 대한 다양성을 증가시켜 페이스팅보다 편향이 조금 높지만, 다양성을 추가하여 예측기들의 상관관계를 줄이므로 분산을 감소시킨다. 그렇기 때문에 일반적으로 페이스팅보다 선호된다.

 

모든 예측기가 훈련을 마치면 앙상블은 모든 예측기의 예측을 모아서 새로운 샘플에 대한 예측을 만든다. 이러한 개별 예측기에서 도출된 예측값들은 원본 훈련 세트로 훈련시킨 것보다 많이 편향되어 있지만 수집 함수를 통과하면서 편향은 비슷해지지만 분산이 줄어든 결과값을 도출할 수 있다.

(수집 함수는 분류일 때는 보통 통계적 최빈값(가장 많은 예측 결과)이고 회귀일 때는 보통 평균을 계산한다.)

 

(+편향 / 분산 트레이드 오프 : 편향이 늘어나면 분산이 줄어든다. 여기서 편향은 잘못된 가정으로 인해 발생할 수 있는 오차, 즉, 2차원 데이터를 선형으로 잘못 가정한 경우 발생하는 오차이다. 모델 훈련 포스팅에 자세한 설명을 해놓았다.)

 

 

사이킷런의 배깅과 페이스팅

 

다음은 결정 트리 분류기 500개의 앙상블을 훈련시키는 예제임. 각 분류기는 훈련 세트에서 중복을 허용하여 무작위로 선택된 100개의 샘플로 훈련된다.

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

bag_clf = BaggingClassifier(
DecisionTreeClassifier(), n_estimators = 500, #결정 트리 분류기 500개의 앙상블
max_samples=100, #선택된 100개의 샘플
bootstrap=True, #배깅, 페이스팅은 bootstrap=False
n_jobs=-1 #사이킷런이 훈련과 예측에 사용할 CPU 코어 수 지정, -1이면 가용한 모든 코어 사용)

bag_clf.fit(X_train, y_train)
y_pred = bag_clf.predict(X_test)

accuracy_score(y_test, y_pred)

이번엔 순수한 결정 트리로 정확도를 구해보자.

dt_clf = DecisionTreeClassifier(random_state=42)
dt_clf.fit(X_train, y_train)
y_pred_tree = dt_clf.predict(X_test)
accuracy_score(y_test, y_pred_tree)

결정 트리에서의 하나의 예측보다 괜찮은 결과를 구할 수 있었다. 위에서 설명했듯이 앙상블 기법은 비슷한 편향에서 더 작은 분산을 만들어 덜 불안정한 결정 경계를 만들게 된다.

 

덜 불규칙한 앙상블 기법의 결정 경계

 

oob 평가

 

무작위 중복 추출을 하는 배깅의 경우 평균적으로 63% 정도의 데이터만 훈련에 사용되고, 나머지 37%의 데이터는 훈련에 사용되지 않는다. 이러한 샘플을 oob(out-of-bag) 샘플이라고 한다. 

 

기존의 훈련 방식에서는 검증 세트를 생성하여 훈련을 평가하였지만 배깅을 활용할 경우에는 oob 샘플을 모델의 평가에 활용할 수 있다.

 

bag_clf = BaggingClassifier(
DecisionTreeClassifier(), n_estimators=500,
bootstrap=True, n_jobs=-1,
oob_score=True #훈련 종료 후 자동으로 oob샘플로 평가를 진행)

bag_clf.fit(X_train, y_train)
bag_clf.oob_score_

그렇다면 실제로 이 모델의 정확도를 평가해보자. oob 샘플을 활용하여 구한 평가값과 유사할까?

y_pred = bag_clf.predict(X_test)
accuracy_score(y_test, y_pred)

상당히 비슷한 평가값을 구할 수 있었다.

 

 

이번엔 oob 샘플을 평가가 아닌 훈련에 사용하는 방법에 대해 알아보겠다.

bag_clf.oob_decision_function_

이렇게 각 샘플별로 [0] : 음성 클래스일 확률, [1] : 양성 클래스일 확률을 추정할 수도 있다.

 

 

 

랜덤 패치와 랜덤 서브스페이스

 

BaggingClassifier는 특성 샘플링도 지원한다. 어떠한 샘플을 선택할지에 대한 매개변수 max_samples, bootstrap과 동일하지만, 해당 샘플의 특성도 무작위적으로 샘플링할 수 있다는 의미이다. 매개변수로는 max_features, bootstrap_features가 있으며 이 기법은 이미지같은 매우 고차원의 데이터셋을 다룰 때 유용하다. 

 

이때 샘플과 훈련 특성을 모두 샘플링하는 것을 랜덤 패치 방식이라고 한다.

반면에 훈련 샘플은 모두 사용하고 (페이스팅, bootstrap=False, max_samples=1.0으로 설정) 특성은 샘플링하는 것을 (bootstrap_features=True  or/and max_features< 1.0) 랜덤 서브스페이스 방식이라고 한다.

 

 

 

랜덤 포레스트

 

앞서 언급했듯이 랜덤 포레스트는 일반적으로 결정 트리에 배깅이나 페이스팅을 적용한 앙상블 기법이다. 

위에서 BaggingClassifier에 DecisionTreeClassifier을 넣어 만드는 방식 대신에 결정 트리에 최적화되어 사용하기 편리한 RandomForestClassifier를 사용할 수 있다.

#bag_clf = BaggingClassifier(
#DecisionTreeClassifier(), n_estimators=500, bootstrap=True, n_jobs=-1,oob_score=True)
#대신에

from sklearn.ensemble import RandomForestClassifier

rnd_clf = RandomForestClassifier(n_estimators=500, #500개의 결정 트리
max_leaf_nodes=16, #최대 16개의 말단 노프(리프 노드) 
n_jobs=-1)
rnd_clf.fit(X_train, y_train)

y_pred_rf = rnd_clf.predict(X_test)

이러한 랜덤 포레스트 알고리즘은 트리의 노드를 분할할 때 전체 특성 중에서 최선의 특성을 찾는 대신에 무작위로 선택한 특성 후보 중에서 최적의 특징을 찾는 식으로 진행되어 무작위성과 다양성이 주입된다. 즉, 이러한 모델은 편향을 손해보는 대신에 분산을 낮추어 전체적으로 훌륭한 모델을 만들어낸다.

 

 

랜덤 포레스트 - 엑스트라 트리

 

랜덤 포레스트에서 트리를 만들 떄 각 노드는 무작위로 특성의 서브셋을 만들어 분할에 사용한다. 이때 트리를 더욱 무작위하게 만들기 위해 최적의 임곗값을 찾는 방식대신에 후보 특성을 사용해 무작위로 분할한 다음 그중에서 최상의 분할을 선택한다. 이렇게 극단적으로 무작위한 트리의 랜덤 포레스트를 익스트림 랜덤 트리 (=엑스트라 트리) 라고 부른다. 여기서도 역시 편향이 늘어나지만 분산을 낮추게 된다. 

엑스트라 트리를 만들기 위해서는 ExtraTreeClassifier() 를 사용하며 사용법은 랜덤 포레스트 분류기와 같다.

 

 

랜덤 포레스트 - 특성 중요도

 

결정 트리를 기반으로 하는 모델은 모두 특성 중요도를 제공하기 때문에 대다수의 특성에 대해 평가할 수 있다. 특히 랜덤 포레스트 모델은 무작위성이라는 특성 때문에 더더욱 특성의 상대적 중요도를 측정하기 쉽다.

사이킷런은 어떤 특성을 사용한 노드에 대해서 평균적으로 불순도를 얼마나 감소시키는지 확인하는 방식으로 특성의 중요도를 측정한다.

 

(가중치 평균이며 각 노드의 가중치는 연관된 훈련 샘플 수와 같다. 노드에 사용된 특성별로

(현재 노드의 샘플 비율 X 불순도) - (왼쪽 자식 노드의 샘플 비율 X 불순도) - (오른쪽 자식 노드의 샘플 비율 X 불순도)

와 같이 계산하여 더하고 정규화하여 특성 중요도의 합이 1이 되도록 만든다.)

 

이러한 과정은 자동화되어 feature_importances_ 변수에 저장되어 있다.

from sklearn.datasets import load_iris

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)

 

(+ 여기서 zip 함수는 두 데이터 모음의 순서쌍을 만들어주는 함수이다.

a = ["a", "b"]
b = [1, 2]
for name, value in zip(a, b):
    print(name, value)

 

부스팅

  • 약한 학습기를 여러 개 연결하여 강한 학습기를 만드는 앙상블 방법
  • 앞의 모델을 보완해나가면서 일련의 예측기를 학습시키는 방식
  • 에이다부스트, 그레이디언트 부스팅 등 다양한 부스팅 방법 존재
  • 연속적인 업데이트를 통해 개선되는 모델이므로 병렬처리가 불가능 (확장성이 좋지 않음)

부스팅 - 에이다부스트

  • 이전 모델이 과소적합했던 훈련 샘플의 가중치를 더 높이는 것
  • 새로운 예측기는 학습하기 어려운 샘플에 점점 적합하게 됨

첫 번째 분류기 훈련, 예측결과 도출 --> 알고리즘이 잘못 분류한 훈련 샘플의 가중치를 상대적으로 높임 --> 두 번째 분류기는 업데이트된 가중치를 사용해 훈련 세트에서 훈련하고 다시 예측 --> 가중치 업데이트 ...

 

결과적으로 업데이트되는 가중치로 인해 아래와 같이 결정 경계가 변하게 된다. (아래의 예시는 SVM 분류기로 만든 예시로 에이다부스트 모델 훈련 시 아래와 같이 변화된다는 것만 알아두자)

왼쪽의 첫 번째 분류기에서는 많은 샘플을 잘못 분류해서 이 샘플들의 가중치가 업데이트되었고 두 번째 분류기에서는 업데이트된 가중치로 훈련하여 보다 정확한 결정경계를 갖게 되었다. 

오른쪽의 그래프는 학습률을 절반으로 낮춰 잘못 분류된 샘플의 가중치는 반복마다 절반 정도만 높아진다.

 

모든 예측기가 훈련을 마치면 이 앙상블은 배깅이나 페이스팅과 비슷한 방식으로 예측을 만든다. 하지만 가중치가 적용된 훈련 세트의 전반적인 정확도에 따라 예측기마다 다른 가중치가 적용된다.

 

 

'핸즈온머신러닝&딥러닝' 카테고리의 다른 글

앙상블 & 랜덤 포레스트 2  (0) 2021.06.22
결정 트리  (0) 2021.06.14
서포트 벡터 머신2  (0) 2021.06.11
서포트 벡터 머신 1  (1) 2021.06.07
모델 훈련 4(로지스틱 회귀)  (0) 2021.06.07