처음부터 차근차근 파이썬 자세히보기

Python-머신 러닝/Python-지도 학습 알고리즘

2. 지도 학습 알고리즘 (5) Ensembles of Decision Trees (Random Forest)

윤빵빵영 2020. 6. 14. 16:02

최종 수정 일자: 2020-06-14 16:02

해당 카테고리에 작성되는 글은 Introduction to Machine Learning with Python(파이썬 라이브러리를 활용한 머신 러닝)을 기반으로 작성되었습니다.

 

앙상블(ensemble)은 여러 개의 머신 러닝 모델을 결합하여 더 강력한 모델을 만들어내는 방법입니다. 여러 개의 머신 러닝 모델을 결합하는 것이기 때문에 무수히 많은 앙상블 모델이 있습니다만, 분류나 회귀에 대해 넓은 범위의 데이터 셋에 대해서는 두 개의 효과적인 앙상블 모델을 사용합니다. 첫 번째는 Random Forest (RF) 알고리즘이며 두 번째는 Gradient Boosted Regression Tree (GBRT) 알고리즘입니다.

 

Random Forest

결정 트리 알고리즘은 학습 데이터 셋을 과대 적합하는 경향이 있습니다. 이의 단점을 보완하기 위한 알고리즘이 random forest입니다. Random forest는 결정 트리의 집합체이며 각 결정 트리는 서로 조금씩 다른 구조를 가지고 있습니다. 각 결정 트리는 예측에 있어 상대적으로 좋은 결과를 내지만 데이터의 일부분에 대해 과대 적합할 가능성이 있습니다. 잘 작동하기는 하지만 결과적으로 과대 적합하게 되는 여러 결정 트리를 만들어서 이들의 결과를 평균내어버리면 과대 적합을 줄일 수 있을 것이라는 전략에서 출발합니다. 이때 각 트리는 목표값 예측에 어느 정도 타당한 모델이면서 서로 다른 트리들로 구성되어야 합니다.

Random forest는 각 결정 트리가 다르다는 것을 보장하기 위해 트리 구축에 어떤 임의성(randomness)을 부여합니다. 우선 각 트리를 만드는데 있어 데이터 포인트를 임의로 선택합니다. 또한 각 분리 test를 위한 특성을 임의로 선택함으로써 임의성을 부여합니다.

 

Random Forest 만들기

 

우선 random forest 모델을 만들기 위한 하위 결정 트리의 수를 정해야 합니다. 이는 RandomForestRegressor 또는 RandomForestClassifier 클래스의 n_estimators 매개 변수로 결정됩니다. 이때 10 개의 트리를 하위 트리로 만든다고 생각해봅시다. 이들 트리는 완벽히 서로 독립적으로 만들어집니다. 트리를 만들기 위해서 우리가 갖고 있는 데이터 셋에서 복원 추출을 진행하여 새로운 데이터 셋을 구성합니다. 이때 n_samples라는 매개 변수로 트리를 구성하는데 추출할 데이터의 수를 지정합니다. 이때 데이터 포인트의 수가 n_samples개여야 하므로 n_samples번만큼 복원 추출을 진행하여 새로운 데이터 셋을 만들고 이를 기반으로 트리를 구성하게 됩니다.

복원 추출을 통해 만들어진 새로운 데이터 셋을 기반으로 결정 트리가 만들어 질 때 앞의 결정 트리의 알고리즘과는 조금 다른 방식으로 만들어집니다. 각 노드에서 가장 최선의 test를 찾는 것이 아닌, 특성들 중 어떤 부분 집합을 임의로 선택하여 이 특성 부분 집합 내에서 최선의 test를 찾습니다. 이때 부분 집합 내 선택되는 특성의 수는 max_features 매개 변수로 조절할 수 있습니다.

복원 추출을 이용한 새로운 데이터 셋을 결정 트리를 만들 때마다 이루어지며 따라서 각 결정 트리는 서로 다른 데이터 셋을 기반으로 만들어집니다. 또한 각 결정 트리 내의 노드는 임의로 선택된 특성들로만 만들어지므로 이러한 두 가지 전략을 통해 random forest는 하위 결정 트리의 구성을 임의로 만들 수 있게 됩니다. 이런 식으로 하위 트리가 서로 다른 트리임을 확실하게 만듭니다.

이때 완전한 임의성을 보장하려면 매개변수 조절에 주의를 해야 합니다. max_features가 특성의 수가 되어버리면 각 분리 노드가 데이터 셋의 모든 특성들을 고려하게 되므로 임의성을 잃습니다. 또한 max_features=1이 되어버리면 test할 특성에 대한 선택권이 없어지는 것과 같으므로, 해당 특성의 서로 다른 기준에 대해서만 탐색하게 되어 마찬가지로 임의성을 잃습니다.

Random forest를 이용한 예측은 forest 내 모든 트리를 데이터 포인트가 따라 내려가면서 결과들을 수집합니다. 그 뒤, 회귀 문제라면 결과들의 평균치를, 분류 문제라면 soft voting 전략을 통해 라벨링을 진행합니다. soft voting은 각 결과들에 대한 확률을 제공하는데, 모든 투표 결과를 평균내어 가장 높은 확률 값을 보이는 class를 알려줍니다.

 

Random Forest 분석

 

from sklearn.datasets import make_moons
X, y = make_moons(n_samples=100, noise=0.25, random_state=3)

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=42)

from sklearn.ensemble import RandomForestClassifier
forest = RandomForestClassifier(n_estimators=5, random_state=2)
forest.fit(X_train, y_train)

fig, axes = plt.subplots(2, 3, figsize=(20, 10))
for i, (ax, tree) in enumerate(zip(axes.ravel(), forest.estimators_)):
    ax.set_title("Tree {}".format(i))
    mglearn.plots.plot_tree_partition(X_train, y_train, tree, ax=ax)
mglearn.plots.plot_2d_separator(forest, X_train, fill=True, ax=axes[-1, -1], alpha=.4)
axes[-1, -1].set_title("Random Forest")
mglearn.discrete_scatter(X_train[:, 0], X_train[:, 1], y_train)
plt.show()

결과)

다섯 개의 트리(n_estimators=5)로 구성된 random forest 모델링을 진행한 것입니다. 해당 모델링은 하위 개별 트리들보다 과적화를 덜 하며 따라서 결정 경계에 대해 더 많이 직관적인 정보를 제공합니다. 실제 적용하는 과정에서는 100~1000개의 더 많은 트리를 이용하며 이 경우 더 부드러운 결정 경계를 갖게 됩니다.

from sklearn.datasets import load_breast_cancer
cancer = load_breast_cancer()

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target, random_state=0)

from sklearn.ensemble import RandomForestClassifier
forest = RandomForestClassifier(n_estimators=100, random_state=0)
forest.fit(X_train, y_train)

print("Accuracy on training set: {:.3f}".format(forest.score(X_train, y_train)))
print("Acciracy on test set: {:.3f}".format(forest.score(X_test, y_test)))

결과)

Accuracy on training set: 1.000
Acciracy on test set: 0.972

 

Random forest97%의 정확도를 보여주며 매개변수 조절 없이도 linear model이나 단일 결정 트리보다 더 좋은 성능을 보여줍니다. max_features 설정을 조절하거나 단일 결정 트리에서 사용했던 pre-pruning 전략을 이용할 수도 있지만 random forest의 기본 매개 변수를 사용해도 잘 작동합니다.

결정 트리와 마찬가지로 random forest는 특성 중요도를 제공합니다.

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target, random_state=0)

from sklearn.ensemble import RandomForestClassifier
forest = RandomForestClassifier(n_estimators=100, random_state=0)
forest.fit(X_train, y_train)

def plot_feature_importances_cancer(model):
    n_features = cancer.data.shape[1]
    plt.barh(range(n_features), model.feature_importances_, align='center')
    plt.yticks(np.arange(n_features), cancer.feature_names)
    plt.xlabel("Feature importance")
    plt.ylabel("Feature")

plot_feature_importances_cancer(forest)
plt.show()

결과)


위의 결과에서 알 수 있듯, random forest는 단일 결정 트리보다 더 많은 특성들에 0이 아닌 중요도를 보입니다. 단이 결정 트리와 비슷하게 random forest 역시 “worst radius” 특성에 많은 중요도를 할당하지만 가장 높은 중요도는 “worst perimeter”로 드러났습니다. 임의의 트리를 만드는 데에서 발생하는 임의성이 알고리즘에게 가능한 많은 설명들을 고려하게 하여 데이터의 더 넓은 시야를 보게 하기 때문입니다.

 

장점, 단점, 매개변수

 

Random forest는 가장 많이 이용되는 머신 러닝 방법 중 하나입니다. 매우 강력하며 매개변수 조절 없이도 잘 작동하고 데이터의 스케일링을 필요로하지 않습니다.

필연적으로 random forest는 결정 트리와 그 장점을 공유하면서 동시에 단일 결정 트리의 단점을 보완합니다. 단일 결정 트리와 다른 점은 random forest의 수 많은 하위 트리들을 시각화하기가 어렵다는 것입니다.

크기가 큰 데이터 셋을 이용한 random forest 모델링은 시간이 많이 소요되므로 다중 CPU 코어를 통한 병렬화가 쉽게 가능합니다. 이때 n_jobs 매개변수를 이용하여 사용할 CPU 코어의 수를 지정할 수 있습니다. n_jobs=-1로 설정하면 모든 코어를 사용하게 됩니다.

Random forest는 알고리즘 그 자체로 임의성을 가지며 random state를 다르게 가져가면 완전히 다른 모델이 만들어지게 됩니다. Random forest 내 더 많은 트리가 있을수록 random state의 선택에 영향을 덜 받게 됩니다. 재현 가능한 결과를 원한다면 random_state를 고정하는 것이 중요합니다.

Random forest는 텍스트 자료와 같은 고 차원의 sparse data에서 잘 작동하지 않습니다. 이러한 데이터는 linear model이 더 적합할 수 있습니다. 앞서 설명했듯, random forest는 크기가 큰 데이터 셋에서 잘 작동하며 학습은 많은 CPU 코어를 이용한 병렬화로 빠르게 진행될 수 있습니다. 그러나 random forest는 많은 메모리를 요구하며 학습과 예측이 linear model보다 느리다는 단점이 있습니다.

조절 가능한 매개변수로는 n_estimators, max_features, 그리고 pre-pruning 옵션인 max_depth가 있습니다. n_estimators가 클수록 더 나은 성능을 보여주며 과대 적합을 줄이는 단단한 앙상블을 만들게 됩니다. 그러나 메모리와 시간을 많이 필요로 하므로 가능한 많은 메모리와 시간을 할당하는 것이 중요합니다.

max_features는 각 트리가 얼마나 임의적인가를 결정하는 매개변수로 작은 max_features는 과대 적합을 감소시킵니다. 분류 문제에서는 max_features=sqrt(n_features), 회귀 문제에서는 max_features=log2(n_features)를 이용하는 것이 rule of thumb입니다. max_featuresmax_leaf_nodes 조건을 추가하는 것은 때때로 성능을 향상시켜줍니다. 또한 학습과 예측에 필요한 메모리 공간과 시간을 극적으로 줄일 수 있습니다.