[혼공머신] 결정트리와 불순도

강민우·2022년 1월 22일
0
post-thumbnail

[혼자 공부하는 머신러닝+딥러닝] 책에 기반한 정리글입니다.
전체 소스코드는 아래 Github 링크에서 확인할 수 있습니다.

Github 링크

0. 개요

결정 트리는 예, 아니오에 대한 질문을 이어나가며 정답을 찾아 학습하는 알고리즘이다.

불순도는 결정 트리가 최적의 질문을 하기 위한 기준이다. 사이킷런에서는 지니 불순도와 엔트로피 불순도를 제공한다.

1. 데이터 준비하기

csv파일을 numpy배열로 변환한다.

import pandas as pd
wine = pd.read_csv('https://bit.ly/wine_csv_data')
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)

#전처리
from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
ss.fit(train_input)
train_scaled = ss.transform(train_input)
test_scaled = ss.transform(test_input)

2. 결정트리

2-1. 결정트리 모델 만들기

사이킷런의 DecisionTreeClassifier 클래스로 결정 트리 알고리즘을 사용할 수 있다.
모델 훈련 후 정확도를 평가한다.

from sklearn.tree import DecisionTreeClassifier
dt = DecisionTreeClassifier(random_state=42)
dt.fit(train_scaled, train_target)
print(dt.score(train_scaled, train_target))
print(dt.score(test_scaled, test_target))

출력
0.996921300750433
0.8592307692307692
과대적합 결과가 나타난다.

2-2. 훈련한 결정트리 모델을 그림으로 나타내기

import matplotlib.pyplot as plt
from sklearn.tree import plot_tree
plt.figure(figsize=(10,7)) # 그래프사이즈 설정, 가로 세로 inch기준
plot_tree(dt)
plt.show()


트리의 깊이를 제한하지 않아 복잡한 트리가 그려졌다.

2-3. 트리의 깊이를 제한하기

max_depth 매개변수는 입력된 숫자 + 1 의 깊이로 트리가 그려진다.
filled 매개변수는 클래스에 맞게 노드의 색을 칠한다.
feature_names 매개변수는 특성의 이름을 전달한다.

plt.figure(figsize=(10,7))
plot_tree(dt, max_depth=1, filled=True, feature_names=['alcohol', 'sugar', 'pH']) 
plt.show()

samples는 총 샘플 수, gini는 불순도를, value는 클래스별 샘플 수를 의미한다.
결정 트리에서 예측하는 방법은, 리프 노드에서 가장 많은 클래스가 예측 클래스가 된다.
위 사진에서 두 노드 모두 양성 클래스가 많아 양성 클래스로 추측된다.

3. 불순도

앞에서의 gini는 지니 불순도를 의미한다. DecisionTreeClassifier 클래스의 criterion 매개변수의 기본값은 gini이며, criterion='entropy'면 엔트로피 불순도를 사용한다.

지니 불순도는 클래스의 비율을 제곱해서 더한 후 1에서 빼면 된다.
지니 불순도 = 1 - (음성 클래스 비율^2 + 양성 클래스 비율^2)

결정 트리 모델은 부모노드와 자식노드의 불순도 차이가 가능한 크도록 트리를 성장시킨다. 자식 노드의 불순도를 샘플 개수에 비례하여 모두 더한 후 부모 노드의 불순도에서 빼면 된다.

위 그림에서 불순도의 차이는
부모의 불순도 - (왼쪽노드 샘플 수 / 부모의 샘플 수) x 왼쪽 노드 불순도 - (오른쪽노드 샘플 수 / 부모의 샘플 수) x 오른쪽 노드 불순도
= 0.367 - (2922/5197) x 0.481 - (2275/5197) x 0.069 = 0.066

이렇게 부모와 자식 노드 사이의 불순도 차이를 정보 이득이라고 한다.

4. 가지치기

결정 트리에서 가지치기를 하지 않는다면 훈련세트에는 아주 잘 맞지만 테스트세트에는 그렇지 못하는 결과가 나타난다.

가지치기를 하는 가장 간단한 방법은 트리의 최대 깊이를 지정하는 것이다.

dt = DecisionTreeClassifier(max_depth=3, random_state=42)
dt.fit(train_scaled, train_target)
print(dt.score(train_scaled, train_target))
print(dt.score(test_scaled, test_target))

출력
0.8454877814123533
0.8415384615384616
훈련 세트의 성능이 전보다 낮아졌다.

이를 그림으로 그려본다.

plt.figure(figsize=(20,15))
plot_tree(dt, 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()


전처리를 하지 않아 이해가 쉽다.

결정 트리는 어떤 특성이 가장 유용한지 나타내는 특성 중요도를 계산해 준다. 특성 중요도는 결정 트리 모델의 feature_importance_ 속성에 존재한다.

print(dt.feature_importances_)

출력 [0.12345626 0.86862934 0.0079144 ]
각각 알코올도수, 당도, pH이다.
당도가 가장 유용한 특성임을 알 수 있다.

profile
어제보다 성장한 오늘

0개의 댓글