7. 앙상블 학습과 랜덤 포레스트
앙상블 학습
- 일련의 예측기(분류나 회귀 모델)로부터 예측을 수집하면 가장 좋은 모델 하나보다 더 좋은 예측을 얻을 수 있음
- 이때 일련의 예측기를 앙상블이라고 하기 때문에
- 이를 앙상블 학습이라고 함
- 앙상블 학습 알고리즘은 앙상블 방법이라고 함
랜덤포레스트
- 훈련 세트로부터 무작위로 각기 다른 서브셋을 만들어 일련의 결정 트리 분류기를 훈련
- 모든 개별 트리의 예측을 구함
- 가장 많은 선택을 받은 클래스를 예측으로 삼음
- 이러한 결정 트리의 앙상블을 랜덤 포레스트 라고 함
7. 1 투표 기반 분류기
Hard Voting
- 로지스틱 회귀 분류기, SVM 분류기, 랜덤 포레스트 분류기, k-최근접 이웃 분류기 등 여러 개의 분류기를 훈련시킴
- 각 분류기의 예측을 모아서 가장 많이 선택된 클래스를 예측하는 방법
- 이렇게 다수결 투표로 정해지는 분류기를 직접 투표(hard voting) 분류기라고 함
- 이 다수결 투표 분류기가 앙상블에 포함된 개별 분류기 중 가장 뛰어난 것보다 정확도가 높을 경우가 많음
- 각 분류기가 약한 학습기(랜덤 추측보다 조금 더 높은 성능을 내는 분류기)일지라도 충분히 많고 다양하다면 앙상블은 강한 학습기가 될 수 있음
Soft Voting
- 모든 분류기가 클래스의 확률을 예측할 수 있으면(즉, predict_proba()메세드가 있으면) 개별 분류기의 예측을 평균 내어 확률이 가장 높은 클래스를 예측할 수 있음 -> 이를 간접 투표(Soft Voting)라고 함
- 확률이 높은 투표에 비중을 더 두기 때문에 직접 투표 방식보다 성능이 높음
- 사이킷런에서 VotingClassifier 클래스 사용시 voting='soft'로 지정
- 모든 분류기가 클래스의 확률을 추정해야 하므로, SVC 같은 클래스 확률을 제공하지 않는 경우 probability를 True로 지정
사이킷런의 투표 기반 분류기
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import VotingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
log_clf = LogisticRegression(solver="lbfgs", random_state=42)
rnd_clf = RandomForestClassifier(n_estimators=100, random_state=42)
svm_clf = SVC(gamma="scale", random_state=42)
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))
=> LogisticRegression 0.864
RandomForestClassifier 0.896
SVC 0.896
VotingClassifier 0.912
7. 2 배깅과 페이스팅
다양한 분류기를 만드는 또 다른 방법으로 같은 알고리즘을 사용하고 훈련 세트의 서브셋을 무작위로 구성하여 분류기를 각기 다르게 학습시키는 방법
배깅(bagging, bootstrap aggregating)
- 훈련 세트에서 중복을 허용하여 샘플링하는 방식
페이스팅(pasting)
- 훈련 세트에서 중복을 허용하지 않고 샘플링하는 방식
- 모든 예측기가 훈련을 마치면 앙상블은 모든 예측기의 예측을 모아서 새로운 샘플에 대한 예측을 만듦
- 수집 함수는 전형적으로 분류일 때는 통계적 최빈값(직접 투표 분류기처럼 가장 많은 예측 결과)
- 회귀에 대해서는 평균 계산
- 개별 예측기는 원본 훈련세트로 훈련시킨 것보다 크게 편향되어 있지만
- 수집 함수를 통과하면 편향과 분산이 모두 감소
- 일반적으로 앙상블의 결과는 원본 데이터셋으로 하나의 예측기를 훈련시킬때와 비교해 편향은 비슷하지만 분산은 줄어듦
- 병렬로 학습, 병렬로 예측을 수행할 수 있기때문에 확장성 덕분에 배깅, 페이스팅의 인기가 높음
7. 2. 1 사이킷런의 배깅과 페이스팅
BaggingClassifier 를 사용해 결정 트리 분류기 500개의 앙상블 훈련
- 각 분류기는 훈련 세트에서 중복을 허용하여 무작위 선택된 100개의 샘플로 훈련
- (페이스팅을 사용하려면 bootstrap=False로 지정)
- n_jobs 매개변수는 사이킷런이 훈련과 예측에 사용할 cpu 코어 수 지정
- BaggingClassifier는 기반이 되는 분류기가 클래스 확률을 추정할 수 있으면 간접 투표 방식을 사용
from sklearn.ensemble import BaggingClassifier
from sklearn.tree import DecisionTreeClassifier
bag_clf = BaggingClassifier(
DecisionTreeClassifier(random_state=42), n_estimators=500,
max_samples=100, bootstrap=True, random_state=42)
bag_clf.fit(X_train, y_train)
y_pred = bag_clf.predict(X_test)
단일 결정 트리의 결정 경계와 500개의 트리를 사용한 배깅 앙상블의 결정 경계 비교
- 앙상블의 예측이 결정 트리 하나의 예측보다 일반화가 잘되었음
- 앙상블은 비슷한 편향에서 더 작은 분산을 만듦
(훈련 세트의 오차 수가 거의 비슷하지만 결정 경계는 덜 불규칙적임)
- 부트스트래핑은 각 예측기가 학습하는 서브셋에 다양성을 증가시키므로
- 배깅이 페이스팅보다 편향이 조금 더 높음
- 하지만 다양성을 추가한다는 것은 예측기들의 상관관계를 줄이므로 앙상블의 분산을 감소시킴
=> 전반적으로 배깅이 더 나은 모델을 만드므로 더 선호됨
- 여유가 된다면 교차검증으로 배깅/페이스팅을 모두 평가해 선택하는 것이 좋음
7. 2. 2 oob 평가
- 배깅을 사용하면 어떤 샘플은 한 예측기를 위해 여러 번 샘플링되고, 어떤 것은 전혀 선택되지 않을 수 있음
- BaggingClassifier는 기본값으로 중복을 허용하여 훈련 세트의 크기만큼인 m개의 샘플을 선택함
- 이는 평균적으로 각 예측기에 훈련 샘플의 63%정도만 샘플링된다는 것을 의미
- 선택되지 않은 훈련 샘플의 나머지 37%를 oob (out-of-bag) 샘플이라고 부름
- 예측기마다 남겨진 37%는 모두 다름
- 예측기가 훈련되는 동안에는 oob샘플을 사용하지 않으므로 별도의 검증 세트를 사용하지 않고 oob샘플을 사용해 평가할 수 있음
- 앙상블의 평가는 각 예측기의 oob 평가를 평균하여 얻음
사이킷런에서 BaggingClassifier를 만들 시 oob 평가를 수행하는 코드
bag_clf = BaggingClassifier(
DecisionTreeClassifier(random_state=42), n_estimators=500,
bootstrap=True, oob_score=True, random_state=40)
bag_clf.fit(X_train, y_train)
bag_clf.oob_score_
=> 0.9013333333333333
oob 평가 결과를 보면 테스트 세트에서 약 90.1%의 정확도를 얻음
from sklearn.metrics import accuracy_score
y_pred = bag_clf.predict(X_test)
accuracy_score(y_test, y_pred)
=>0.912
oob 샘플에 대한 결정 함수 값은 oob_decision_function_ 변수로 확인 -> 각 훈련 샘플의 클래스 확률을 반환
bag_clf.oob_decision_function_
=> array([[0.31746032, 0.68253968], [0.34117647, 0.65882353], [1. , 0. ], ... [0.57291667, 0.42708333]])
- oob 평가는 첫 번째 훈련 샘플이 양성 클래스에 속할 확률을 68.25%로 추정,
- 음성 클래스에 속할 확률은 31.75%로 추정
7. 3 랜덤 패치와 랜덤 서브스페이스
BaggingClassifier는 특성 샘플링도 지원
- 샘플링은 max_features, bootstrap_features 두 매개변수로 조절
- 작동 방식은 max_samples, bootstrap과 동일하지만 샘플이 아니고 특성에 대한 샘플링
- 즉, 각 예측기는 무작위로 선택한 입력 특성의 일부분으로 훈련됨
- 이미지와 같은 매우 고차원 데이터셋을 다룰 때 유용
- 훈련 특성과 샘플을 모두 샘플링 하는 것을 랜덤 패치 방식 이라고 함
- 훈련 샘플을 모두 사용하고, 특성은 샘플링하는 것을 랜덤 서브스페이스 방식 이라고함
- 특성 샘플링은 더 다양한 예측기를 만들며 편향을 늘리는 대신 분산을 낮춤
7. 4 랜덤 포레스트
랜덤 포레스트는 일반적으로 배깅 또는 페이스팅을 적용한 결정 트리의 앙상블
- BaggingClassifier에 DecisionTreeClassifier를 넣어 만드는 대신 RandomForestClassifier을 사용할 수 있음
(결점 트리에 최적화)
- 최대 16개 리프 노드를 갖는 500개 트리로 이뤄진 랜덤 포레스트 분류기를 여러 CPU 코어에서 훈련시키는 코드
from sklearn.ensemble import RandomForestClassifier
rnd_clf = RandomForestClassifier(n_estimators=500, max_leaf_nodes=16, n_jobs=-1)
rnd_clf.fit(X_train, y_train)
y_pred_rf = rnd_clf.predict(X_test)
- 몇 가지 예외가 있지만, RandomForestClassifier는 DecisionTree Classifier의 매개변수와 앙상블 자체를 제어하는 데 필요한 BaggingClassifier의 매개변수를 모두 가지고 있음
랜덤 포레스트는 트리의 노드를 분할할 때 전체 특성 중에서 최선의 특성을 찾는 대신,
무작위로 선택한 특성 후보 중에서 최적의 특성을 찾는 식으로 무작위성을 더 주입함
- 트리를 더 다양하게 만들고
- 편향을 손해보는 대신 분산을 낮추어 더 좋은 모델을 만듦
bag_clf = BaggingClassifier(
DecisionTreeClassifier(max_features="auto", max_leaf_nodes=16),
n_estimators=500, max_samples=1.0, bootstrap=True, n_jobs=-1)
BaggingClassifer를 사용해 앞의 RandomForestClassifier와 유사하게 만든 코드
7. 4. 1 엑스트라 트리
극단적으로 무작위한 트리의 랜덤 포레스트, 익스트림 랜덤 트리 앙상블(=엑스트라 트리)
- 랜덤 포레스트에서 트리를 만들 때, 각 노드는 무작위로 특성의 서브셋을 만들어 분할에 사용
- 트리를 더 무작위하게 만들기 위해 최적의 임계값을 찾는 대신,
- 후보 특성을 사용해 무작위로 분할한 다음 그 중에 최상의 분할을 선택
- 편향이 늘어나지만, 분산을 낮춤
- 최적의 임계값을 노드마다 찾는 것이 시간이 많이 소요되므로, 일반적인 랜덤포레스트보다 훨씬 빠름
- 사이킷런의 ExtraTreesClassifier 클래스 사용
7. 4. 2 특성 중요도
랜덤 포레스트는 특성의 상대적 중요도를 측정하기 쉬움
- 사이킷런은 어떤 특성을 사용한 노드가 평균적으로 불순도를 얼마나 감소시키는지 확인해 특성 중요도 측정
- 가중치 평균이며, 각 노드의 가중치는 연관된 훈련 샘플 수와 같음
Iris 데이터셋에 RandomForestClassifier 훈련 후 각 특성의 중요도 측정
- 사이킷런은 훈련이 끝난 뒤 특성마다 자동으로 점수를 계산하고 중요도 전체 합이 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)
MNIST 데이터셋에 랜덤 포레스트 분류기를 훈련시키고 각 픽셀의 중요도를 나타낸 그래프
- 랜덤 포레스트는 특히 특성 선택 시 어떤 특성이 중요한지 빠르게 확인할 수 있어 매우 편리함
7. 5 부스팅
약한 학습기를 여러 개 연결하여 강한 학습기를 만드는 앙상블 방법
- 앞의 모델을 보완해나가면서 일련의 예측기를 학습시키는 아이디어
- 가장 인기 있는 것은 에이다부스트(adaptive boosting), 그래디언트 부스팅
7. 5. 1 에이다부스트
이전 예측기를 보완하는 새로운 예측기를 만드는 방법
=> 이전 모델이 과소적합했던 훈련 샘플의 가중치를 더 높이는 것
- 새로운 예측기는 학습하기 어려운 샘플에 점점 더 맞춰지게 됨
EX)
1. 에이다부스트 분류기를 만들 때 먼저 알고리즘이 기반이 되는 첫 번째 분류기를 훈련 세트에서 훈련시키고, 예측을 만듦
2. 알고리즘이 잘못 분류된 훈련 샘플의 가중치를 상대적으로 높임
3. 두 번재 분류기는 업데이트된 가중치를 사용해 훈련 세트에서 훈련하고 다시 예측을 만듦
4. 그 다음 다시 가중치 업데이트
5. 반복
moons데이터 셋에 훈련시킨 다섯 개의 연속된 예측기의 결정 경계
(이 모델은 커널의 규제를 강하게 한 RBF 커널 SVM 분류기)
- 첫 번째 분류기가 많은 샘플을 잘못 분류해 이 샘플들의 가중치가 높아짐
- 두 번째 분류기는 이 샘플들을 더 정확히 예측하게 됨
- 이러한 연속된 학습 기법은 경사 하강법과 비슷한 면이 있음
- 경사 하강법은 비용 함수를 최소화하기 위해 한 예측기의 모델 파라미터를 조정
- 반면에 에이다부스트는 점차 더 좋아지도록 앙상블에 예측기를 추가
모든 예측기가 훈련을 마치면 앙상블은 배깅이나 페이스팅 비슷한 방식으로 예측을 만듦
- 가중치가 적용된 훈련 세트의 전반적인 정확도에 따라 예측기마다 다른 가중치가 적용
※ 연속된 학습 기법의 단점
각 예측기는 이전 예측기가 훈련되고 평가된 후에 학습될 수 있기 때문에 병렬화를 할 수 없음
(배깅이나 페이스팅만큼 확장성이 높지 않음)
7. 5. 2 그레이디언트 부스팅
에이다부스트처럼 그래디언트 부스팅은 앙상블에 이전까지의 오차를 보정하도록 예측기를 순차적으로 추가하지만,
에이다부스트처럼 반복마다 샘플의 가중치를 수정하는 대신 이전 예측기가 만든 잔여 오차에 새로운 예측기를 학습
결정 트리를 기반 예측기로 사용하는 간단한 회귀 문제
=> 그래디언트 부스팅 또는 그래디언트 부스티드 회귀 트리
from sklearn.tree import DecisionTreeRegressor
tree_reg1 = DecisionTreeRegressor(max_depth=2, random_state=42)
tree_reg1.fit(X, y)
y2 = y - tree_reg1.predict(X)
tree_reg2 = DecisionTreeRegressor(max_depth=2, random_state=42)
tree_reg2.fit(X, y2)
y3 = y2 - tree_reg2.predict(X)
tree_reg3 = DecisionTreeRegressor(max_depth=2, random_state=42)
tree_reg3.fit(X, y3)
y_pred = sum(tree.predict(X_new) for tree in (tree_reg1, tree_reg2, tree_reg3))
- DecisionTreeRegressor를 훈련 세트에 학습
- 첫 번째 예측기에서 생긴 잔여 오차에 두 번째 DecisionTreeRegressor를 훈련
- 그 다음 두 번째 예측기가 만든 잔여 오차에 세 번째 회귀 모델을 훈련
=> 세 개의 트리를 포함하는 앙상블 모델
- 새로운 샘플에 대한 예측 시 모든 트리의 예측을 더하면 됨
사이킷런의 GradientBoostingRegressor 사용해, GBRT 앙상블 훈련 가능
from sklearn.ensemble import GradientBoostingRegressor
gbrt = GradientBoostingRegressor(max_depth=2, n_estimators=3, learning_rate=1.0, random_state=42)
gbrt.fit(X, y)
- 왼쪽 열은 이 세 트리의 예측, 오른쪽 열은 앙상블의 예측
- 첫 번째 행에서는 앙상블에 트리가 하나만 있으므로 첫 번째 트리의 예측과 같음
- 두 번째 행에서는 새로운 트리가 첫 번째 트리의 잔여 오차에 대해 학습
- 오른쪽의 앙상블 예측이 두 개의 트리 예측의 합과 같은 것을 볼 수 있음
- 세 번째 행에서는 또 다른 트리가 두 번째 트리의 잔여 오차에 훈련
- 트리가 앙상블에 추가될수록 앙상블의 예측이 점차 좋아짐
learning_rate 매개변수가 각 트리의 기여 정도를 조절
- 0.1처럼 낮게 설정하면 앙상블의 훈련 세트에 학습시키기 위해 많은 트리가 필요하지만 예측 성능은 좋아짐
=> 축소 라고 부르는 규제 방법
- 왼쪽 훈련 세트를 학습하기에는 트리가 충분하지 않은 반면
- 오른쪽은 트리가 너무 많아 훈련 세트에 과대적합되었음
조기 종료 기법
최적의 트리 수를 찾기 위해 조기 종료 기법(4장 참조) 사용 가능
- staged_predict() 메서드를 사용해 구현 가능
- 훈련의 각 단계에서 앙상블에 의해 만들어진 예측기를 순회하는 반복자 반환
120개 트리로 GBRT 앙상블을 훈련시키고 최적의 트리 수를 찾기 위해 각 훈련 단계에서 검증 오차를 측정,
마지막에 최적의 트리 수를 사용해 새로운 GBRT 앙상블을 훈련
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
X_train, X_val, y_train, y_val = train_test_split(X, y, random_state=49)
gbrt = GradientBoostingRegressor(max_depth=2, n_estimators=120, random_state=42)
gbrt.fit(X_train, y_train)
errors = [mean_squared_error(y_val, y_pred)
for y_pred in gbrt.staged_predict(X_val)]
bst_n_estimators = np.argmin(errors) + 1
gbrt_best = GradientBoostingRegressor(max_depth=2, n_estimators=bst_n_estimators, random_state=42)
gbrt_best.fit(X_train, y_train)
실제로 훈련을 중지하는 방법으로 조기 종료 구현
- warm_start=True로 설정하면 사이킷런이 fit() 메서드가 호출될 때 기존 트리를 유지하고 훈련을 추가할 수 있도록 함
다섯 번의 반복 동안 검증 오차가 향상되지 않으면 훈련을 멈추는 조기 종료 방법
gbrt = GradientBoostingRegressor(max_depth=2, warm_start=True, random_state=42)
min_val_error = float("inf")
error_going_up = 0
for n_estimators in range(1, 120):
gbrt.n_estimators = n_estimators
gbrt.fit(X_train, y_train)
y_pred = gbrt.predict(X_val)
val_error = mean_squared_error(y_val, y_pred)
if val_error < min_val_error:
min_val_error = val_error
error_going_up = 0
else:
error_going_up += 1
if error_going_up == 5:
break # early stopping
GradientBoostingRegressor는 각 트리가 훈련할 때 사용할 훈련 샘플의 비율을 지정할 수 있는 subsample 매개변수 지원
- 예를 들어, subsample=0.25라고 하면, 각 트리는 무작위로 선택된 25%의 훈련 샘플로 학습됨
- 편향이 높아지는 대신 분산은 낮아짐
- 훈련 속도를 높임
=> 이 기법을 확률적 그래디언트 부스팅 라고 함
최적화된 그래디언트 부스팅 구현으로 XGBoost 파이썬 라이브러리가 유명
import xgboost
xgb_reg = xgboost.XGBRegressor()
xgb_reg.fit(X_train, y_train,
eval_set=[(X_val, y_val)], early_stopping_rounds=2)
y_pred = xgb_reg.predict(X_val)
7. 6 스태킹
앙상블에 속한 모든 예측기의 예측을 취합하는 간단한 함수(직접 투표 등)를 사용하는 대신, 취합하는 모델을 훈련 시키고자 하는 방법
새로운 샘플에 회귀 작업을 수행하는 앙상블
- 아래의 세 예측기는 각각 다른값을 예측하고
- 마지막 예측기(블렌더 또는 메타 학습기)가 이 예측을 입력으로 받아 최종 예측을 만듦
블렌더를 학습시키는 일반적인 방법은 홀드 아웃 세트 사용
- 먼저 훈련 세트를 두 개의 서브셋으로 나눔
- 첫 번째 서브셋은 첫 번째 레이어의 예측을 훈련시키기 위해 사용
- 첫 번째 레이어의 예측기를 사용해 두 번째(홀드 아웃) 세트에 대한 예측을 만듦
- 예측기들이 훈련하는 동안 이 샘플들을 보지 못했기 때문에 이때 만들어진 예측은 새로운 것
- 홀드 아웃 세트의 각 샘플에 대해 세 개의 예측값이 있음
- 타깃값은 그대로 쓰고 앞에서 예측한 값을 입력 특성으로 사용하는 새로운 훈련 세트를 만들 수 있음
- 블렌더가 새 훈련 세트로 훈련됨
- 즉, 첫 번째 레이어의 예측을 가지고 타깃값을 예측하도록 학습
블렌더를 여러 개 훈련시킬 수 있음
(ex. 하나는 선형 회귀, 다른 하나는 랜덤 포레스트 회귀로)
- 블렌더만의 레이어가 만들어짐
1. 훈련 세트를 세 개의 서브셋으로 나눔
2. 첫 번째 세트는 첫 번째 레이어를 훈련시키는 데 사용
3. 두 번째 세트는 첫 번째 레이어의 예측기로 두 번째 레이어를 훈련시키기 위한 훈련 세트를 만드는 데 사용
4. 세 번째 세트는 두 번째 레이어의 예측기로 세 번째 레이어를 훈련시키기 위한 훈련 세트를 만드는 데 사용
5. 작업이 끝나면 각 레이어를 차례대로 실행해 새로운 샘플에 대한 예측을 만들 수 있음
'AI > Hands-on ML' 카테고리의 다른 글
[핸즈온 머신러닝] 8장 - 차원 축소 (0) | 2021.01.24 |
---|---|
[핸즈온 머신러닝] 핸즈온 머신러닝2 정오표 (0) | 2021.01.22 |
[핸즈온 머신러닝] 6장 - 결정 트리 (0) | 2021.01.03 |
[핸즈온 머신러닝] 5장 - 서포트 벡터 머신 (0) | 2021.01.03 |
[핸즈온 머신러닝] 4장 - 모델 훈련 (0) | 2020.12.08 |