if-then-else
규칙의 집합체라고 할 수 있음 → 이해와 구현이 쉽다sklearn.tree.DecisionTreeClassifier
predictors = ['borrower_score', 'payment_inc_ratio']
outcome = 'outcome'
X = loan3000[predictors]
y = loan3000[outcome]
loan_tree = DecisionTreeClassifier(random_state=1, criterion='entropy',
min_impurity_decrease=0.003)
loan_tree.fit(X, y)
plotDecisionTree(loan_tree, feature_names=predictors, class_names=loan_tree.classes_)
노드가 참이면 왼쪽, 거짓이면 오른쪽으로 움직이면서 결정된다.
→이걸 text로 나타내려면 `textDecisionTree()`를 사용
```jsx
print(textDecisionTree(loan_tree))
```
```jsx
node=0 test node: go to node 1 if 0 <= 0.5750000178813934 else to node 6
node=1 test node: go to node 2 if 0 <= 0.32500000298023224 else to node 3
node=2 leaf node: [[0.785, 0.215]]
node=3 test node: go to node 4 if 1 <= 10.42264986038208 else to node 5
node=4 leaf node: [[0.488, 0.512]]
node=5 leaf node: [[0.613, 0.387]]
node=6 test node: go to node 7 if 1 <= 9.19082498550415 else to node 10
node=7 test node: go to node 8 if 0 <= 0.7249999940395355 else to node 9
node=8 leaf node: [[0.247, 0.753]]
node=9 leaf node: [[0.073, 0.927]]
node=10 leaf node: [[0.457, 0.543]]
```
마지막 분할 영역에 해당하는 출력이 최대한 비슷한 결과를 보이도록 데이터를 반복적으로 분할하는 것
분할 영역 A와 P개의 예측변수 집합 에 대해
각 예측변수 에 대해 에 해당하는 어떤 변수(s)를 넣어 A를 ≥s, <s인 두 집합으로 나눈다.
분할 영역 안에서 동질성을 측정한다.
s를 바꿔가며 위 과정을 반복하고, 동질성이 가장 큰 와 를 선택한다.
과 에 대해 위 과정을 반복하며 계속해서 분할한다.
충분한 분할(더이상 동질성이 개선되지 않음)을 진행했을 때, 알고리즘을 종료한다.
fig, ax = plt.subplots(figsize=(6, 4))
loan3000.loc[loan3000.outcome=='paid off'].plot(
x='borrower_score', y='payment_inc_ratio', style='.',
markerfacecolor='none', markeredgecolor='C1', ax=ax)
loan3000.loc[loan3000.outcome=='default'].plot(
x='borrower_score', y='payment_inc_ratio', style='o',
markerfacecolor='none', markeredgecolor='C0', ax=ax)
ax.legend(['paid off', 'default']);
ax.set_xlim(0, 1)
ax.set_ylim(0, 25)
ax.set_xlabel('borrower_score')
ax.set_ylabel('payment_inc_ratio')
x0 = 0.575
x1a = 0.325; y1b = 9.191
y2a = 10.423; x2b = 0.725
ax.plot((x0, x0), (0, 25), color='grey')
ax.plot((x1a, x1a), (0, 25), color='grey')
ax.plot((x0, 1), (y1b, y1b), color='grey')
ax.plot((x1a, x0), (y2a, y2a), color='grey')
ax.plot((x2b, x2b), (0, y1b), color='grey')
labels = [('default', (x1a / 2, 25 / 2)),
('default', ((x0 + x1a) / 2, (25 + y2a) / 2)),
('paid off', ((x0 + x1a) / 2, y2a / 2)),
('paid off', ((1 + x0) / 2, (y1b + 25) / 2)),
('paid off', ((1 + x2b) / 2, (y1b + 0) / 2)),
('paid off', ((x0 + x2b) / 2, (y1b + 0) / 2)),
]
for label, (x, y) in labels:
ax.text(x, y, label, bbox={'facecolor':'white'},
verticalalignment='center', horizontalalignment='center')
plt.tight_layout()
plt.show()
위에서 말한 ‘동질성(클래스 순도)’을 측정하는 방법이 필요함
동질성 최대화 = 불순도 최소화
지니불순도와 엔트로피가 대표적인 불순도 측정 지표
DecisionTreeClassifier
은 gini를 기본값으로 분할 기준을 정함
지니불순도 (≠ 지니계수 : 이진 분류 문제에 한정됨)
EX) 100개의 샘플이 있는 어떤 노드의 두 클래스 비율이 0.5씩이라면 지니 불순도는 0.5가 됨 ⇒ 최악
노드에 하나의 클래스만 있다면 지니 불순도는 0이 됨 ⇒ 완벽히 분류된 것
엔트로피
두 값 모두 0에 가까울수록 좋음
각 분할로 만들어지는 영역에 대해 불순도를 측정하고 가중 평균을 계산한 후 단계마다 그 값이 가장 낮은 영역을 선택
def entropyFunction(x):
if x == 0: return 0
return -x * math.log(x, 2) - (1 - x) * math.log(1 - x, 2)
def giniFunction(x):
return x * (1 - x)
x = np.linspace(0, 0.5, 50)
impure = pd.DataFrame({
'x': x,
'Accuracy': 2 * x,
'Gini': [giniFunction(xi) / giniFunction(.5) for xi in x],
'Entropy': [entropyFunction(xi) for xi in x],
})
fig, ax = plt.subplots(figsize=(4, 4))
impure.plot(x='x', y='Accuracy', ax=ax, linestyle='solid')
impure.plot(x='x', y='Entropy', ax=ax, linestyle='--')
impure.plot(x='x', y='Gini', ax=ax, linestyle=':')
plt.tight_layout()
plt.show()
정확도가 높은 부분에서 지니 불순도와 엔트로피 측정값이 비슷하게 높아짐
트리가 커질수록 점점 아주 작게 영역이 분할되는데 이는 세부 규칙이 만들어 지는 것
규칙이 세분화되면 학습 데이터에 대해 100% 정확도를 갖게 되는데 이는 학습 데이터의 노이즈까지 학습된 결과로 ‘과적합’된 것
새로운 데이터에 대해서도 좋은 성능을 갖기 위해서는 트리 성장을 멈춰야 함
파이썬에서 멈추는 방법
min_samples_split
(기본값 2)과 min_samples_leaf
(기본값 1)사용 최소 분할 영역 크기나 말단 잎 크기를 조절
→ 하위 영역 또는 말단 잎 크기가 너무 작다면 분할을 멈춤
min_impurity_decrease
를 사용해 불순도 감소값에 따라 분할을 제한함
→ min_impurity_decrease
값이 작을수록 트리는 복잡해짐
max_depth
를 이용
but 예측 정확도를 최대화 하기 위한 최적값을 결정하기 매우 어렵기 때문에
(교차검증으로 파라미터 변경 + 가지치기로 트리 수정) 결합한 방법을 이용하는 것이 좋음
트리 모델을 이용해 회귀분석을 하는 방법 또한 위에서 소개한 것과 동일한 과정을 거친다.
차이점
트리 장점