Day to_day

[지도 학습] 결정 트리(Decision Tree)의 원리와 유의점 (하이퍼 파라미터 이해, Feature importance) 본문

Machine Learning

[지도 학습] 결정 트리(Decision Tree)의 원리와 유의점 (하이퍼 파라미터 이해, Feature importance)

m_inglet 2023. 3. 2. 01:25
728x90
반응형

❗본 포스팅은 권철민 선생님의 '파이썬 머신러닝 완벽가이드' 강의와 '파이썬 라이브러리를 활용한 머신러닝' 서적을 기반으로 개인적인 정리 목적 하에 재구성하여 작성된 글입니다.

 

 

포스팅 개요

지도 학습에서 많은 전처리 없이 쉽게 다뤄볼 수 있는 모델 결정 트리(Decision Tree, 의사결정트리, 의사결정나무라고도 함)에 대해서 알아보고, 개념과 사용 예제, 트리 모델의 장단점까지 정리해보고자 한다.

 

 

결정 트리 (Decision Tree)

결정 트리의 원리는 쉽게 말하면 '스무고개' 놀이와 같이 예/아니오 질문을 이어가면서 학습하는 방식이다.

데이터를 하나의 기준에 대해 해당하는 지, 해당하지 않는 지를 분류하여 아래와 같이 트리처럼 데이터를 나누게 된다.

 

위의 그림은 결정 트리를 도식화 한 것이다.

각각의 네모는 '노드'를 뜻하며 맨 꼭대기. 즉 전체 데이터에 해당하는 것은 '루트 노드', 규칙이 있는 곳이 '규칙 노드', 그리고 더 이상 분할되지 않고 데이터가 균일하다고 판단되면 그 값은 '리프 노드'라고 칭한다. 

 

데이터가 균일?

이 말이 의미하는 것을 좀 더 자세히 설명하려고 한다.

그림을 보면 첫번째는 노란색 영역 안에 있는 데이터가 섞여서 균일하지 않고, 두번째의 경우는 데이터가 섞이지 않고 파란색 데이터만 모여서 균일하다고 말할 수 있다.

이렇듯 결정 트리 과정에서 분할을 했을 때, 더 이상 다른 데이터가 섞여있지 않고 균일하게 모여있으면 분할을 멈춘다는 뜻이다.

 

 

결정 트리의 과정을 정리하면 다음과 같다.

1. 가능한 한 많은 데이터를 구분 지을 수 있는 기준을 선택 (규칙 조건)

2. 나누어진 노드의 불순도를 평가

3. 각 노드에서 하나의 규칙 조건을 가진 이진 결정 트리를 진행

4. 각 분할된 영역이 한 개의 타깃값(하나의 클래스)만 가질 때까지 위의 내용을 반복

( * 이때 하나의 클래스로만 이루어진 리프노드를 순수 노드라고 한다.)

 

 

[예제 코드]

from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer
from sklearn.tree import DecisionTreeClassifier

cancer = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target, stratify=cancer.target, random_state=42)

dt = DecisionTreeClassifier()
dt.fit(X_train, y_train)
accuracy_train = dt.score(X_train, y_train)
accuracy_test = dt.score(X_test, y_test)
print("Train dataset 정확도: ", accuracy_train) # 과적합이 되어 학습 데이터셋에 대해서는 100% 정답
print("Test dataset 정확도: {0:.3f}".format(accuracy_test)) # 테스트 데이터셋은 92%의 정답률

 

 

 

결정 트리 모델의 시각화 - Graphviz

실제 데이터를 결정 트리 모델에 적용해보았을 때 시각화 하여 트리 형식으로 보고 싶을 것이다. 

그리고 시각화 했을 때 더 직관적으로 분할 과정을 알기 쉽다.

Graphviz를 이용하면 되는데 자세한 설치 방법은 다음 링크를 참고하면 된다. 

 

시각화 예시는 다음과 같다.

첫 번째 맨 위에 있는 노드를 보자.

  • 기준 : petal length ≤ 2.45
  • gini 계수 : 0.667 (0에 가까울수록 균일함)
  • sample : 120 (현재 규칙에 해당하는 데이터 개수)
  • value = [41, 40, 39] (value 중에서 가장 큰 것을 기준으로 뚝 떼어낸다.)
  • class : setosa (가장 많은 비율을 차지한 (41에 해당하는) 클래스는 setosa)
  • 기준에 의해서 True, False로 나누고 다시 또 이 과정을 반복
  • 조건이 붙어있으면 자식 노드를 생성한다.
  • 그렇지 않고 조건이 없으면 리프 노드로 마침

그리고 주황색의 노드는 리프 노드로 gini 계수가 0인 것을 볼 수 있다.

그렇담 여기 나오는 gini 계수는 무엇인가?

 

 

정보 균일도 측정 방법

아까 말했던 데이터를 분할 했을 때 균일하다면 분할을 멈춘다고 했다. 우리는 그 균일하다는 '정도'를 알아야 한다.

그래서 두 가지가 있는데 첫 번째는 '정보 이득', 두 번째는 '지니 계수'가 있다.

 

정보 이득

  • 엔트로피 개념을 기반으로 한다. 불확실성, 불균일성을 보는 지표이다.
  • 서로 다른 값이 섞여있으면 엔트로피가 높고, 같은 값이 섞여있으면 엔트로피가 낮다. 즉, 정보 이득과 엔트로피 지수는 서로 반대의 값을 보이게 된다. 그래서 결정 트리를 분할하는 기준은 정보 이득이 높으면 분할을 멈추게 된다.
  • 정보가 균일하지 않다면 엔트로피 지수는 높을 것이고, 정보 이득은 낮을 것이다.
  • 정보이득 : 1 - 엔트로피 지수

 

지니 계수

  • 경제학에서 불평등 지수를 나타낼 때 사용하는 계수
  • 0이 가장 평등하고, 1로 갈수록 불평등하다.
  • 머신러닝에 적용될 때는 지니 계수가 낮을수록 데이터 균일도가 높은 것으로 해석되며 계수가 낮은 속성을 기준으로 분할한다.

 

 

결정 트리의 유의점

일반적으로 트리를 만들 때 모든 리프 노드가 순수 노드가 될 때까지 진행하면, 모델이 매우 복잡해지고 훈련 데이터 셋에 과대적합된다. 그래서 과대적합을 막기 위해 여러 방법이 있다.

 

결정 트리에 아무 조건을 붙이지 않고 실행한 경우 (너무 세밀한 구역까지 나눔 - 과대적합)

 

과대적합을 막는 방법

  1. 트리 생성을 일찍 중단하는 방법 (사전 가지치기)
    • 트리의 최대 깊이 제한
    • 리프의 최대 개수 제한
    • 노드가 분할하기 위한 포인트의 최소 개수 지정
  2. 트리를 만든 후 처리하는 방법 (사후 가지치기)
    • 데이터 포인트가 적은 노드 삭제 또는 병합

( * scikit-learn에서는 사전 가지치기만 지원)

 

 

결정 트리의 주요 하이퍼 파라미터

앞서 말했듯이 모델을 일반화시키기 위해서 결정 트리의 하이퍼 파라미터 튜닝을 통해 과대적합을 막아야 한다.

주요 하이퍼 파라미터는 다음과 같다.

  • max_depth : 분할하는 횟수, 트리의 개수
  • max_features : 학습할 때 모든 feature를 사용 안 해도 된다. 분할할 수 있는 규칙이 너무 많아져서 너무 tree가 커질 수 있기 때문이다.
  • min_samples_split (defualt : 2) : 만약에 samples를 4라고 할 때 samples가 3개 남아있으면 더 이상 분할하지 않는다는 의미
  • min_samples_leaf : 분할이 될 경우 왼쪽과 오른쪽의 브랜치 노드에서 가져야 할 최소한의 샘플 데이터 수. 만약 3이라고 하면 남아있는 샘플 데이터 수가 5개라면 어떻게 나눠도 양쪽의 노드에 최소 3개의 샘플 데이터를 줄 수가 없다. 그렇다면 분할을 중단한다.
  • max_leaf_nodes : 말단 노드(leaf node)의 최대 개수

 

[예제 코드]

# 트리의 깊이 제한
dt_limit = DecisionTreeClassifier(max_depth=4, random_state=0)
dt_limit.fit(X_train, y_train)

print("Train dataset 정확도: {0:.3f}".format(dt_limit.score(X_train, y_train))) # train 정확도 0.988
print("Test dataset 정확도: {0:.3f}".format(dt_limit.score(X_test, y_test))) # test 정확도 0.951

 

 

결정 트리의 특성 중요도 (Feature Importance)

트리를 만드는 결정에 각 특성이 얼마나 중요한지를 평가하는 것이 특성 중요도이다.

이 값은 0-1 사이의 숫자로, 각 특성에 대해 0은 전혀 사용되지 않음을 뜻하고 1은 완벽하게 타깃 클래스를 예측했다는 뜻이다.

 

중요하게 알아야 할 점이 있는데 ‘어? 그러면 중요한 feature를 알았으니까 이 feature들만 가지고 분류를 하면 다 분류가 되는 건가?’ 하고 생각할 수 있는 게 그걸 의미하는 게 아니다.

단지 트리 모델에서 이진 분류를 할 때 어떤 feature가 많이 쓰이는 가를 집계해 나타내는 것이다.

어떤 특성의 feature_importance_ 값이 낮다고 해서 이 특성이 유용하지 않다는 것이 아니라 트리가 단지 그 특성을 선택하지 않았을 뿐이며 다른 특성이 동일한 정보를 지니고 있어서일 수도 있다.

특성 중요도는 항상 양수이며 특성이 어떤 클래스를 지지하는지는 알 수가 없다. 어떤 특성이 높고 낮음이 어떤 클래스를 의미하는지 알 수 없다는 말이다.

 

feature importance는 feature_importances_ 모듈로 접근할 수 있다.

print("특성 중요도 \n", dt_limit.feature_importances_)

 

 

결정 트리의 특성 중요도 시각화

# feature importance 시각화하기
import matplotlib.pyplot as plt
import numpy as np

def plot_feature_importances_cancer(model):
    n_features = cancer.data.shape[1]
    plt.barh(np.arange(n_features), model.feature_importances_, align='center')
    plt.yticks(np.arange(n_features), cancer.feature_names)
    plt.xlabel("feature importances")
    plt.ylabel("feature")
    plt.ylim(-1, n_features)
    
plot_feature_importances_cancer(dt_limit)

 

 

정리

결정 트리는 데이터의 스케일에 구애받지 않으므로 특성의 정규화나 표준화 같은 전처리 과정이 따로 필요 없다. 또한 스케일이 서로 다르거나 이진 특성과 연속적인 특성이 혼합되어 있을 때도 잘 작동한다.

다만, 가지치기를 적용함에도 불구하고, 과대적합되는 경향이 있어 일반화 성능이 좋지 않을 수 있다.

과대적합을 막기 위한 하이퍼 파라미터 튜닝에 더욱 신경 써야 한다.

 

728x90
반응형
BIG

'Machine Learning' 카테고리의 다른 글

머신러닝의 유형? 알고 넘어가기!  (0) 2023.01.18
Comments