데이터의 민족

< 핸즈온 머신러닝 - 결정 트리 > 본문

Machine Learning의 민족/핸즈온 머신러닝

< 핸즈온 머신러닝 - 결정 트리 >

댕구리댕댕구리 2022. 6. 3. 18:11
728x90
반응형
SMALL
 

Chapter_6 결정 트리.ipynb

Colaboratory notebook

colab.research.google.com


  • 분류, 회귀, 다중 출력 작업 가능
  • 매우 복잡한 데이터셋 학습 가능
  • 랜덤 포레스트의 기본 요소

 

1. 결정트리 학습과 시각화

from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier

iris = load_iris()

X = iris.data[:, 2:]
y = iris.target

tree_clf = DecisionTreeClassifier(max_depth = 2, random_state = 42)
tree_clf.fit(X, y)
from graphviz import Source
from sklearn.tree import export_graphviz

export_graphviz(
        tree_clf,
        out_file=os.path.join(IMAGES_PATH, "iris_tree.dot"),
        feature_names=iris.feature_names[2:],
        class_names=iris.target_names,
        rounded=True,
        filled=True
    )

Source.from_file(os.path.join(IMAGES_PATH, "iris_tree.dot"))

 

2. 예측하기

  • 분류에서 루트 노드(깊이가 0인 맨 꼭대기의 노드)에서 시작
  • 기준에 맞게 분류 진행
  • 노드의 gini 속성은 불순도
    • 한 노드의 샘플이 같은 클래스에 속해 있다면 순수 노드(gini = 0)

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import ListedColormap

def plot_decision_boundary(clf, X, y, axes=[0, 7.5, 0, 3], iris=True, legend=False, plot_training=True):
    x1s = np.linspace(axes[0], axes[1], 100)
    x2s = np.linspace(axes[2], axes[3], 100)
    x1, x2 = np.meshgrid(x1s, x2s)
    X_new = np.c_[x1.ravel(), x2.ravel()]
    y_pred = clf.predict(X_new).reshape(x1.shape)
    custom_cmap = ListedColormap(['#fafab0','#9898ff','#a0faa0'])
    plt.contourf(x1, x2, y_pred, alpha=0.3, cmap=custom_cmap)
    if not iris:
        custom_cmap2 = ListedColormap(['#7d7d58','#4c4c7f','#507d50'])
        plt.contour(x1, x2, y_pred, cmap=custom_cmap2, alpha=0.8)
    if plot_training:
        plt.plot(X[:, 0][y==0], X[:, 1][y==0], "yo", label="Iris setosa")
        plt.plot(X[:, 0][y==1], X[:, 1][y==1], "bs", label="Iris versicolor")
        plt.plot(X[:, 0][y==2], X[:, 1][y==2], "g^", label="Iris virginica")
        plt.axis(axes)
    if iris:
        plt.xlabel("Petal length", fontsize=14)
        plt.ylabel("Petal width", fontsize=14)
    else:
        plt.xlabel(r"$x_1$", fontsize=18)
        plt.ylabel(r"$x_2$", fontsize=18, rotation=0)
    if legend:
        plt.legend(loc="lower right", fontsize=14)

plt.figure(figsize=(8, 4))
plot_decision_boundary(tree_clf, X, y)
plt.plot([2.45, 2.45], [0, 3], "k-", linewidth=2)
plt.plot([2.45, 7.5], [1.75, 1.75], "k--", linewidth=2)
plt.plot([4.95, 4.95], [0, 1.75], "k:", linewidth=2)
plt.plot([4.85, 4.85], [1.75, 3], "k:", linewidth=2)
plt.text(1.40, 1.0, "Depth=0", fontsize=15)
plt.text(3.2, 1.80, "Depth=1", fontsize=13)
plt.text(4.05, 0.5, "(Depth=2)", fontsize=11)


plt.show()

  • 굵은 수직선이 루트 노즈(깊이 0)의 결정 경계(꽃잎 길이 = 2.45)를 나타냄
  • 왼쪽: 순수 노드의 영역
  • 오른쪽: 순수 노드의 영역이 아니기에 꽃잎 너비 1.75에서 나누어짐
  • max_depth = 2이기에 추가 분할 없음

 

2 - 1. 블랙 박스와 화이트 박스

  • 화이트 박스: 결정 트리와 같이 직곽적이고 결정 방식의 이해가 쉬운 모델
  • 블랙박스: 랜덤 포레스트나 신경망 같이 알고리즘의 성능이 뛰어나고 예측을 만드는 연산 과정이 쉽게 확인 가능
    • But) 설명이 어려움

 

3. 클래스 확률의 추정

  • 예시) 깊이가 5, 너비가 1.5인 꽃잎
tree_clf.predict_proba([[5, 1.5]]), tree_clf.predict([[5, 1.5]])

 

4. CART 훈련 알고리즘

  • Decision Tree 성장 위해 사용
  • 탐욕적 알고리즘으로, 맨 위 루트 노드에서 최적의 분할을 찾으며 이어지는 각 단계에서 이 과정을 반복
    • But) 현재 단계의 분할이 몇 단계를 거쳐 가장 낮은 불순도로 이어질 수 있는지에 대한 고려 없음 + 최적의 솔루션 보장 없음
  • 탐욕적 알고리즘은 납득할 만한 좋은 솔루션 정도

 

5. 계산 복잡도

  • 결정 트리를 탐색하기 위해서는 O(log2(m))개의 노드를 거쳐야함
  • 하나의 특성값만 예측시 확인하기 때문에 특성 수와 무관하며, 큰 훈련 세트를 다룰 때도 예측 속도가 매우 빠름
  • 훈련 세트가 수천 개 이하일 경우 사이킷런은 presort = True로 지정하면 훈련 속도를 높일 수 있음

 

6. 지니 불순도 또는 엔트로피?

  • default는 지니 지수이지만, criterion 매개변수를 'entropy'로 지정하면 엔트로피 불순도를 사용
  • 엔트로피는 분자의 무질서함을 측정하는 것으로 원래 열역학의 개념, 분자가 안정되고 질서 정연하면 엔트로피가 0에 가까움
  • 지니와 엔트로피의 큰 차이는 없음, 지니 분순도가 좀 더 빠른정도
  • 가장 빈도가 높은 클래스를 한쪽 가지로 고립시키는 경향이 있는 지니 불순도에 반해, 엔트로피는 좀 더 균형 잡힌 트리를 생성

 

7. 규제 매개변수

  • 결정 트리는 훈련 데이터에 대한 제약 사항이 거의 없음
  • 결정 트리는 파라미터가 보통 많지만, 수가 결정되지 않기 때문에 이런 모델을 비파라미터 모델이라고 지칭
    • But) 결정 트리의 과적합을 막기 위해 자유도에 대한 제한 필요 있음
  • 사이킷런에서 결정 트리에 대한 규제변수는 max_depth
  • min_으로 시작하는 매개변수를 증가시키거나 max_로 시작하는 매개변수를 감소시키면 모델에 대한 규제가 커짐
    • min_samples_split (분할되기 위해 노드가 가져야 하는 최소 샘플 수)
    • min_samples_leaf (리프 노드가 가지고 있어야 할 최소 샘플 수)
    • min_weight_fraction_leaf(min_samples_leaf와 같지만 가중치가 부여된 전체 샘플 수에서의 비율)
    • max_leaf_nodes(리프 노드의 최대 수)
    • max_features (각 노드에서 분할에 사용할 특성의 최대 수)
# 오른쪽 결정 트리는 min_samples_leaf = 4로 지정 훈련
from sklearn.datasets import make_moons
Xm, ym = make_moons(n_samples=100, noise=0.25, random_state=53)

deep_tree_clf1 = DecisionTreeClassifier(random_state=42)
deep_tree_clf2 = DecisionTreeClassifier(min_samples_leaf=4, random_state=42)
deep_tree_clf1.fit(Xm, ym)
deep_tree_clf2.fit(Xm, ym)

fig, axes = plt.subplots(ncols=2, figsize=(10, 4), sharey=True)
plt.sca(axes[0])
plot_decision_boundary(deep_tree_clf1, Xm, ym, axes=[-1.5, 2.4, -1, 1.5], iris=False)
plt.title("No restrictions", fontsize=16)
plt.sca(axes[1])
plot_decision_boundary(deep_tree_clf2, Xm, ym, axes=[-1.5, 2.4, -1, 1.5], iris=False)
plt.title("min_samples_leaf = {}".format(deep_tree_clf2.min_samples_leaf), fontsize=14)
plt.ylabel("")


plt.show()

 

8. 회귀

# 2차식으로 만든 데이터셋 + 잡음
np.random.seed(42)
m = 200
X = np.random.rand(m, 1)
y = 4 * (X - 0.5) ** 2
y = y + np.random.randn(m, 1) / 10
from sklearn.tree import DecisionTreeRegressor

tree_reg = DecisionTreeRegressor(max_depth = 2)
tree_reg.fit(X, y)
  • 만약 x1 = 0.6인 타깃값을 예측한다고 가정
  • 루트 노드부터 시작, 트리를 순회하면 value = 0.111인 리프 노드에 도달
  • 이 리프 노드에 있는 110개 훈련 샘플의 평균 타깃값이 예측값
  • 110개 샘플에 대한 MSE(평균제곱오차)를 계산하면 0.015

 

from sklearn.tree import DecisionTreeRegressor

tree_reg1 = DecisionTreeRegressor(random_state=42, max_depth=2)
tree_reg2 = DecisionTreeRegressor(random_state=42, max_depth=3)
tree_reg1.fit(X, y)
tree_reg2.fit(X, y)

def plot_regression_predictions(tree_reg, X, y, axes=[0, 1, -0.2, 1], ylabel="$y$"):
    x1 = np.linspace(axes[0], axes[1], 500).reshape(-1, 1)
    y_pred = tree_reg.predict(x1)
    plt.axis(axes)
    plt.xlabel("$x_1$", fontsize=18)
    if ylabel:
        plt.ylabel(ylabel, fontsize=18, rotation=0)
    plt.plot(X, y, "b.")
    plt.plot(x1, y_pred, "r.-", linewidth=2, label=r"$\hat{y}$")

fig, axes = plt.subplots(ncols=2, figsize=(10, 4), sharey=True)
plt.sca(axes[0])
plot_regression_predictions(tree_reg1, X, y)
for split, style in ((0.1973, "k-"), (0.0917, "k--"), (0.7718, "k--")):
    plt.plot([split, split], [-0.2, 1], style, linewidth=2)
plt.text(0.21, 0.65, "Depth=0", fontsize=15)
plt.text(0.01, 0.2, "Depth=1", fontsize=13)
plt.text(0.65, 0.8, "Depth=1", fontsize=13)
plt.legend(loc="upper center", fontsize=18)
plt.title("max_depth=2", fontsize=14)

plt.sca(axes[1])
plot_regression_predictions(tree_reg2, X, y, ylabel=None)
for split, style in ((0.1973, "k-"), (0.0917, "k--"), (0.7718, "k--")):
    plt.plot([split, split], [-0.2, 1], style, linewidth=2)
for split in (0.0458, 0.1298, 0.2873, 0.9040):
    plt.plot([split, split], [-0.2, 1], "k:", linewidth=1)
plt.text(0.3, 0.5, "Depth=2", fontsize=13)
plt.title("max_depth=3", fontsize=14)


plt.show()

tree_reg1 = DecisionTreeRegressor(random_state=42)
tree_reg2 = DecisionTreeRegressor(random_state=42, min_samples_leaf=10)
tree_reg1.fit(X, y)
tree_reg2.fit(X, y)

x1 = np.linspace(0, 1, 500).reshape(-1, 1)
y_pred1 = tree_reg1.predict(x1)
y_pred2 = tree_reg2.predict(x1)

fig, axes = plt.subplots(ncols=2, figsize=(10, 4), sharey=True)

plt.sca(axes[0])
plt.plot(X, y, "b.")
plt.plot(x1, y_pred1, "r.-", linewidth=2, label=r"$\hat{y}$")
plt.axis([0, 1, -0.2, 1.1])
plt.xlabel("$x_1$", fontsize=18)
plt.ylabel("$y$", fontsize=18, rotation=0)
plt.legend(loc="upper center", fontsize=18)
plt.title("No restrictions", fontsize=14)

plt.sca(axes[1])
plt.plot(X, y, "b.")
plt.plot(x1, y_pred2, "r.-", linewidth=2, label=r"$\hat{y}$")
plt.axis([0, 1, -0.2, 1.1])
plt.xlabel("$x_1$", fontsize=18)
plt.title("min_samples_leaf={}".format(tree_reg2.min_samples_leaf), fontsize=14)

plt.show()

 

9. 불안정성

  • 계단 모양의 결정 경계 생성(모든 분할은 축에 수직)
    • So) 훈련 세트의 회전에 민감
np.random.seed(6)
Xs = np.random.rand(100, 2) - 0.5
ys = (Xs[:, 0] > 0).astype(np.float32) * 2

angle = np.pi / 4
rotation_matrix = np.array([[np.cos(angle), -np.sin(angle)], [np.sin(angle), np.cos(angle)]])
Xsr = Xs.dot(rotation_matrix)

tree_clf_s = DecisionTreeClassifier(random_state=42)
tree_clf_s.fit(Xs, ys)
tree_clf_sr = DecisionTreeClassifier(random_state=42)
tree_clf_sr.fit(Xsr, ys)

fig, axes = plt.subplots(ncols=2, figsize=(10, 4), sharey=True)
plt.sca(axes[0])
plot_decision_boundary(tree_clf_s, Xs, ys, axes=[-0.7, 0.7, -0.7, 0.7], iris=False)
plt.sca(axes[1])
plot_decision_boundary(tree_clf_sr, Xsr, ys, axes=[-0.7, 0.7, -0.7, 0.7], iris=False)
plt.ylabel("")

plt.show()

  • 왼쪽 결정 트리는 간단한 선형 분류
  • 오른쪽 결정 트리는 45도를 회전한 데이터셋을 사용
    • 더 좋은 방향으로 회전 시키는 방법: PCA(주성분분석)

참고 교재

 

핸즈온 머신러닝

머신러닝 전문가로 이끄는 최고의 실전 지침서 텐서플로 2.0을 반영한 풀컬러 개정판 『핸즈온 머신러닝』은 지능형 시스템을 구축하려면 반드시 알아야 할 머신러닝, 딥러닝 분야 핵심 개념과

book.naver.com

728x90
반응형
LIST
Comments