물먹는산세베리아

[혼공머신]05-1 결정 트리 본문

AI/Machine Learning & Deep Learning

[혼공머신]05-1 결정 트리

suntall 2022. 7. 14. 10:58
import pandas as pd
wine = pd.read_csv('https://bit.ly/wine_csv_data')

 이번에도 샘플로 사용할 데이터셋을 가져왔다.

wine.head()

head() 메서드로 처음 5개의 샘플을 확인할 수 있다.

차례대로 알코올 도수, 당도, pH를 뜻하며 마지막 class는 타깃값으로 0이면 레드와인, 1이면 화이트 와인을 나타낸다.

이때 화이트 와인이 양성 클래스이기 때문에 전체 와인 데이터에서 화이트 와인을 골라내는 문제라고 볼 수 있다.

 

info() 메소드

wine.info()

*wine은 데이터프레임

=> 누락된 값 없음 확인!

누락된 값 있으면 버리거나 평균값으로 채우기

 

describe() 메소드

wine.describe()​

평균, 표준편차, 최소, 1사분위수, 2사분위수(중간값), 3사분위수, 최대 값을 보여 준다.

*2사분위수는 데이터를 일렬로 늘어놓았을 때 정중앙 값(또는 가운데 2개값 평균)

알코올 도수, 당도, pH 값의 스케일이 다르기 때문에 StandardScaler 클래스를 사용해 특성을 표준화해야한다.

알코올은 1~14, sugar은 0~65..차이 많이 나네

 

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

class는  target 배열에 따로 저장.

나머지는 data배열에

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)

훈력세트, 테스트세트 나누기 train_test_split()

기본값 25% (test_size)

print(train_input.shape, test_input.shape)

훈련세트 5197개, 테스트 세트 1300개

 

 

전처리하기

from sklearn.preprocessing import StandardScaler

ss = StandardScaler()
ss.fit(train_input)

train_scaled = ss.transform(train_input)
test_scaled = ss.transform(test_input)

단골코드(?)

StandardScaler 클래스로 훈련 세트 전처리, 그대로 같은 객체 사용해서 테스트세트 전처리하기

from sklearn.linear_model import LogisticRegression

lr = LogisticRegression()
lr.fit(train_scaled, train_target)

print(lr.score(train_scaled, train_target))
print(lr.score(test_scaled, test_target))​

훈련 세트, 테스트 세트 모두 표준점수로 변환되었고, train_scaled, train_target으로 로지스틱 회귀 모델을 훈련할 예정

둘 다 0.7x가 나왔는데 점수가 높지 않아 어떤게 화이트 와인인지 골라내는게 어렵다.

이럴 때는

1. 규제 매개 변수 C의 값을 바꾸거나

2. solver 매개변수에서 다른 알고리즘으르 선택하거나

3. 다항 특성을 만들어 추가할 수도 있다.

 

어떻게 해석?

print(lr.coef_, lr.intercept_)

로지스틱 회귀가 학습한 계수와 절편임

?????

 

순서도!를 써보자 => 결정트리

 

결정트리

질문 -> 답 반복 => '이유를 설명하기 쉽다' 

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

DecisionTreeClassifier 클래스, fit() 훈련, score() 정확도 평가 사용

*random_state는 실전에서 안 씀. 책에서는 같은 값을 유지하기 위해서 쓴 것

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))​

*Classifier은 분류에, Regressor은 회귀에 쓰임

훈련세트의 점수는 엄청 높은 반면 테스트 성능은 상대적으로 낮아 '과대적합된 모델'이라고 볼 수 있다.

*과대적합: 훈련 세트는 점수가 굉장히 좋은 반면 테스트 세트는 점수가 굉장히 나쁘면 모델이 훈련 세트에 과대적합된 것

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

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

출처: 혼자 공부하는 머신러닝 딥러닝 github

plot_tree() 함수를 쓰면 그림으로 출력할 수 있다.

맨 위의 노드를 루트노드, 말단 노드를 리프노드라고 부른다.

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

위 코드에서 max_depth루트 노드를 제외한 확장 노드의 개수,

filled노드의 색 유무

feature_names특성의 이름을 전달한다.

출처: 혼자 공부하는 머신러닝 딥러닝 github

노드 안에 들어있는 내용은 다음과 같다.

테스트 조건(sugar)
분순도(gini)
총 샘플 수(samples)
클래스별 샘플 수(value)

트리를 해석해보면

루트 노드에서 sugar가 -0.239인 경우 왼쪽(Yes)으로, 그렇지 않으면 오른쪽(No)으로 이동한다.

samples 수는 5197이고 그 중 음성 클래스(레드 와인)은 1258개, 양성 클래스(화이트 와인)은 3939개이다.

자식 노드들도 똑같이 해석하면 된다.

(gini는 뒤에서 나옴)

전체적으로 봤을 때 왼쪽 자식 노드에서는 양성 클래스의 비율이 줄었고, 오른쪽 자식 노드에서는 양성 클래스의 비율이 크게 늘었는데 노드의 색을 보면 이 노드가 훨씬 진한 것을 알 수 있다. 어떤 클래스의 비율이 높아질수록 색이 진해진다고 한다.(plot_tree()함수에서 filled=True 지정 시)

결과적으로 결정 트리에서 예측하는 방법은 리프 노드에서 가장 많은 클래스가 예측 클래스가 된다.

 

불순도

(1) gini '지니 불순도'

DecisionTreeClassifier 클래스의 criterion 매개변수의 기본값이 gini이다.

위의 루트 노드에서 당도 -0.239를 기준으로 왼쪽과 오른쪽 노드를 나눌때 criterion 매개변수에 지정한 지니 불순도를 사용한다.

지니 불순도 = 1 - (음성 클래스 비율^2 + 양성 클래스 비율^2)

 즉, 1- ((1258/5197)^2 + (3939/5197)^2) = 0.367이 된다.

 

결정 트리 모델은 부모 노드와 자식 노드의 불순도 차이(=정보 이득)가 가능한 크도록 트리를 성장시키는데

그 차이를 계산하려면 자식 노드의 불순도를 샘플 개수에 비례하여 모두 더한 후 부모 노드의 불순도에서 빼면 된다.

 

(2) 엔트로피 불순도

DecisionTreeClassifier  클래스에서 criterion='entropy'를 지정하여 불순도 사용

밑이 2인 로그를 사용

기본 값인 지니 불순도 결과가 만든 차이는 크지 않다. (불순도 자체의 차이 아님)

 

결론: 불순도 기준을 사용해 정보 이득이 최대가 되도록 노드를 분할하고, 마지막에 도달한 노드의 클래스 비율을 보고 예측을 만든다.

 

가지치기

가장 간단한 방법 => 트리 최대 깊이 지정하기

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))

DecisionTreeClassifier 클래스의 max_depth 매개변수는 루트 아래로 최대 n개의 노드까지만 성장할 수 있도록 제한해준다.

훈련 세트 성능은 0.99에서 낮아졌지만 테스트 성능은 거의 그대로이다.

출처: 혼자 공부하는 머신러닝 딥러닝 github

왼쪽에서 세번째 노드에 도달해야만 레드 와인으로 예측

즉, 레드 와인으로 예측하려면 당도는 -0.239보다 작고 -0.802보다 커야 하며 알코올 도수는 0.454보다 작아야 한다.

 

음수인데..?

불순도를 기준으로 샘플을 나누는데 이 불순도는 클래스별 비율을 갖고 계산된다. 하지만 특성값의 스케일이 계산에 영향을 미치지 않기 때문에 표준화 전처리를 할 필요가 없다.

=> 전처리 전의 훈련 세트와 테스트 세트로 다시 결정 트리 모델 훈련

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

print(dt.score(train_input, train_target))
print(dt.score(test_input, test_target))

결과 똑같음.

표준 점수로 바꾸지 않았을 때의 트리임

 

print(dt.feature_importances_)

여러 특성 중 뭐가 가장 유용했는지 계산한 결과를 담고 있는 속성이다. freature_importances_

가운데 (sugar)가 가장 중요한 속성임을 알 수 있다.