Decision Tree¶
아래 데이터는 캐글의 Red Wine Quality 데이터셋의 일부를 발췌한 것이다.
In [1]:
import pandas as pd
wine = pd.read_csv('https://bit.ly/wine_csv_data')
In [2]:
wine.head()
Out[2]:
class = 0 이면 레드와인, 1이면 화이트와인(양성 클래스)
- 판다스 데이터프레임의 유용한 메서드 2가지
- info() : 데이터프레임의 각 열의 데이터 타입과 누락된 데이터가 있는지 확인하는데 유용함
In [3]:
wine.info()
- 총 6497개의 entries가 있는데 Non-Null count가 6497이므로 누락된 값은 없다.
- 다음으로는 describe() 메서드이다.
- 이 메서드는 열에 대한 간략한 통계를 출력해준다.
- 최소, 최대, 평균값 등을 볼 수 있다.
In [4]:
wine.describe()
Out[4]:
- 알코올 도수, 당도, pH 값의 스케일이 다르므로 표준화할 필요가 있다.
In [5]:
#input과 target 분리
data = wine[['alcohol', 'sugar', 'pH']].to_numpy()
target = wine['class'].to_numpy()
In [6]:
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)
In [7]:
print(train_input.shape, test_input.shape)
훈련 세트 : 5197 개 / 테스트 세트 : 1300 개
In [8]:
from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
ss.fit(train_input)
train_scaled = ss.transform(train_input)
test_scaled = ss.transform(test_input)
In [9]:
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))
- 훈련 세트와 테스트 세트의 스코어가 모두 낮은 것을 보아 과소적합됬음을 알 수 있다.
- Regulazation factor인 C값을 바꾸어볼 수 있다.
- solver 매개변수에서 다른 알고리즘을 선택할 수 있다.
- 다항 특성을 만들어 추가할 수 있다.
In [10]:
print(lr.coef_, lr.intercept_)
- 로지스틱 회귀 모델을 사용하여 완벽한 결과를 만들어낼 수 있지만, 그 결과값을 일반적인 대중에게 설명하기는 어렵다.
- 특히 다항 특성을 추가한 경우에는 어떤 조치를 해야할지 덜 명확할 수 있다.
Decision Tree¶
- 결정 트리는 스무고개와 같이 특정한 기준에 따라 데이터들을 분류한다.
- 사이킷런에서는 DecisionTreeClassifier 클래스를 사용할 수 있다.
In [11]:
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))
- 훈련 세트에 대해서는 정확도가 아주 높지만, 테스트 세트에 대해서는 낮은 것으로 보아 overfitting 되었음을 알 수 있다.
In [12]:
import matplotlib.pyplot as plt
from sklearn.tree import plot_tree
plt.figure(figsize=(10,7))
plot_tree(dt)
plt.show()
(이러니 overfitting이 안될 수가 있나 ...)
- 맨 위의 node 를 root node라고 하고 맨 밑의 node를 leaf node라고 한다.
- 이 때 node란 훈련 데이터의 특성에 대한 테스트를 표현하며, branch는 테스트의 결과를 나타낸다.
- 따라서 일반적으로 node는 2개의 branch를 가진다.
In [13]:
plt.figure(figsize=(10,7))
plot_tree(dt, max_depth=1, filled=True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show()
- max_depth 매개변수는 root node를 제외하고 깊이를 얼마나 확장할 것인지를 정하는 변수이다.
- filled 매개변수는 클래스에 맞게 node의 색을 칠할 수 있다.
- 클래스마다 색깔을 지정하고 특정 클래스의 비율이 높으면 색이 진하게 바뀐다.
- feature_names 매개변수는 특성의 이름을 전달할 수 있다.
- 노드에 적혀있는 수치는 위에서부터 차례로 테스트 조건, 불순도, 총 샘플 수, 클래스별 샘플 수를 뜻한다.
- branch에서 왼쪽은 yes, 오른쪽은 no이다.
- 클래스별 샘플 수는 각 node에서 예측한 레드와인(1258)과 화이트와인(3939, 양성 클래스)의 수라고 할 수 있다.
- 여기서 불순도에 gini라고 적혀있는 것은 지니 불순도를 의미한다.
- DecisionTreeClassifer 클래스에서 gini는 criterion 매개변수의 기본값이다.
- 지니 불순도 = 1 - (양성 클래스 비율^2 + 음성 클래스 비율^2)이다.
- 지니 불순도의 경우 양쪽 클래스가 모두 동일하게 존재하는 경우 0.5로 가장 작고, 한쪽에 모두 몰려있는 경우 1.0으로 가장 크다.
- 결정 트리 모델은 부모 노드와 자식 노드의 불순도 차이가 가능한 크도록 트리를 성장시킨다.
- 부모 노드와 자식 노드의 불순도 차이는 아래와 같이 계산한다:
부모의 불순도 - (왼쪽 노드 샘플 수 / 부모의 샘플 수 ) * 왼쪽 노드 불순도 - (오른쪽 노드 샘플 수 / 부모의 샘플 수 ) * 오른쪽 노드 불순도
- 위와 같이 계산한 부모 노드와 자식 노드 사이의 불순도 차이를
정보 이득(information gain)
이라고 한다.
- 한편 criterion 매개변수에 gini가 아니라 entropy를 사용할 수 있는데, 이 경우는 아래와 같이 계산한다:
-음성 클래스 비율 * log_2(음성 클래스 비율) - 양성 클래스 비율 * log_2(양성 클래스 비율)
- 이 때 minus sign을 붙이는 이유는 log 안의 값이 1보다 작으므로 음수가 나오기 때문이다.
Branching¶
In [14]:
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))
In [15]:
plt.figure(figsize=(15,10))
plot_tree(dt, filled=True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show()
- 위 Tree를 보면 깊이 1의 node는 당도를 기준으로,
- 깊이 2의 node에서 왼쪽은 도수를 기준으로, 오른쪽은 pH를 기준으로 분류를 했음을 알 수 있다.
- feature 별로 scale이 다른데 표준화를 해야할까?
- 불순도 계산시에는 어차피 비율로써 들어가기 때문에 표준화를 할 필요가 없다.
- 이것이 DT의 또 다른 장점 중 하나이다.
In [16]:
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))
전처리하기 전 데이터와 완벽히 정확도가 같음을 알 수 있다.
In [17]:
plt.figure(figsize=(20,15))
plot_tree(dt, filled=True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show()
- 역시 전처리 하기 전과 결과적으로 같은 그래프가 나온다.
- 그러나 데이터를 전처리하지 않아 직관적인 해석이 가능하다.
- 위에서 ss를 거친 경우 당도가 음수였는데, 이 경우는 기존 값을 사용하기 때문이다.
In [18]:
print(dt.feature_importances_)
- DT의 featureimportances 속성에는 특성의 중요도가 저장되어 있다.
- 이 경우 두번째 특성인 당도의 중요도가 87%정도로 높은 것을 알 수 있다.
- 그러므로 깊이 1인 노드에서 당도를 기준으로 트리를 나눈 것이다.
- 특성 중요도는 각 노드의 정보 이득과 전체 샘플에 대한 비율을 곱한 후 특성별로 더하여 계산한다.
- 특성 중요도를 활용하여 결정 트리 모델을 특성 선택에 활용할 수 있다. 이것이 결정 트리 알고리즘의 또 다른 장점 중 하나이다.
- 결정 트리는 많은 앙상블 학습 알고리즘의 기반이 된다.
- 앙상블 학습은 신경망과 함께 가장 높은 성능을 내기 때문에 인기가 높은 알고리즘이다.
확인 문제¶
In [19]:
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))
- min_impurity_decrease 매개변수 :
어떤 노드의 정보 이득 * 노드의 샘플 수 /. 전체 샘플 수
값이 매개변수 값보다 작으면 더 이상 분할하지 않는다.
- 성능은 depth를 3으로 설정했을 때보다 높고, 처음 했던 것처럼 depth 제한이 없는 경우처럼 과대적합하지도 않다.
In [20]:
plt.figure(figsize=(20,15), dpi=300)
plot_tree(dt, filled=True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show()
- Tree가 비대칭적으로 생겼음을 알 수 있다.
In [21]:
from IPython.core.display import display, HTML
display(HTML("<style>.container { width:90% !important; }</style>"))
'혼자 공부하는 머신러닝 + 딥러닝' 카테고리의 다른 글
[혼공머신] 11. Ensemble Learning (0) | 2021.12.18 |
---|---|
[혼공머신] 10. Cross Validation & Grid Search (0) | 2021.12.18 |
[혼공머신] 8. Stochastic Gradient Descent (0) | 2021.12.18 |
[혼공머신] 7. Logistic Regression (0) | 2021.12.17 |
[혼공머신] 6. Feature Engineering & Regularization (0) | 2021.12.17 |