--15.결정트리(Decision Tree).ipynb--
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import os
base_path = r'/content/drive/MyDrive/dataset'
"""
데이터 : Red Wine Quality
출처 : https://www.kaggle.com/datasets/uciml/red-wine-quality-cortez-et-al-2009
alcohol : 알코올 도수
sugar : 당도
pH : pH
class : 타깃값! 0 이면 레드와인, 1 이면 화이트 와인 <= 이진분류문제!
화이트와인이 양성클래스. 전체와인 데이터에서 화이트와인을 골라내는 문제다!
"""
None
file_path = os.path.join(base_path, 'wine.csv')
wine_df = pd.read_csv(file_path)
wine_df
wine_df.info()
wine_df.describe()
"""
balanced vs imbalanced 여부
suger는 뭔가 imbalanced 해보인다.
데이터는 양성클래스가 더 많은 것 같다.
alcohol, suger, ph 모두 스케일이 다르다 -> 전처리 필요
"""
None
wine_df.columns
data = wine_df[['alcohol', 'sugar', 'pH']].to_numpy()
target = wine_df['class'].to_numpy()
data
target
np.unique(target) # 반드시 확인해야함!!
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_input.shape, test_input.shape
train_target.shape, test_target.shape
from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
ss.fit(train_input)
train_scaled = ss.transform(train_input)
test_scaled = ss.transform(test_input)
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))
"""
↑ 둘다 점수가 높지 않다 -> 과소적합(underfit) 의심
"""
None
print(lr.coef, lr.intercept)
"""
z = 0.51270274 x 알코올 + 1.6733911 x 당도 + (-0.68767781) x ph + 1.81777902
z 값이 0보다 크면 화이트 와인(양성), 작으면 레드와인(음성)
현재 정확도는 77% 정도
"""
None
"""
대부분의 머신러닝의 모델은 학습의 결과를 설명하기 어렵다.
'순서도 (flow chart)' 처럼 알기 쉽게 설명이 되는 모델은 없을까?
"""
None
결정트리
"""
결정트리는 "이유를 설명하기 쉽다"
질문을 하나하나 던져 가면서 정답을 맞추어 나가는 모델 (like 스무고개)
데이터를 잘 나눌 수 있는 질문들을 찾아서 정확한 값을 도출한다.
"""
None
from sklearn.tree import DecisionTreeClassifier
dt = DecisionTreeClassifier(random_state=42)
dt.fit(train_scaled, train_target)
dt.score(train_scaled, train_target), dt.score(test_scaled, test_target)
"""
↑ 로지스틱 회귀에 비해 높은 점수이긴 하나 훈련세트에 대한 점수가 매우 높다 (거의 다 맞춤?)
테스트 세트에 대한 점수가 상대적으로 많이 낮다. => 과대적합(Overfit) 의심.
"""
None
"""
사이킷럿의 결정 트리 알고리즘은 노드에서 최적의 분할을 찾기 전에 특성의 순서를 섞습니다.
따라서 약간의 무작위성이 주입되는데 실행할 때마다 점수가 조금씩 달라질 수 있기 때문입니다.
여기에서는 실습한 결과가 같도록 유지하기 위해 random_state 를 지정합니다.
"""
None
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()
dt = DecisionTreeClassifier(max_depth=3, random_state=42)
dt.fit(train_scaled, train_target)
dt.score(train_scaled, train_target), dt.score(test_scaled, test_target)
plt.figure(figsize=(20, 15))
plot_tree(dt, filled = True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show()
"""
위에서 레드와인 (음성클래스)로 판정하는 경우는?
당도는 -0.239보다 작고, -0.802보다 커야하고, 알코올 도수는 0.454 보다 작아야한다.
"""
None
"""
그런데, 당도가 음수다? (말이 안된다.) => 이유는? 전처리(표준화) 했기 때문.
결정트리는 '불순도'를 기준으로 샘플을 나눈다 => 클래스별 비율을 가지고 계산한다.
따라서 스케일은 영향을 주지 않는다!
전처리 필요가 없다 => 결정트리 알고리즘의 장점중 하나
"""
None
dt = DecisionTreeClassifier(max_depth=3, random_state=42)
dt.fit(train_input, train_target)
dt.score(train_input, train_target), dt.score(test_input, test_target)
plt.figure(figsize=(20, 15))
plot_tree(dt, filled = True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show()
"""
이제 제대로 된 수치로 에측 결과에 대한 수치 설명이 가능해졌다.
당도가 1.625 보다 크고 4.325보다 작은 와인 중에 알코올 도수가 11.025 와 같거나 작은 것이 레드 와인이고!
그 외에는 모두 화이트 와인으로 예측
"""
None
dt.featureimportances
validation set
train_input.shape, test_input.shape # 8:2로 쪼개놓은 상태
sub_input, val_input, sub_target, val_target = \
train_test_split(train_input, train_target, test_size = 0.2, random_state=42)
sub_input.shape, val_input.shape
dt = DecisionTreeClassifier(random_state=42)
dt.fit(sub_input, sub_target)
print(dt.score(sub_input, sub_target))
print(dt.score(val_input, val_target)) # validation 으로 모델 평가
from sklearn.model_selection import cross_validate
scores = cross_validate(dt, train_input, train_target)
scores
np.mean(scores['test_score']) # 교차검증의 최종점수는 5개의 점수를 평균하여 얻을 수 있다.
from sklearn.model_selection import StratifiedKFold
scores = cross_validate(dt, train_input, train_target, cv = StratifiedKFold())
np.mean(scores['test_score'])
splitter = StratifiedKFold(n_splits=10, shuffle=True, random_state=42)
scores = cross_validate(dt, train_input, train_target, cv = splitter)
np.mean(scores['test_score'])
scores
모델이 학습하기 전에 세팅을 해주는것
from sklearn.model_selection import GridSearchCV
params = {
'min_impurity_decrease' : [0.0001, 0.0002, 0.0003, 0.0004, 0.00005],
}
gs = GridSearchCV(DecisionTreeClassifier(random_state=42), params, n_jobs=-1)
gs.fit(train_input, train_target)
dt = gs.bestestimator
dt
dt.score(train_input, train_target)
gs.bestparams
gs.cvresults
pd.DataFrame(gs.cvresults)
gs.cvresults['mean_test_score']
bestindex = np.argmax(gs.cv_results['mean_test_score'])
best_index
gs.cvresults['params'][best_index]
params = {
'min_impurity_decrease' : np.arange(0.0001, 0.001, 0.0001), # 9개 값
'max_depth' : range(5,20,1), # 15개 값
'min_samples_split' : range(2, 100, 10), #10개의 값.
}
gs = GridSearchCV(DecisionTreeClassifier(random_state=42), params, n_jobs=-1)
gs.fit(train_input, train_target)
gs.bestparams
np.max(gs.cvresults['mean_test_score'])
pd.DataFrame(gs.cvresults).shape
from scipy.stats import uniform, randint
rgen = randint(0,10)
rgen
rgen.rvs(10)
np.unique(rgen.rvs(1000), return_counts = True) # 정말 균등하게 뽑아내나?
ugen = uniform(0, 1) # 확률분포 객체
ugen.rvs(10)
params = {
'min_impurity_decrease' : uniform(0.0001, 0.001), # 0.0001에서 0.001 사이의 실수값 샘플링
'max_depth' : randint(20, 50), # 20 에서 50 사이의 정수값 샘플링.
'min_samples_split' : randint(2, 25), # ...
'min_samples_leaf' : randint(1,25), # 리프 노드가 되기위한 최소 샘플의 개수
# 어떤 노드가 분할하여 만들어질 자식노드의 샘플수가 이 값보다 작을 경우 분할하지 않는다.
}
from sklearn.model_selection import RandomizedSearchCV
gs = RandomizedSearchCV(DecisionTreeClassifier(random_state=42), params,
n_iter = 100, # 샘플링 횟수
n_jobs = -1, random_state=42
)
gs.fit(train_input, train_target)
gs.bestparams
np.max(gs.cvresults['mean_test_score'])
dt = gs.bestestimator # 최적 파라미터로 훈련된 모델
dt.score(test_input, test_target)