본문 바로가기
Machine Learning

[혼자 공부하는 머신러닝+딥러닝] 결정 트리

by 가론노미 2023. 5. 9.
[혼자 공부하는 머신러닝+딥러닝] 책의 내용을 정리한 글입니다.

개념

로지스틱 회귀 모델 같은 대부분의 머신러닝 모델은 모델이 왜 계수 값을 이렇게 학습했는지 학습의 결과를 설명하기가 어렵다.

쉬운 방법으로 학습을 설명할 수 있는 모델은 없을까?

결정 트리

: 예/아니오에 대한 질문을 이어나가면서 정답을 찾아 학습하는 알고리즘

  • 비교적 예측 과정을 이해하기 쉽고 성능도 뛰어나다
  • 결정 트리의 맨 위의 노드를 루프 노드, 맨 아래 끝에 달린 노드를 리프 노드라고 한다
  • 제한 없이 성장할 경우 훈련 세트에 과대적합되기 쉽다
    • 사이킷런에서는 결정 트리의 성장을 제한하기 위한 여러 가지 가지치기 매개변수를 제공
  • 특성 값의 스케일은 결정 트리 알고리즘에 아무런 영향을 미치지 않는다
    • 표준화 전처리를 할 필요가 없음

불순도

: 결정 트리가 최적의 질문을 찾기 위한 기준

  • 사이킷런의 결정 트리 클래스 DecisionTreeClassfier에서 criterion 매개변수를 통해 불순도 기준을 지정한다
    • gini - 클래스의 비율을 제곱해서 더한 다음 1에서 뺌
    • entropy - 제곱이 아닌 밑이 2인 로그를 사용하여 곱함

 

정보 이득

: 부모 노드와 자식 노드의 불순도 차이

 

→ 결정트리 알고리즘은 불순도 기준을 사용해 정보 이득이 최대화되도록 학습한다.

특성 중요도

: 결정 트리에 사용된 특성이 불순도를 감소하는데 기여한 정도를 나타내는 값

  • 각 노드의 정보 이득과 전체 샘플에 대한 비율을 곱한 후 특성별로 더하여 계산
  • 특성 중요도를 활용하여 특성 선택에 활용할 수 있다는 점이 결정 트리 알고리즘의 또 다른 장점

코드 작성

비교적 비전문가에게도 설명하기 쉽도록 결정 트리를 사용해 와인 분류 문제를 풀어보자

데이터 준비

import pandas as pd

wine = pd.read_csv('https://bit.ly/wine_csv_data')

wine.describe()
data = wine[['alcohol', 'sugar', 'pH']].to_numpy()
target = wine['class'].to_numpy()

# 훈련 세트와 테스트 세트 분리
from sklearn.model_selection import train_test_split

train_input, test_input, train_target, test_target = train_test_split(
    data, target, test_size=0.2, random_state=42)

결정 트리 모델 훈련 후 score 확인

from sklearn.tree import DecisionTreeClassifier

dt = DecisionTreeClassifier(random_state=42)
dt.fit(train_input, train_target)

print(dt.score(train_input, train_target))
print(dt.score(test_input, test_target))
# => 0.996921300750433
# => 0.8584615384615385

결정 트리 그림을 출력하여 확인해 보기

import matplotlib.pyplot as plt
from sklearn.tree import plot_tree

plt.figure(figsize=(10,7))
plot_tree(dt)
plt.show()

plt.figure(figsize=(10,7))
# max_depth를 2로 주면 루트 노드를 제외하고 하나의 노드를 더 확장하여 그림
plot_tree(dt, max_depth=1, filled=True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show()

결정 트리가 과대 적합되지 않도록 가장 간단한 방법으로 트리의 최대 깊이를 지정하여 가지치기 진행

dt = DecisionTreeClassifier(max_depth=3, random_state=42)
dt.fit(train_input, train_target)

print(dt.score(train_input, train_target))
print(dt.score(test_input, test_target))
# => 0.8454877814123533
# => 0.8415384615384616

plt.figure(figsize=(20,15))
plot_tree(dt, filled=True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show()

위 결정 트리 모델에서 어떤 특성이 가장 유용한지 나타내는 특성 중요도 확인

print(dt.feature_importances_)
# => [0.12345626 0.86862934 0.0079144 ]
# 두 번째 특성인 당도가 가장 높은 것을 확인할 수 있음

 

이 장에서 사용된 핵심 패키지와 함수

pandas

  • info()
    • 데이터프레임의 요약된 정보를 출력
    • 인덱스와 컬럼 타입을 출력하고 널이 아닌 값의 개수, 메모리 사용량을 제공
  • describe()
    • 데이터프레임 열의 통계 값을 제공
    • 수치형일 경우 최소, 최대, 평균, 표준편차와 사분위값 등이 출력
    • 문자열 같은 객체타입의 열은 가장 자주 등장하는 값과 횟수 등이 출력

scikit-learn

  • DecisionTreeClassifier
    • 결정 트리 분류 클래스
    • criterion - 불순도를 지정(기본값 - gini)
    • splitter - 노드를 분할하는 전략을 선택 (기본값 - best)
      • best - 정보 이득이 최대가 되도록 분할
      • random - 임의로 노드를 분할
    • max_depth - 트리가 성장할 최대 깊이를 지정 (기본값 - None)
    • min_samples_split - 노드를 나누기 위한 최소 샘플 개수 (기본값 - 2)
    • max_features - 최적의 분할을 위해 탐색할 특성의 개수를 지정 (기본값 - None)
  • plot_tree()
    • 결정 트리 모델을 시각화
    • max_depth - 나타낼 트리의 깊이를 지정 (기본값 - None)
    • feature_names - 특성의 이름을 지정할 수 있음
    • filled - True로 지정하면 타깃값에 따라 노드 안에 색을 채움