로지스틱 회귀 모델을 적용하여 이진 분류를 해보자.
import pandas as pd
wine = pd.read_csv("https://bit,ly/wine_csv_data")
wine.head()
데이터를 보면 첫 번째 열부터 차례대로 도수, 당도, pH를 나타내며, 마지막 열인 class는 타깃값이 0이면 레드와인, 1이면 화이트 와인이라고 한다.
wine.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 6497 entries, 0 to 6496
Data columns (total 4 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 alcohol 6497 non-null float64
1 sugar 6497 non-null float64
2 pH 6497 non-null float64
3 class 6497 non-null float64
dtypes: float64(4)
memory usage: 203.2 KB
describe() 메소드를 이용하여 간략한 통계를 출력해보자.
wine.describe()
alcohol sugar pH class
count 6497.000000 6497.000000 6497.000000 6497.000000
mean 10.491801 5.443235 3.218501 0.753886
std 1.192712 4.757804 0.160787 0.430779
min 8.000000 0.600000 2.720000 0.000000
25% 9.500000 1.800000 3.110000 1.000000
50% 10.300000 3.000000 3.210000 1.000000
75% 11.300000 8.100000 3.320000 1.000000
max 14.900000 65.800000 4.010000 1.000000
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)
훈련 세트와 테스트 세트의 크기를 확인해 보자.
print(train_input.shape, test_input.shape)
(5197, 3) (1300, 3)
이제 표준화를 해보자.
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))
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))
0.7808350971714451
0.7776923076923077
낮은 score() + 학습원리 설명하기 어려움
--결정 트리 이용
결정 트리 모델
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
점수가 높아졌다. !
이 트리를 시각화해보자. by using plot tree()
import matplotlib.pyplot as plt
from sklearn.tree import plot_tree
plt.figure(figsize=(10,7))
plt_tree(dt)
plt.show()
노드의 길이가 너무 길어서 제한해보자 by using max_depth = 1 하면 하나의 노드를 더 그림
plt.figure(figsize=(10,7))
plot_tree(dt, max_depth = 1 , filled = True, feature_names = ['alcohol','sugar','pH'])
plt.show()
그림이 담고 있는 정보는 조건 (sugar), 불순도(gini), 샘플 수, 클래스 별 샘플 수로 이루어져 있다.
루트 노드는 당도가 -0.239이하인지...yes <--- / No --->
불순도
- 해당 범주 안에 서로 다른 데이터가 얼마나 섞여 있는지를 뜻함.
gini는 지니 불순도를 의미하는데,
<<<<<클래스개수에 따른 케이스들의 불순한 정도를 나타내는 척도라고 생각하면 될 것 같다.
지니불순도가 필요한 이유는 의사결정을 하는데 있어서 최적의 분류를 위한 결정을 계속해서 맞이하는데 이 결정에 사용되기 때문이다. 이 변수로 인해서 분류를 거쳤을 때 지니불순도가 얼마나 되는가? 를 생각할 수 있게된다. 또다른 불순도지표로는 엔트로피가 있다.>>>>>>
DecisionTreeClassifier 클래스의 criterion 매개변수의 기본값이 gini이다.
지니 불순도(gini) = 1 - ( 음성 클래스 비율^2 + 양성 클래스 비율^2 )
결정 트리 모델은 부모와 자식 노드의 불순도 차이가 커지도록 트리를 성장시킨다.
(자식 노드의 불순도를 샘플 개수에 비례하여 모두 더하고 그 이후 부모 노드의 불순도에서 뺀다.)
=부모 gini - (왼쪽 샘플 수 / 부모 샘플 수) x 왼쪽 gini - (오른쪽 샘플 수 / 부모 샘플 수) x 오른쪽 gini
= 0.367 - [(2922/5197) x 0.481] - [(2275/5197) x 0.069] = 0.066
부모와 자식 노드간의 불순도 차이를 정보 이득이라고 한다.
따라서 정보 이득이 최대가 되도록 데이터를 나누고, 그 기준으로 불순도 (gini)를 사용한다.
한편, 사이킷런에서는 또 다른 불순도 기준이 존재한다. ( 엔트로피 불순도)
DeicisionTreeClassifier 클래스에서 criterion='entropy'
엔트로피 불순도 기준은 아래에 log 2를 붙여서 사용한다.
가지치기
끝도 없이 자라나는 트리가 생길 수 있기 때문에 가지치기는 필수적이다.
가장 간단한 방법은 아까 했던 것처럼 max_depth = 으로 지정할 수 있다.
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()
위 그림을 보면 깊이 0~3 중 깊이 1은 당도를 기준, 2는 당도,도수,pH 순으로 기준을 사용했다.
최종 노드를 보면 왼쪽에서 세번째 노드만 음성클래스가 더 많은것을 볼 수 있다.
따라서 이 노드 (주황)에 도착해야만 레드와인으로 분류하는것 같다.
즉, 당도는 -0.802~-0.239 사이, 도수는 0.454보다 작아야 레드와인으로 분류한다.
그런데 당도가 음수라는것을 어떻게 해석해야할까.( 문제는 전처리)
결정 트리는 표준화 전처리 과정을 하지 않아도 된다!!!
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
트리를 그려보자 (no 전처리)
plt.figure(figsize=(20,15))
plot_tree(dt, filled=True, feature_names=['alcohol','sugar','pH'])
plt.show()
이제 가시적으로 해석하기 더 쉽다.
마지막으로, 결정 트리에는 어떤 특성이 가장 유용한지 나타내는 특성 중요도를 계산해 줍니다. 이 트리의 루트 노드와 깊이1에서 당도를 사용했기 때문에 당도가 가장 유용한 특성일 것 같다.
메서드는 DecisionTreeClassifier.feature_importances_
print(dt.feature_importances_)
[0.12345626 0.86862934 0.0079144 ]
'머신러닝 & 딥러닝 기초 > 5장 - 트리 알고리즘' 카테고리의 다른 글
5 - 3 : 트리의 앙상블 (0) | 2023.01.17 |
---|---|
5 - 2 교차 검증과 그리드 서치 (0) | 2023.01.10 |