2022.03.18 금
2022.03.19 토
: 분류와 회귀 문제에 널리 사용하는 모델
결정 트리를 학습한다
= 정답에 가장 빨리 도달하는 예/아니오 질문 목록을 학습한다
질문
복잡도 제어
리프노드 → All 순수노드 ⇒ 모델 복잡 & 과대적합
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer
cancer = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(
cancer.data, cancer.target, stratify=cancer.target, random_state=42)
tree = DecisionTreeClassifier(random_state=0)
tree.fit(X_train,y_train)
print("훈련 세트 정확도: {:.3f}".format(tree.score(X_train,y_train)))
print("테스트 세트 정확도: {:.3f}".format(tree.score(X_test,y_test)))
tree = DecisionTreeClassifier(max_depth=4,random_state=0)
tree.fit(X_train,y_train)
print("사전 가지치기 훈련 세트 정확도: {:.3f}".format(tree.score(X_train,y_train)))
print("사전 가지치기 테스트 세트 정확도: {:.3f}".format(tree.score(X_test,y_test)))
훈련 세트 정확도: 1.000 테스트 세트 정확도: 0.937 사전 가지치기 훈련 세트 정확도: 0.988 사전 가지치기 테스트 세트 정확도: 0.951
→ 첫번째 경우 - 가치지기 없이 → 과대적합
→ 두번째 경우 - 사전 가지치기 → 과대적합 줄어듦
결정 트리 분석
export_graphviz()로 시각화
from sklearn.tree import export_graphviz
export_graphviz(tree, out_file="tree.dot", class_names=["악성", "양성"],
feature_names = cancer.feature_names, impurity=False, filled=True)
import graphviz
with open("tree.dot") as f:
dot_graph = f.read()
display(graphviz.Source(dot_graph))
→ samples : 각 노드에 있는 샘플 수 → value : 클래스 당 샘플 수
특성 중요도
: 트리를 만드는 결정에 각 특성이 얼마나 중요한지 평가함
0 ~ 1 사이의 숫자
거의 사용x 완벽하게 타깃 클래스 예측함
특성 중요도의 전체 합 = 1
항상 양수
특성이 어떤 클래스를 지지하는지 알 수 없음
print("특성 중요도:\\n", tree.feature_importances_)
# 특성 중요도 시각화
def plot_feature_importances_cancer(model):
n_features = cancer.data.shape[1]
plt.barh(np.arange(n_features), model.feature_importances_, align='center')
plt.yticks(np.arange(n_features), cancer.feature_names)
plt.xlabel("특성 중요도")
plt.ylabel("특성")
plt.ylim(-1, n_features)
plot_feature_importances_cancer(tree)
특성 중요도: [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.01019737 0.04839825 0. 0. 0.0024156 0. 0. 0. 0. 0. 0.72682851 0.0458159 0. 0. 0.0141577 0. 0.018188 0.1221132 0.01188548 0. ]
→ 트리 첫 루트노드였던 worst radius 값이 가장 큼
→ 그렇다고 해서 다른 특성이 유용하지 않다는 뜻은 아님
→ worst radius 높다 ⇒ 양성을 의미하는지 악성을 의미하는지 알 수 없음
X[1] 정보만 사용 (X[0] 정보 전혀 사용x) 단순하게 비례/반비례 관계가 아님
회귀 결정 트리 특성 DecisionTreeRegressor
외삽 extrapolation 훈련 데이터의 범위 밖 포인트에 대해 예측 불가 ex)
# 컴퓨터 메모리 가격 동향
import os
ram_prices = pd.read_csv(os.path.join(mglearn.datasets.DATA_PATH, "ram_price.csv"))
plt.yticks(fontname="Arial")
plt.semilogy(ram_prices.date, ram_prices.price)
plt.xlabel("년")
plt.ylabel("가격 ($/Mbyte)")
# 2000년 전 후 가격 예측
# DecisionTreeRegressor와 Linear Regression 비교
from sklearn.tree import DecisionTreeRegressor
from sklearn.linear_model import LinearRegression
# 2000년 이전을 훈련데이터, 2000년 이후를 테스트 데이터로..
data_train = ram_prices[ram_prices.date < 2000]
data_test = ram_prices[ram_prices.date >= 2000]
# train
# 가격 예측을 위해 날짜 특성만을 이용
X_train = data_train.date[:, np.newaxis]
# 데이터와 타깃 사이의 관계를 간단하게 만들기 위해 로그 스케일로 바꿈
y_train = np.log(data_train.price)
tree = DecisionTreeRegressor().fit(X_train, y_train)
linear_reg = LinearRegression().fit(X_train, y_train)
# predict
# 전체 기간에 대해서 수행
X_all = ram_prices.date[:,np.newaxis]
pred_tree = tree.predict(X_all)
pred_lr = linear_reg.predict(X_all)
# 예측한 값 -> 로그 스케일로 되돌림 (지수함수)
price_tree = np.exp(pred_tree)
price_lr = np.exp(pred_lr)
# 시각화
plt.semilogy(data_train.date, data_train.price, label = "훈련 데이터")
plt.semilogy(data_test.date, data_test.price, label = "테스트 데이터")
plt.semilogy(ram_prices.date, price_tree, label = "트리 예측")
plt.semilogy(ram_prices.date, price_lr , label = "선형회귀 예측")
plt.legend()
Linear Regression과 DecisionTreeRegreesor 비교
→ Linear Regression : 2000년 이후 꽤 정확하게 예측
→ tree : 2000년 이후 단순히 마지막 포인트를 이용해 예측 (새로운 데이터 예측 불가)
매개변수
장단점
장점
단점
과대 적합되는 경향 O → 일반화 성능 좋지 않음
⇒ 단일 결정 트리의 대안 : 2.3.6 결정 트리의 앙상블