강의자료/머신러닝

[캐글(Kaggle) 따라하기]06-1.의사결정 트리

파아란 기쁨 2022. 12. 21. 19:16

https://wondangcom.tistory.com/2354

타이타닉 문제를 해결 하면서 에서 의사결정 트리로 훈련을 해 보았는데요~

오늘은 의사결정트리가 무엇인지 확인을 해 보겠습니다.

 

1. 의사결정트리(Decision Tree)란?

  • 의사결정트리는 일련의 분류 규칙을 통해 데이터를 분류,회귀하는 지도학습 모델 중 하나이다.
  • 결과 모델이 Tree 구조를 가지고 있기 때문에 Decision Tree라는 이름을 가진다.
  • 모델의 형태는 다음과 같다.

이러한 결정트리 모델은 마치 스무고개 문제와 같습니다.

즉 남자인가? 라는 질문에 아니오 라면 생존, 예라면 나이가 10살보다 작은가? 라는  질문에서 예라면 생존, 아니라면 사망 과 같이 한 단계 한단계 질문에 따라 데이터를 구분하는 모델을 의사결정트리 모델이라고 합니다.

 

2. 데이터 준비

- https://www.kaggle.com/code/mungsang/wine-csv-data-binary/data

 

wine_csv_data_binary

Explore and run machine learning code with Kaggle Notebooks | Using data from wine_scv_data

www.kaggle.com

캐글에 있는 Red & White Wine 데이터 셋을 가지고 분류하는 이진 분류 데이터를 가지고 결정트리 모델을 연습해 보도록 하겠습니다.

데이터의 필드를 살펴 보면 alcohol, sugar, pH, class 와 같이 구성되어 있습니다.

이 데이터를 다운로드 하여 코랩에서 연습을 해 보도록 하겠습니다.

먼저 Data 영역에서 Download를 하여 코랩에 업로드 합니다.

3. 데이터 로딩

import pandas as pd
wine = pd.read_csv('wine_csv_data.csv')
print(wine.head())

판다스를 이용해서 csv 파일을 읽어서 5개의 데이터를 확인해 보면 위와 같은 데이터가 나옵니다. 여기서 class는 0이면 레드, 1이면 화이트 와인의 의미입니다.

즉 이진 분류 문제입니다.

판다스의 info()를 이용해서 각열의 데이터 타입과 누락된 데이터 정보를 확인해 보겠습니다.

wine.info() #wine 정보를 확인해 보자

총 6497개의 데이터 중 모든 필드가 6497개의 non-null 이므로 누락 데이터는 없습니다.

만약 누락된 값이 있다면 훈련세트의 평균값으로 채워 주면 됩니다.

다음으로 describe()를 이용해서 열의 통계를 살펴 보겠습니다.

wine.describe()

평균(mean),표준편차(std),최소(min),최대(max),중간값(50%), 1사분위(25%),3사분위(75%) 값을 확인 할 수 있습니다.

여기서 알 수 있는 것은 alchohol 과 sugar,pH 모두 스케일이 다르다는 것입니다.

먼저 데이터를 훈련세트와 테스트 데이터로 분리합니다.

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)
print(train_input.shape,test_input.shape)

우리가 특성의 스케일을 맞추기 위해서 가장 널리 사용하는 전처리 방법 중 하나는 표준점수를 이용해서 같은 스케일로 만들어 주는 방법이 있습니다.

사이킷런에서 제공하는 StandardScaler 클래스는 같은 스케일로 만들어서 훈련을 하기 때문에 여기서는 StandardScale을 이용해서 전처리 합니다.

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

4. 결정트리 모델 훈련하기

결정트리 모델은 스무고개와 같습니다.

데이터를 잘 나눌 수 있는 질문을 찾는 다면 계속 질문을 추가해서 분류 정확도를 높일 수 있습니다.

이것도 사이킷런의 DecisionTreeClassifie 클래스를 사용하면 됩니다.

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

훈련 세트의 점수는 99.7 점 정도이지만 테스트 세트의 점수는 85.9 점 정도로 나오네요~

훈련 세트의 점수가 높고 테스트 세트의 점수가 낮은 것을 보니 과적합된 모델입니다.

이 모델을 그림으로 표현해 보면 다음과 같습니다.

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))
plot_tree(dt,max_depth=1,filled=True,feature_names=['alcohol','sugar','pH'])
plt.show()

max_depth=1 로 파라미터를 설정하면 루트 노드를 제외하고 하나의 노드만 확장해서 그립니다.

feature_names에는 특성의 이름을 전달 할 수 있습니다.

filled = True 로 설정시 클래스마다 색깔을 다르게 부여합니다.

위의 그림의 의미를 살펴 보면

테스트 조건 : sugar <= -0.239 보다 작아지면 왼쪽 아니면 오른쪽

gini : 불순도,

samples : 총 샘플수

values : 클래스별 샘플수

결정트리에서 예측하는 방법은 단순합니다.

리프노드에서 가장 많은 클래스가 예측 클래스가 됩니다.

만약 결정트리의 성장을 위와 같이 1개에서 멈춘다면 왼쪽 노드와 오른쪽 노드에 도달한 샘플은 모두 양성클래스가 됩니다.

왜냐하면 왼쪽과 오른쪽 모두 양성의 개수가 많기 때문입니다.

불순도(gini)란?

gini는 지니불순도(Gini impurity)를 의미한다.
DecisionTreeClassifier 클래스의 criterion 매개변수의 기본값이 'gini'이다. criterion 매개변수의 용도는 노드에서 데이터를 분할할 기준을 정하는 것이다.

위에서 그린 트리에서 루트 노드의 어떻게 -0.239 를 기준으로 왼쪽과 오른쪽으로 나누었을까?
바로 criterion 매개변수에 지정한 지니 불순도를 사용한다. 그렇다면 지니 불순도는 어떻게 계산할까?
지니불순도 = 1 - (음성클래스 비율^2 + 양성 클래스 비율 ^2)
루트노드는 총 5197개의 샘플이 있고, 그중에 1258개의 음성클래스와 3939개의 양성클래스가 있다.
지니불순도 = 1 -( (1258/5197)^2 + (3939/5197)^2) = 0.367
만약 음성과 양성이 정확히 반반이라면 지니불순도는 0.5 가 되어 최악의 경우가 된다.
혹은 노드에 하나의 클래스만 있다면 지니 불순도는 0 이되어 가장 작은데 이런 경우는 순수노드라고 한다.


결정트리 모델은 부모노드(parent node)와 자식노드(child node)의 불순도 차이가 가능한 크도록 트리를 성장시킨다.
부모노드와 자식노드의 불순도 차이를 계산하는 방법을 알아 보자.
먼저 자식노드이 불순도를 샘플 개수에 비례하여 모두 더한다. 그 다음 부모 노드에서 불순도를 빼 주면 된다.
부모의 불순도 -(왼쪽노드 샘플수/부모의 샘플수) * 왼쪽 노드 불순도 - (오른쪽 노드 샘플 수/부모의 샘플 수) * 오른쪽 노드 불순도 = 0.367 - (2922/5197) * 0.481 - (2257/5197) * 0.069 = 0.066
이러한 불순도 차이를 정보이득(information gain)이라고 부른다.
이렇게 정보이득이 최대가 되도록 데이터를 나누는데 이때 지니 불순도를 기준으로 사용한다.


사이킷런의 DecisionTreeClassifier 클래스에서 criterion='entropy' 를 지정하여 엔트로피 불순도를 사용할 수 있다.
엔트로피 불순도는 제곱이 아니라 밑이 2인 로그를 사용하여 곱한다.
-(음성클래스 비율) *log2(음성클래스비율) - 양성클래스비율 * log2(양성클래스비율)
= -(1258/5197) * log2(1258/5197) - (3939/5197) * log2(3939/5197) = 0.798


보통 기본값인 지니 불순도와 엔트로피 불순도가 만든 결과의 차이는 크지 않다.
여기서는 기본값인 지니 불순도를 계속 사용한다.


5. 가지치기

열매를 잘 맺기 위해 과수원에서 가지치기를 하는 것과 마찬가지로 결정 트리도 가지치기를 해야 합니다.

결정트리에서 가지치기를 하는 가장 간단한 방법은 자라날 수 있는 트리의 최대 깊이를 지정하는 것입니다.

max_depth를 3으로 지정해 봅시다.

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

훈련세트와 테스트 세트의 성능 모두 낮아 졌습니다.

이 모델을 트리 그래프로 그려 봅니다.

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

깊이 3의 왼쪽 첫번째는 sugar 를 기준으로 나누고 두번째는 alcohol,세번째와 네번째는 pH를 기준으로 나누는 것을 알 수 있습니다.

그런데 여기서 sugar 가 -0.854 라는 음수로 된 당도가 이상하네요~

불순도를 기준으로 샘플을 나눈다고 했는데 불순도는 클래스별 비율을 가지고 계산을 합니다.

그렇다면 샘플을 어떤 클래스 비율로 나누는지 계산할 때 특성값의 스케일이 계산에 영향을 미칠까요?

그렇지 않습니다. 여기서 특성값의 스케일은 아무런 영향을 미치지 않습니다. 따라서 결정트리에서는 표준화 전처리를 할 필요가 없습니다.

이것이 결정 트리 알고리즘의 장점 중 하나입니다.

그렇다면 전처리 하기 전의 훈련세트와 테스트 세트로 결정트리 모델을 다시 훈련해 봅니다.

dt=DecisionTreeClassifier(max_depth=3)
dt.fit(train_input,train_target)
print(dt.score(train_input,train_target))
print(dt.score(test_input,test_target))

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

전처리 전과 후의 결과가 같은 것을 알 수 있습니다. 그래프는 특성값을 표준점수로 바꾸지 않은 경우라서 이해하기가 훨씬 쉽습니다.

 

마지막으로 결정트리는 어떤 특성이 가장 유용하게 사용되는지 특성 중요도를 계산할 수 있습니다.

이 트리의 루트 노드와 깊이 1에서 당도를 사용했기 때문에 당도(sugar)이 유용한 특성중 하나 일 것 같습니다.

정말 그런지 결정트리 모델에서 사용한 특성 중요도를 살펴 보겠습니다.

결정트리 특성 중요도는 feature_importances_  속성에 저장되어 있습니다.

print(dt.feature_importances_)

특성이 'alcohol','sugar','pH' 순으로 들어 갔는데 역시 sugar가 가장 중요도가 높은 것을 알 수 있네요.

 

와인 분류 문제를 통해서 의사결정트리 알고리즘에 대해 알아 보았는데요~

결정트리에서는 지니 불순도에 의해서 샘플을 나누고 또한 결정트리에서는 특성값의 스케일이 서로 달라도 전혀 문제가 없기 때문에 표준화 작업이 필요 없다는 점을 이해 하시면 될 것 같습니다.

또한 min_impurity_decrease 매개변수를 이용해서 가지치기를 할 수도 있는데요~ 이 매개변수는 어떤 노드의 정보이득 * (노드의 샘플수) / (전체 샘플수) 값이 매개변수보다 작으면 더 이상 분할 하지 않습니다.

이러한 매개변수 값을 이용해서 의사결정 트리의 성능을 높일 수가 있습니다.

 

 

참고]

혼자 공부하는 머신러닝

사업자 정보 표시
원당컴퓨터학원 | 기희경 | 인천 서구 당하동 1028-2 장원프라자 502호 | 사업자 등록번호 : 301-96-83080 | TEL : 032-565-5497 | Mail : icon001@naver.com | 통신판매신고번호 : 호 | 사이버몰의 이용약관 바로가기