엑스트라 트리(Extra Tree)란?
이전 글에 랜덤 포레스트가 특성을 기반으로 결정 트리를 무작위로 선택하는 것이였다면, 엑스트라 트리는 특성 또한 무작위로 선택하게 된다. 즉 무작위성을 더 추가한다는 뜻
어렵게 말하면 후보 특성을 무작위로 분할하여 최적의 분할을 찾는 것이다.
이렇게 진행하면 랜덤 포레스트 모델보다 특성에 대한 중요도(의존도)가 커져 성능은 낮아질 지 언정, 과대적합을 방지하고 검증 세트 점수를 향상시킬 수 있다. 무작위성이 더 크기 때문에 속도도 빠르다.
이 정도만 알고 가도 코드 이해하는 데는 문제가 없을 것 같으니 바로 구현으로~~
기본 설정
- 필수 모듈 불러오기
- 그래프 출력 관련 기본 설정 지정
In [ ]:
pip install mglearn
In [ ]:
# 파이썬 ≥3.5 필수 (파이썬 3.7 추천)
import sys
assert sys.version_info >= (3, 5)
# 사이킷런 ≥0.20 필수
import sklearn
assert sklearn.__version__ >= "0.20"
# 공통 모듈 임포트
import numpy as np
import os
# mglearn 임포트
# pip install mglearn 따로 실행해서 install 해야합니다.
import mglearn
# 노트북 실행 결과를 동일하게 유지하기 위해
np.random.seed(42)
# 깔끔한 그래프 출력을 위해
%matplotlib inline
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.rc('axes', labelsize=14)
mpl.rc('xtick', labelsize=12)
mpl.rc('ytick', labelsize=12)
# 그림을 저장할 위치
PROJECT_ROOT_DIR = "."
CHAPTER_ID = "decision_trees"
IMAGES_PATH = os.path.join(PROJECT_ROOT_DIR, "images", CHAPTER_ID)
os.makedirs(IMAGES_PATH, exist_ok=True)
def save_fig(fig_id, tight_layout=True, fig_extension="png", resolution=300):
path = os.path.join(IMAGES_PATH, fig_id + "." + fig_extension)
print("Saving figure", fig_id)
if tight_layout:
plt.tight_layout()
plt.savefig(path, format=fig_extension, dpi=resolution)
직접 구현 엑스트라 트리(accuracy_score: 0.867)
make_moons 데이터셋을 생성
- make_moons(n_samples=10000, noise=0.4) 이용
- 결과를 일정하게 만들기 위해 random_state=42 추가
In [ ]:
from sklearn.datasets import make_moons
X, y = make_moons(n_samples=10000, noise=0.4, random_state=42)
make_moons 데이터셋을 train 세트와 test 세트 분할
- train_test_split() 사용
In [ ]:
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
ExtraTree 파라미터 찾기: 랜덤 탐색 활용
- RandomizedSearchCV 활용
In [ ]:
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import randint
from sklearn.tree import DecisionTreeClassifier
params = {
'max_leaf_nodes': list(range(2, 100)),
'min_samples_split': [2, 3, 4]
}
rnd_search = RandomizedSearchCV(DecisionTreeClassifier(random_state=42, splitter="random"),
params, verbose=1, cv=3)
rnd_search.fit(X_train, y_train)
Out[ ]:
RandomizedSearchCV(cv=3, error_score=nan,
estimator=DecisionTreeClassifier(ccp_alpha=0.0,
class_weight=None,
criterion='gini',
max_depth=None,
max_features=None,
max_leaf_nodes=None,
min_impurity_decrease=0.0,
min_impurity_split=None,
min_samples_leaf=1,
min_samples_split=2,
min_weight_fraction_leaf=0.0,
presort='deprecated',
random_state=42,
splitter='random'),
iid='deprecated', n_iter=10, n_jobs=None,
param_distributions={'max_leaf_nodes': [2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13,
14, 15, 16, 17, 18,
19, 20, 21, 22, 23,
24, 25, 26, 27, 28,
29, 30, 31, ...],
'min_samples_split': [2, 3, 4]},
pre_dispatch='2*n_jobs', random_state=None, refit=True,
return_train_score=False, scoring=None, verbose=1)
In [ ]:
cvres = rnd_search.cv_results_
for mean_score, params in zip(cvres["mean_test_score"], cvres["params"]):
print(np.sqrt(-mean_score), params)
nan {'min_samples_split': 2, 'max_leaf_nodes': 13}
nan {'min_samples_split': 2, 'max_leaf_nodes': 5}
nan {'min_samples_split': 4, 'max_leaf_nodes': 50}
nan {'min_samples_split': 2, 'max_leaf_nodes': 96}
nan {'min_samples_split': 2, 'max_leaf_nodes': 89}
nan {'min_samples_split': 4, 'max_leaf_nodes': 43}
nan {'min_samples_split': 2, 'max_leaf_nodes': 38}
nan {'min_samples_split': 4, 'max_leaf_nodes': 70}
nan {'min_samples_split': 3, 'max_leaf_nodes': 38}
nan {'min_samples_split': 2, 'max_leaf_nodes': 16}
랜덤 탐색 시의 최적의 하이퍼 파라미터 값
In [ ]:
rnd_search.best_estimator_
Out[ ]:
DecisionTreeClassifier(ccp_alpha=0.0, class_weight=None, criterion='gini',
max_depth=None, max_features=None, max_leaf_nodes=38,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=1, min_samples_split=2,
min_weight_fraction_leaf=0.0, presort='deprecated',
random_state=42, splitter='random')
엑스트라 트리 만들기
- 각각 무작위로 선택된 100개의 n_instances와 1,000개의 n_trees 생성
- 사이킷런의 ShuffleSplit 클래스 활용
In [ ]:
from sklearn.model_selection import ShuffleSplit
n_trees = 1000
n_instances = 100
mini_sets = []
rs = ShuffleSplit(n_splits=n_trees, test_size=len(X_train) - n_instances, random_state=42)
for mini_train_index, mini_test_index in rs.split(X_train):
X_mini_train = X_train[mini_train_index]
y_mini_train = y_train[mini_train_index]
mini_sets.append((X_mini_train, y_mini_train))
앞에서 찾은 최적의 하이퍼 파라미터 값을 사용
- clone(grid_search_cv.best_estimator_) 이용
각각의 서브 데이터셋에 결정 트리 훈련, 테스트 세트에서 1,000개의 결정트리 평가
더 작은 데이터셋에서 훈련되었기 때문에 성능이 이전보다 낮다.
In [ ]:
from sklearn.metrics import accuracy_score
from sklearn.base import clone
# clone: 동일 파라미터를 가지고 새로운 추정치 생성
forest = [clone(rnd_search.best_estimator_) for _ in range(n_trees)]
accuracy_scores = []
for tree, (X_mini_train, y_mini_train) in zip(forest, mini_sets):
tree.fit(X_mini_train, y_mini_train)
y_pred = rnd_search.predict(X_test)
accuracy_scores.append(accuracy_score(y_test, y_pred))
np.mean(accuracy_scores)
Out[ ]:
0.8584999999999998
각 테스트 세트 샘플에 1,000개의 결정 트리 예측 생성
- SciPy의 mode()를 사용하여 다수결 예측(majority-vote predictions)이 만들어진다.
In [ ]:
Y_pred = np.empty([n_trees, len(X_test)], dtype=np.uint8)
for tree_index, tree in enumerate(forest):
Y_pred[tree_index] = tree.predict(X_test)
In [ ]:
from scipy.stats import mode
y_pred_majority_votes, n_votes = mode(Y_pred, axis=0)
테스트 세트에서 예측을 평가하면 첫 번째 모형보다 조금 더 높은 정확도를 얻는다.
In [ ]:
accuracy_score(y_test, y_pred_majority_votes.reshape([-1]))
Out[ ]:
0.8735
요약
엑스트라 트리 모델 직접 구현 후 사이킷런의 모델과 성능 비교
- make_moons 데이터셋을 이용하였다.
- Extra Trees 모델은 랜덤 포레스트에 비해서 무작위로 특성을 선정하여 진행하기 때문에 연산 속도가 빠르고 최적의 파라미터 값을 적용하여 진행하는 모델이다. 그래서 랜덤포레스트와 상당히 닮은 것을 확인할 수 있다.
- 구현 결과 0.8735 만큼의 정확도를 얻을 수 있다.
- 참고 문헌: 핸즈온 머신러닝
- Github link: 머신러닝 실습 7