티스토리 뷰

728x90

1. 아이디어

무작위로 선택된 수천 명의 사람들에게 독립적으로 어떤 문제에 대한 질문을 하고 대답을 모은다고 가정하자. 많은 경우 이들의 대답이 전문가 한 명의 답보다 낫다고 한다. 즉 집단지성이 전문가 한 사람보다 낫다는 것이다. 앙상블 방법을 통해 이 집단지성을 학습 알고리즘들에 적용시켜 단일 학습 알고리즘보다 더 좋은 성능을 꾀하고자 한다.

1.1 앙상블

그렇다면 앙상블이란 무엇인가? 사전적 정의로는 a group of musicians, actors, or dancers who perform together.

머신러닝에서는 일련의 예측기를 앙상블이라 부른다.

2. 앙상블 방법

앙상블 방법은 크게 2가지로 나뉜다. 먼저, 하나의 데이터 셋에 여러가지 다른 훈련 알고리즘을 훈련시킨 후 조합하는 방식으로 투표 기반 방식이 대표적이다. 투표 기반 방식은 다시 직접 투표 방식과 간접 투표 방식으로 나뉜다. 직접 투표 방식은 각 훈련 알고리즘의 예측을 취합해 가장 많이 선택된 클래스를 최종 예측값으로 출력하는 방식이다. 사이킷런에서 매개변수로 아래와 같이 voting="hard"를 지정하면 된다.

간접 투표 방식은 개별 훈련 알고리즘의 모든 클래스에 대한 예측 확률을 취합해 평균을 낸 후 그 값이 가장 큰 클래스를 최종 예측값으로 출력하는 방식이다. 이 방식에는 조건이 있다. 각 훈련 알고리즘이 클래스의 확률을 추정할 수 있어야 한다.(즉 predict_proba() 메서드가 존재해야 한다.)

사이킷런에서 매개변수로 아래와 같이 voting="soft"를 지정하면 된다.

* SVC는 기본값으로 클래스 확률을 제공하지 않으므로 probability=True로 지정해야 한다.

 

 

voting='hard'의 결과

voting='soft'의 결과

보이다시피 앙상블 방법이 단일 모델만 사용하는 것보다 낫고, 직접 투표 방식보다 간접 투표 방식이 더 낫다. 일반적으로도 그렇다.

 

앙상블의 두 번째 방법은 하나의 데이터 셋을 여러 서브셋으로 분할하고 각기 다른 서브셋으로 같은 훈련 알고리즘을 사용하는 것이다. 이 방식은 다시 훈련 셋에서 중복을 허용해 샘플링하는 방식인 배깅과 중복을 허용하지 않고 샘플링하는 페이스팅으로 나뉜다. 먼저 언급한 투표 기반 방법과 이 방법은 함께 사용된다. 예를 들어 직접 투표 방식과 배깅, 간접 투표 방식과 배깅을 함께 사용할 수 있다.

 

배깅 방법을 사용하려면 BaggingClassifier와 함께 bootstrap=True로 설정하면 된다.

페이스팅 방법을 사용하려면 BaggingClassifier와 함께 bootstrap=True로 설정하면 된다.

보이다시피 배깅이 페이스팅보다 더 좋은 결과를 냈다. 배깅은 중복을 허용하는 샘플링으로 각 예측기가 학습하는 데이터 셋에 다양성을 증가시키므로 배깅이 페이스팅보다 편향이 조금 더 높다. 다양성을 증가시키는 것은 각 예측기 간의 상관관계를 줄이기 때문에 앙상블의 분산을 감소시킨다. 일반적으로 배깅이 페이스팅보다 더 좋은 결과를 낸다.

 

2.1 oob 평가

배깅을 사용하면 평균적으로 각 훈련 알고리즘(예를 들어 위의 코드에서 하나의 트리)에 훈련 샘플의 약 63% 정도만 샘플링된다. 나머지 37%를 oob(out-of-bag)샘플이라고 부르며 이 샘플를 테스트 샘플로 사용할 수 있다. 전체 앙상블의 성능은 각 예측기(예를 들면 트리)의 oob 평가를 평균해 측정할 수 있다. 사이킷런에서는 아래와 같이 oob_score=True를 지정해 성능을 측정한다.

2.2 특징 샘플링

BaggingClassifier는 특징 샘플링도 지원한다. 샘플링할 특징의 최대 개수인 max_features와 중복을 허용해 특징을 샘플링할 것인지 여부를 지정하는 bootstrap_features 매개변수가 있다. 훈련 샘플과 특성 모두를 샘플링하는 것을 랜덤 패치 방식이라 하고 훈련 샘플은 모두 사용하고 특성만 샘플링하는 것을 랜덤 서브스페이스 방식이라 한다.

랜덤 패치 방식

랜덤 서브스페이스 방식

결과가 좋지 않다. 그렇다면 특성을 굳이 샘플링하는 이유가 무엇인가? 다양한 이유가 있지만 대표적으로 고차원의 데이터 셋을 다룰 때 유용하다. 이미지같은 데이터는 특성이 매우 많다. 따라서 모든 특성을 다 모델 훈련에 사용하지 않고 샘플링하면 학습 속도를 크게 개선할 수 있다. 또한 샘플링을 통해 더 다양하게 훈련된 모델을 만들 수 있기 때문에 편향은 높이는 대신 분산을 낮추는 효과를 얻을 수 있다.

(* 위 코드의 데이터는 2차원 데이터)

 

2.3 특성 중요도

랜덤 포레스트의 또 다른 장점은 특성의 상대적 중요도를 측정하기 쉽다는 것이다. 어떤 특성을 사용한 노드가 랜덤 포레스트에 있는 모든 트리에 걸쳐서 평균적으로 불순도를 얼마나 감소시키는지 확인해 특성의 중요도를 측정한다. feature_importances_ 변수를 통해 정규화된 값을 확인할 수 있다. 아래는 삼성전자 주식 데이터를 이용해 특성의 중요도를 측정하는 코드와 그 결과이다.(특성 조합도 별도로 추가했다.)

3. 부스팅

부스팅은 약한 학습기 여러 개를 연결해 강한 학습기를 만드는 앙상블 방법이다. 그 중 가장 인기있는 것은 에이다부스트와 그레디언트 부스팅이다.

3.1 에이다부스트

먼저 에이다부스트부터 살펴보자. 에이다부스트는 이전 훈련 알고리즘이 훈련 후 잘못 분류한 클래스의 샘플의 가중치를 더 증가시킨 후 다음 훈련 알고리즘으로 훈련하는 방식을 반복한다. 가중치는 하이퍼파라미터 learning_rate로 조절한다. 모든 예측기가 훈련을 마치면 그 후 배깅이나 페이스팅같은 방식으로 예측을 진행한다.

3.2 그레디언트 부스팅

그레디언트 부스팅도 에이다부스트처럼 이전 학습 알고리즘의 오차를 보정하도록 학습 알고리즘을 순차적으로 추가하는 방식이다. 하지만 샘플의 가중치를 수정하는 대신 다음 학습 알고리즘를 이전 학습 알고리즘의 잔여 오차에 학습시킨다. 결정 트리를 기반 알고리즘으로 사용하는 것을 그레디언트 트리 부스팅 또는 회귀 문제에 사용한다면 그레디언트 부스티드 회귀 트리라 한다.

 

코드

사이킷런의 GradientBoostingRegressor 메서드를 이용하면 위와 같이 수작업을 하지 않아도 된다.

learning_rate를 낮게 설정하면 학습에 더 많은 트리가 필요하지만 일반적으로 성능은 더 좋아진다.

최적의 트리 수를 찾기 위해서는 조기 종료 기법을 사용하면 된다. 사이킷런에서는 간단히 staged_predict() 메서드를 사용하면 된다.

 

3.3 확률적 그레디언트 부스팅

그레디언트 부스팅에서 훈련에 사용할 샘플의 비율을 subsample 매개변수(0~1의 값 x)를 통해 지정하면 각 트리에 사용되는 훈련 샘플이 전체 샘플의 x 퍼센트만 사용된다. 따라서 편향은 높아지고 분산은 낮아진다. 또한 훈련 속도 더 빨라진다.

 

3.4 XGBoost

최적화된 그레디언트 부스팅 구현으로 유명한 XGBoost가 있다. 이 알고리즘은 매우 빠른 속도, 확장성, 이식성을 특징으로 한다. 

자동 종료 기능

4.스태킹

앙상블 속 모든 훈련 알고리즘의 예측을 단순히 취합해 투표 기반 방식을 통해 예측을 하는 것 대신 취합하는 모델을 훈련시키는 방식이다. 이 취합 모델은 블렌더 혹은 메타 학습기라 불린다.

블렌더의 일반적인 학습 방식은 홀드 아웃 세트를 사용하는 것이다.

 

무슨 말인가 하면, 먼저 개별 모델들과 블렌더를 정의한다.

random_forest_clf = RandomForestClassifier(n_estimators=100, random_state=42)
extra_trees_clf = ExtraTreesClassifier(n_estimators=100, random_state=42)
svm_clf = LinearSVC(random_state=42)

blender = RandomForestClassifier(n_estimators=200, oob_score=True, random_state=42)

 

훈련 데이터 셋을 3개(train, val, test)로 나눈다.

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

그 후 3개의 모델(5개여도 7개여도 상관없음)을 train 셋으로 각각 훈련시킨 후

random_forest_clf.fit(X_train, y_train)
extra_trees_clf.fit(X_train, y_train)
svm_clf.fit(X_train, y_train)

val 셋으로 각각 예측을 진행하고 각 예측값을 변수에 저장한다.

pred1 = random_forest_clf.predict(X_val, y_val)
pred2 = extra_trees_clf.predict(X_val, y_val)
pred3 = svm_clf.predict(X_val, y_val)

이를 concat하여 합친 후에

X_new = np.array([pred1, pred2, pred3])

블렌더의 입력 특성으로하여 예측을 진행하면 된다.

blender.fit(X_new, y_test)
blender.predict(NEW_DATA)

 

댓글