결정 트리
[결정 트리 개념]
결정 트리(decision tree)는 분류와 회귀 작업 그리고 다중 출력 작업까지 가능한 다목적 머신러닝 알고리즘이며, 매우 복잡한 데이터셋도 학습할 수 있는 강력한 알고리즘이다.
결정 트리는 최근에 자주 사용되는 강력한 머신러닝 알고리즘인 랜덤 포레스트의 기본 구성 요소이기도 하다.
결정 트리는 export_graphviz() 함수를 사용해 그래프 정의를 .dot 파일로 출력하여 훈련된 결정 트리를 시각화할 수 있다.
그리고나서 graphviz.Source.from_file()을 사용해 주피터 노트북에 파일을 로드하고 표시할 수 있다.
Source.from_file('iris_tree.dot')
graphviz는 오픈 소스 그래프 시각화 소프트웨어 패키지이며, .dot 파일을 PDF 또는 PNG와 가튼 다양한 형식으로 변환하는 dot 명령줄 도구가 포함되어 있다.
결정 트리의 결정 경계
아래 코드는 결정 트리의 결정 경계를 시각화해주는 코드이다.
굵은 수직선이 루트 노드(깊이 0)의 결정 경계 (꽃잎 길이=2.45cm)를 나타낸다.
- 왼쪽 영역: 순수 노드(Iris-Setosa만 있음)이기 때문에 더 나눌 수 없음.
- 오른쪽 영역: 순수 노드가 아니므로 깊이 1의 오른쪽 노드는 꽃잎 너비=1.75cm에서 나누어진다.
max_depth를 2로 설정 했으므로 결정 트리는 더 분할되지 않았으나, max_depth3를 3으로 하면 깊의 2의 두 노드가 각각 결정 경계를 추가로 만든다.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
custom_cmap = ListedColormap(['#fafab0', '#9898ff', '#a0faa0'])
plt.figure(figsize=(8, 4))
lengths, widths = np.meshgrid(np.linspace(0, 7.2, 100), np.linspace(0, 3, 100))
X_iris_all = np.c_[lengths.ravel(), widths.ravel()]
y_pred = tree_clf.predict(X_iris_all).reshape(lengths.shape)
plt.contourf(lengths, widths, y_pred, alpha=0.3, cmap=custom_cmap)
for idx, (name, style) in enumerate(zip(iris.target_names, ("yo", "bs", "g^"))):
plt.plot(X_iris[:, 0][y_iris == idx], X_iris[:, 1][y_iris == idx],
style, label=f"Iris {name}")
tree_clf_deeper = DecisionTreeClassifier(max_depth=3, random_state=42)
tree_clf_deeper.fit(X_iris, y_iris)
th0, th1, th2a, th2b = tree_clf_deeper.tree_.threshold[[0, 2, 3, 6]]
plt.xlabel("Petal length (cm)")
plt.ylabel("Petal width (cm)")
plt.plot([th0, th0], [0, 3], "k-", linewidth=2)
plt.plot([th0, 7.2], [th1, th1], "k--", linewidth=2)
plt.plot([th2a, th2a], [0, th1], "k:", linewidth=2)
plt.plot([th2b, th2b], [th1, 3], "k:", linewidth=2)
plt.text(th0 - 0.05, 1.0, "Depth=0", horizontalalignment="right", fontsize=15)
plt.text(3.2, th1 + 0.02, "Depth=1", verticalalignment="bottom", fontsize=13)
plt.text(th2a + 0.05, 0.5, "(Depth=2)", fontsize=11)
plt.axis([0, 7.2, 0, 3])
plt.legend()
plt.show()
결정 트리 클래스 확률 추정
결정 트리는 한 샘플이 특정 클래스 k에 속할 확률을 추정할 수도 있다. 먼저 이 샘플의 리프 노드를 찾기 위해 트리를 탐색하고 그 노드에 있는 클래스 k의 훈련 샘플의 비율을 반환한다.
예를 들어 길이가 5cm 이고, 너비가 1.5cm인 꽃잎을 발견했다고 가정했을 경우, 이에 해당하는 리프 노드는 깊이 2에서 왼쪽 노드이므로 결정 트리는 그에 해당하는 확률을 출력한다.
# 클래스 확률 추정
# 길이가 5이고 너비가 1.5인 꽃잎
tree_clf.predict_proba([[5, 1.5]]).round(3)
▶ Iris-Setosa: 0%, Iris-Versicolor: 90.7%, Iris-Virginica: 9.3%
만약 클래스를 하나 예측한다면 가장 높은 확률을 가진 Iris-Versicolor[1]를 출력할 것이다.
tree_clf.predict([[5, 1.5]])
CART 훈련 알고리즘
사이킷런은 결정 트리를 훈련시키기 위해 CART 알고리즘을 사용한다.
먼저 훈련 세트를 하나의 특성의 임곗값을 사용해 두 개의 서브셋으로 나누고, 크기에 따른 가중치가 적용된 가장 순수한 서브셋으로 나눌 수 있는 (특성, 임곗값) 쌍을 찾는다.
CART 알고리즘이 훈련 세트를 성공적으로 둘로 나누었다면 같은 방식으로 서브셋을 또 나누고 그다음엔 서브셋의 서브셋을 나누고 이런 식으로 계속 반복한다.
이 과정은 max_depth 매개변수로 정의된 최대 깊이가 되거나 불순도를 줄이는 분할을 찾을 수 없을 때 멈추게 된다.
이 외에도 다른 몇 개의 매개변수도 중지 조건에 관여한다.
- min_samples_split
- min_samples_leaf
- min_weight_fraction_leaf
- max_leaf_nodes
CART 알고리즘은 탐욕 알고리즘인데, 맨 위 루트 노드에서 최적의 분할을 찾으며 이어지는 각 단계에서 이 과정을 반복한다. 현재 단계의 분할이 몇 단계를 거쳐 가장 낮은 불순도로 이어질 수 있을지 없을지느 고려하지 않는다. 탐욕 알고리즘은 종종 납득할 만한 훌륭한 솔루션을 만들어내나, 최적의 솔루션을 보장하지는 않는다.
그러므로 결정 트리를 훈련할 때 납득할 만한 좋은 솔루션에 만족해야 한다.
규제 매개변수
결정 트리는 훈련 데이터에 대한 제약 사항이 거의 없어 제한을 두지 않으면 트리가 훈련 데이터에 아주 가깝게 맞추려고 해서 과대적합되기 쉽다. 결정 트리는 모델 파라미터가 전혀 없는 것이 아니라 훈련되기 전에 파라미터 수가 결정되지 않기 때문에 비파라미터 모델(nonpararmetric model) 이라고 한다. 그래서 모델 구조가 데이터에 맞춰져서 고정되지 않고 자유롭다.
선형 모델 같은 파라미터 모델은 모델 파라미터 수가 미리 정해져 있으므로 자유도가 제한되어 과대적합될 위험이 줄어드나, 과소적합될 위험은 커진다.
훈련 데이터에 대한 과대적합을 피하기 위해 학습할 때 결정 트리의 자유도를 제한하는 것을 규제라고 하며, 규제 매개변수는 사용하는 알고리즘에 따라 다르지만 보통 적어도 결정 트리의 최대 깊이는 제어할 수 있다.
사이킷런에서의 max_depth 매개변수로 이를 조절하며 max_depth를 줄이면 모델을 규제하게 되고 과대적합의 위험이 감소한다. 기본값은 제한이 없는 것을 의미하는 None 이다.
[DecisionTreeClassifier에서 결정 트리의 형태를 제한하는 하이퍼파라미터]
- max_features: 각 노드에서 분할에 사용할 특성의 최대 수
- max_leaf_nodes: 리프 노드의 최대 수
- min_samples_split: 분할되기 위해 노드가 가져야하 하는 최소 샘플 수
- min_samples_leaf: 리프 노드가 생성되기 위해 가지고 있어야 할 최소 샘플 수
- min_weight_fraction_leaf: min_sample_leaf와 같지만 가중치가 부여된 전체 샘플 수에서의 비율
- min_impurity_decrease: 노드 분할이 감소시킬 최소 불순도를 지정. 기본값은 0.
▶ min_ 으로 시작하는 매개변수를 증가시키거나 max_ 로 시작하는 매개변수를 감소시키면 모델에 규제가 커진다.
[moons 데이터셋에서 규제 테스트 해보기]
규제 없이 결정 트리 하나를 훈련하고, min_samples_leaf=5로 다른 결정 트리를 훈련하고 각 결정 트리의 결정 경계 시각화
from sklearn.datasets import make_moons
X_moons, y_moons = make_moons(n_samples=150, noise=0.2, random_state=42)
tree_clf1 = DecisionTreeClassifier(random_state=42)
tree_clf2 = DecisionTreeClassifier(min_samples_leaf=5, random_state=42)
tree_clf1.fit(X_moons, y_moons)
tree_clf2.fit(X_moons, y_moons)
def plot_decision_boundary(clf, X, y, axes, cmap):
x1, x2 = np.meshgrid(np.linspace(axes[0], axes[1], 100),
np.linspace(axes[2], axes[3], 100))
X_new = np.c_[x1.ravel(), x2.ravel()]
y_pred = clf.predict(X_new).reshape(x1.shape)
plt.contourf(x1, x2, y_pred, alpha=0.3, cmap=cmap)
plt.contour(x1, x2, y_pred, cmap="Greys", alpha=0.8)
colors = {"Wistia": ["#78785c", "#c47b27"], "Pastel1": ["red", "blue"]}
markers = ("o", "^")
for idx in (0, 1):
plt.plot(X[:, 0][y == idx], X[:, 1][y == idx],
color=colors[cmap][idx], marker=markers[idx], linestyle="none")
plt.axis(axes)
plt.xlabel(r"$x_1$")
plt.ylabel(r"$x_2$", rotation=0)
fig, axes = plt.subplots(ncols=2, figsize=(10, 4), sharey=True)
plt.sca(axes[0])
plot_decision_boundary(tree_clf1, X_moons, y_moons,
axes=[-1.5, 2.4, -1, 1.5], cmap="Wistia")
plt.title("No restrictions")
plt.sca(axes[1])
plot_decision_boundary(tree_clf2, X_moons, y_moons,
axes=[-1.5, 2.4, -1, 1.5], cmap="Wistia")
plt.title(f"min_samples_leaf = {tree_clf2.min_samples_leaf}")
plt.ylabel("")
plt.show()
- 왼쪽: 규제하지 않은 결정 트리 ▶ 과대적합
- 오른쪽: 규제(min_sample_leaf=5)를 추가한 결정 트리 ▶ 일반화가 더 잘됨.
다른 랜덤 시드로 생성한 테스트 세트에서 두 결정 트리를 평가해보면
X_moons_test, y_moons_test = make_moons(n_samples=1000, noise=0.2,
random_state=43)
print(tree_clf1.score(X_moons_test, y_moons_test))
print(tree_clf2.score(X_moons_test, y_moons_test))
▶ 두 번째 결정트리의 정확도가 더 높다.
회귀
결정 트리는 회귀 문제에도 사용되는데, 사이킷런의 DecisionTreeRegressor를 사용해 잡음이 섞인 2차 함수 형태의 데이터셋에서 max_depth=2 설정으로 회귀 트리를 만들어 본다.
import numpy as np
from sklearn.tree import DecisionTreeRegressor
np.random.seed(42)
X_quad = np.random.rand(200, 1) - 0.5 # 랜덤한 하나의 입력 특성
y_quad = X_quad ** 2 + 0.025 * np.random.randn(200, 1)
tree_reg = DecisionTreeRegressor(max_depth=2, random_state=42)
tree_reg.fit(X_quad, y_quad)
export_graphviz(
tree_reg, # 시각화할 의사 결정 트리
out_file='squad_tree.dot', # 시각화된 트리를 저장할 파일의 이름과 형식 지정
feature_names=['x1'], # 트리의 노드에서 표시할 특성의 이름
rounded=True, # 노드의 모서리를 둥글게 만듦
filled=True # 노드를 색상으로 채움.
)
# 파일 로드 및 표시
Source.from_file('squad_tree.dot')
▶ 이는 앞서 만든 분류 트리와 매우 비슷해 보이나, 주요한 차이는 각 노드에서 클래스를 예측하는 대신 어떤 값을 예측한다는 점이다.
만약 x1 = 0.2인 샘플의 타깃값을 예측한다고 가정할 경우 위의 그림 처럼 루트 노드 - 오른쪽 자식 노드 - 왼쪽 자식 노드(리프 노드)로 이동하여 value = 0.028을 예측으로 사용하고, 이 리프 노드에 있는 110개 훈련 샘플의 평균 타깃값이 예측값이 된다.
이 예측값을 사용해 110개 샘플에 대한 평균 제곱 오차(MSE)를 계산하면 0.015가 된다.
★ export_graphviz 사용하여 결정 트리 시각화하는 방법
위 코드는 export_graphviz와 Source.from_file을 이용하여 의사결정 트리 모델을 시각화하는 방법을 보여준다. 각 함수와 매개변수의 기능을 설명하면 다음과 같다.
1) export_graphviz:
- export_graphviz는 의사결정 트리를 .dot 파일로 저장하는 함수다. 이 함수는 주로 Scikit-Learn 라이브러리에서 의사결정 트리를 시각화하기 위해 사용된다.
- tree_reg는 시각화할 의사결정 트리 모델을 나타낸다.
- out_file='squad_tree.dot'는 시각화된 트리를 저장할 파일의 이름과 형식을 지정한다. 여기서는 .dot 파일로 저장된다.
- feature_names=['x1']는 트리의 노드에서 표시할 특성의 이름이다. 여기서는 'x1'이라는 하나의 특성이 사용되었다.
- rounded=True는 노드의 모서리를 둥글게 만들어 시각적 구분을 돕는다.
- filled=True는 노드를 색상으로 채워 모델의 예측 결과를 더 직관적으로 볼 수 있도록 설정한다.
2) Source.from_file('squad_tree.dot'):
Source.from_file은 .dot 파일을 불러와 표시하는 함수이다. Graphviz나 관련 라이브러리를 이용해 .dot 파일의 시각적 표현을 생성하여, 트리를 이미지 형태로 볼 수 있도록 한다.
이 코드를 실행하면 squad_tree.dot 파일에 저장된 트리 구조를 로드하고, 이를 그래픽으로 표시하게 된다.
이 모델의 예측을 max_depth=2와 max_depth=3으로 설정할 때는 아래와 같다.
tree_reg2 = DecisionTreeRegressor(max_depth=3, random_state=42)
tree_reg2.fit(X_quad, y_quad)
def plot_regression_predictions(tree_reg, X, y, axes=[-0.5, 0.5, -0.05, 0.25]):
x1 = np.linspace(axes[0], axes[1], 500).reshape(-1, 1)
y_pred = tree_reg.predict(x1)
plt.axis(axes)
plt.xlabel("$x_1$")
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_reg, X_quad, y_quad)
th0, th1a, th1b = tree_reg.tree_.threshold[[0, 1, 4]]
for split, style in ((th0, "k-"), (th1a, "k--"), (th1b, "k--")):
plt.plot([split, split], [-0.05, 0.25], style, linewidth=2)
plt.text(th0, 0.16, "Depth=0", fontsize=15)
plt.text(th1a + 0.01, -0.01, "Depth=1", horizontalalignment="center", fontsize=13)
plt.text(th1b + 0.01, -0.01, "Depth=1", fontsize=13)
plt.ylabel("$y$", rotation=0)
plt.legend(loc="upper center", fontsize=16)
plt.title("max_depth=2")
plt.sca(axes[1])
th2s = tree_reg2.tree_.threshold[[2, 5, 9, 12]]
plot_regression_predictions(tree_reg2, X_quad, y_quad)
for split, style in ((th0, "k-"), (th1a, "k--"), (th1b, "k--")):
plt.plot([split, split], [-0.05, 0.25], style, linewidth=2)
for split in th2s:
plt.plot([split, split], [-0.05, 0.25], "k:", linewidth=1)
plt.text(th2s[2] + 0.01, 0.15, "Depth=2", fontsize=13)
plt.title("max_depth=3")
plt.show()
분류에서와 같이 회귀 작업에서도 결정 트리가 과대적합 되기 쉽다. 규제가 없다면 훈련 세트에 과대적합된 예측을 하게 되는데, 이때 규제를 적용(min_samples_leaf=10) 하면 더 나은 모델을 만들어준다.
tree_reg1 = DecisionTreeRegressor(random_state=42)
tree_reg2 = DecisionTreeRegressor(random_state=42, min_samples_leaf=10)
tree_reg1.fit(X_quad, y_quad)
tree_reg2.fit(X_quad, y_quad)
x1 = np.linspace(-0.5, 0.5, 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_quad, y_quad, "b.")
plt.plot(x1, y_pred1, "r.-", linewidth=2, label=r"$\hat{y}$")
plt.axis([-0.5, 0.5, -0.05, 0.25])
plt.xlabel("$x_1$")
plt.ylabel("$y$", rotation=0)
plt.legend(loc="upper center")
plt.title("No restrictions")
plt.sca(axes[1])
plt.plot(X_quad, y_quad, "b.")
plt.plot(x1, y_pred2, "r.-", linewidth=2, label=r"$\hat{y}$")
plt.axis([-0.5, 0.5, -0.05, 0.25])
plt.xlabel("$x_1$")
plt.title(f"min_samples_leaf={tree_reg2.min_samples_leaf}")
plt.show()
축 방향에 대한 민감성
[결정 트리의 장점]
결정 트리는 비교적 이해하고 해석하기 쉬우며, 사용하기 편하고, 여러 용도로 사용할 수 있으며, 성능도 뛰어나다.
[결정 트리의 제한]
결정 트리는 계단 모양의 결정 경계를 만드는데 (모든 분할은 축에 수직), 데이터의 방향에 민감하다.
np.random.seed(6)
X_square = np.random.rand(100, 2) - 0.5
y_square = (X_square[:, 0] > 0).astype(np.int64)
angle = np.pi / 4 # 45 도
rotation_matrix = np.array([[np.cos(angle), -np.sin(angle)],
[np.sin(angle), np.cos(angle)]])
X_rotated_square = X_square.dot(rotation_matrix)
tree_clf_square = DecisionTreeClassifier(random_state=42)
tree_clf_square.fit(X_square, y_square)
tree_clf_rotated_square = DecisionTreeClassifier(random_state=42)
tree_clf_rotated_square.fit(X_rotated_square, y_square)
fig, axes = plt.subplots(ncols=2, figsize=(10, 4), sharey=True)
plt.sca(axes[0])
plot_decision_boundary(tree_clf_square, X_square, y_square,
axes=[-0.7, 0.7, -0.7, 0.7], cmap="Pastel1")
plt.sca(axes[1])
plot_decision_boundary(tree_clf_rotated_square, X_rotated_square, y_square,
axes=[-0.7, 0.7, -0.7, 0.7], cmap="Pastel1")
plt.ylabel("")
plt.show()
선형으로 구분될 수 있는 데이터셋을 예로 들면,
- 왼쪽의 결정 트리는 쉽게 데이터셋을 구분
- 오른쪽의 결정 트리(데이터셋을 45도 회전) 는 불필요하게 구불구불해졌다. ▶ 일반화되기 어려움
이 문제를 제한하는 한 가지 방법은 데이터의 스케일을 조정한 다음 주성분 분석(PCA) 변환을 적용하는 것이다. 즉, 특성 간의 상관 관계를 줄이는 방식으로 데이터를 회전하여 결정 트리를 더 쉽게 만들 수 있다.
데이터의 스케일을 조정하고 PCA를 사용하여 데이터를 회전시키는 작은 파이프라인을 만든 다음 이 데이터에서 DecisionTreeClassifier를 훈련 시켜본다.
from sklearn.decomposition import PCA
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
pca_pipeline = make_pipeline(StandardScaler(), PCA())
X_iris_rotated = pca_pipeline.fit_transform(X_iris)
tree_clf_pca = DecisionTreeClassifier(max_depth=2, random_state=42)
tree_clf_pca.fit(X_iris_rotated, y_iris)
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.decomposition import PCA
from sklearn.tree import DecisionTreeClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
import matplotlib.colors
# 결정 경계 시각화
plt.figure(figsize=(8, 4))
axes = [-2.2, 2.4, -0.6, 0.7]
z0s, z1s = np.meshgrid(np.linspace(axes[0], axes[1], 100),
np.linspace(axes[2], axes[3], 100))
X_iris_pca_all = np.c_[z0s.ravel(), z1s.ravel()]
y_pred = tree_clf_pca.predict(X_iris_pca_all).reshape(z0s.shape)
custom_cmap = matplotlib.colors.ListedColormap(['#fafab0', '#9898ff', '#a0faa0'])
plt.contourf(z0s, z1s, y_pred, alpha=0.3, cmap=custom_cmap)
for idx, (name, style) in enumerate(zip(iris.target_names, ("yo", "bs", "g^"))):
plt.plot(X_iris_rotated[:, 0][y_iris == idx],
X_iris_rotated[:, 1][y_iris == idx],
style, label=f"Iris {name}")
plt.xlabel("$z_1$")
plt.ylabel("$z_2$", rotation=0)
th1, th2 = tree_clf_pca.tree_.threshold[[0, 2]]
plt.plot([th1, th1], axes[2:], "k-", linewidth=2)
plt.plot([th2, th2], axes[2:], "k--", linewidth=2)
plt.text(th1 - 0.01, axes[2] + 0.05, "Depth=0",
horizontalalignment="right", fontsize=15)
plt.text(th2 - 0.01, axes[2] + 0.05, "Depth=1",
horizontalalignment="right", fontsize=13)
plt.axis(axes)
plt.legend(loc=(0.32, 0.67))
plt.show()
[PCA 관련 글]
결정 트리의 분산 문제
결정 트리의 주요 문제는 분산이 상당히 크다는 것인데, 하이퍼파라미터나 데이터를 조금만 변경해도 매우 다른 모델이 생성될 수 있다. 사이킷런에서 사용하는 훈련 알고리즘은 각 노드에서 평가할 특성 집합을 랜덤하게 선택하는 확률적 방식으로 정확히 동일한 데이터에서 동일한 결정 트리를 재훈련하더라도 random_state 매개변수를 설정하지 않는 한 매우 다른 모델이 생성될 수 있다.
# random_state 를 이전과 다르게 설정
tree_clf_tweaked = DecisionTreeClassifier(max_depth=2, random_state=40)
tree_clf_tweaked.fit(X_iris, y_iris)
plt.figure(figsize=(8, 4))
y_pred = tree_clf_tweaked.predict(X_iris_all).reshape(lengths.shape)
plt.contourf(lengths, widths, y_pred, alpha=0.3, cmap=custom_cmap)
for idx, (name, style) in enumerate(zip(iris.target_names, ("yo", "bs", "g^"))):
plt.plot(X_iris[:, 0][y_iris == idx], X_iris[:, 1][y_iris == idx],
style, label=f"Iris {name}")
th0, th1 = tree_clf_tweaked.tree_.threshold[[0, 2]]
plt.plot([0, 7.2], [th0, th0], "k-", linewidth=2)
plt.plot([0, 7.2], [th1, th1], "k--", linewidth=2)
plt.text(1.8, th0 + 0.05, "Depth=0", verticalalignment="bottom", fontsize=15)
plt.text(2.3, th1 + 0.05, "Depth=1", verticalalignment="bottom", fontsize=13)
plt.xlabel("Petal length (cm)")
plt.ylabel("Petal width (cm)")
plt.axis([0, 7.2, 0, 3])
plt.legend()
plt.show()
▶ 이러한 경우 여러 결정 트리의 예측을 평균하면 분산을 크게 줄일 수 있으며, 이러한 결정 트리의 앙상블을 랜덤 포레스트라고 한다.
다음 내용
[출처]
핸즈 온 머신러닝
https://thebook.io/080223/0058/
zephyrus1111.tistory.com - [Graphviz] Dot graph(그래프)를 이미지로 저장하기 (feat. render)
dschloe.github.io - 입문자를 위한 머신러닝 분류 튜토리얼 - Decision Tree
'[파이썬 Projects] > <파이썬 머신러닝>' 카테고리의 다른 글
[머신러닝] 서포트 벡터 머신(SVM) (1) | 2024.11.14 |
---|---|
[머신러닝] 모델 훈련 - 2 (3) | 2024.11.13 |
[머신러닝] 모델 훈련 - 1 (6) | 2024.11.09 |
[머신러닝] 분류: MNIST 데이터셋 실습 - 2 (0) | 2024.11.09 |
[머신러닝] 분류: MNIST 데이터셋 실습 - 1 (0) | 2024.11.09 |