반응형

결정트리(Decision Tree)는
- 구조가 직관적이고
- 규칙 기반으로 동작하며
- 모델 해석이 쉬운 장점
때문에 머신러닝 입문 단계에서 반드시 다뤄지는 알고리즘입니다.
이번 글에서는 결정트리를 사용할 때 반드시 함께 이해해야 할 두 가지 핵심 개념을 다룹니다.
- 결정트리에서 속성 중요도(Feature Importance)는 무엇이며, 어떻게 해석해야 하는가
- 결정트리가 왜 과적합(Overfitting)에 매우 취약한가
특히 시각화 코드를 통해
“잘 맞추는 모델 ≠ 좋은 모델”
이라는 개념을 직관적으로 이해하는 것이 목표입니다.
1. 결정트리의 속성 중요도(Feature Importance)란?
결정트리는 데이터를 분할하면서
어떤 피처가 분류에 얼마나 중요한 역할을 했는지를 계산해 제공합니다.
이를 **속성 중요도(Feature Importance)**라고 합니다.
- 값의 범위는 0 ~ 1
- 모든 피처 중요도의 합은 1
- 값이 클수록 모델 분류에 더 많이 기여한 피처입니다.
단,
“중요도가 높다 = 실제 의미적으로 중요하다”는 의미는 아닙니다.
결정트리는 데이터 분할 기준에서 얼마나 자주, 얼마나 크게 사용되었는지만을 기준으로 계산합니다.
2. 속성 중요도 확인 코드 (아이리스 데이터 기준)
import seaborn as sns
import numpy as np
# DecisionTreeClassifier로 학습된 모델(dt_clf)이 이미 존재한다고 가정
# feature_importances_는 각 피처의 중요도를 배열 형태로 반환합니다.
print(f"{dt_clf.feature_importances_}")
# 피처 이름과 중요도를 함께 출력
# zip을 이용해 (피처명, 중요도)를 매핑
for name, value in zip(iris_data.feature_names, dt_clf.feature_importances_):
print(f"{name} - {value}")
# 시각화를 통해 피처 중요도를 한눈에 확인
sns.barplot(
x=dt_clf.feature_importances_,
y=iris_data.feature_names
)

해석 포인트
- 어떤 피처가 가장 먼저 분기 기준으로 사용되었는지 확인할 수 있습니다.
- 중요도가 0에 가까운 피처는 거의 사용되지 않았다는 의미입니다.
- 하지만 상관관계가 높은 피처가 여러 개 존재하면 중요도가 왜곡될 수 있음을 항상 염두에 두어야 합니다.
3. 과적합(Overfitting)이란 무엇인가
과적합이란
모델이 학습 데이터에 너무 과도하게 맞춰져, 새로운 데이터에 대한 예측 성능이 떨어지는 현상
을 의미합니다.
결정트리는 다음 이유로 과적합에 특히 취약합니다.
- 분할을 계속 반복하며
- 데이터의 아주 작은 차이까지 규칙으로 만들어 버리기 때문입니다.
4. 과적합을 이해하기 위한 샘플 데이터 생성
4-1. 인위적인 분류 데이터 생성
from sklearn.datasets import make_classification
import matplotlib.pyplot as plt
%matplotlib inline
plt.title("3 Class value with 2 Features Sample data creation")
# 2개의 피처와 3개의 클래스를 가진 데이터 생성
X_features, y_labels = make_classification(
n_features=2, # 피처 개수
n_redundant=0, # 불필요한 피처 없음
n_informative=2, # 실제로 의미 있는 피처 2개
n_classes=3, # 클래스 개수
n_clusters_per_class=1,
random_state=0
)
# 생성된 데이터 시각화
plt.scatter(
X_features[:, 0],
X_features[:, 1],
marker='o',
c=y_labels,
s=25,
cmap='rainbow',
edgecolors='k'
)
이 데이터는 의도적으로 분류가 쉬운 형태로 생성되었습니다.

5. 결정 경계(Decision Boundary) 시각화 함수
import numpy as np
def visual_boundary(model, X, y):
fig, ax = plt.subplots()
# 학습 데이터 산점도
ax.scatter(
X[:, 0], X[:, 1],
c=y,
s=25,
cmap='rainbow',
edgecolors='k',
zorder=3
)
ax.axis('tight')
ax.axis('off')
# 그래프 범위 설정
xlim_start, xlim_end = ax.get_xlim()
ylim_start, ylim_end = ax.get_ylim()
# 결정 경계를 그리기 위한 meshgrid 생성
xx, yy = np.meshgrid(
np.linspace(xlim_start, xlim_end, num=200),
np.linspace(ylim_start, ylim_end, num=200)
)
# 각 좌표에 대해 클래스 예측
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
n_classes = len(np.unique(y))
# 결정 경계 시각화
ax.contourf(
xx, yy, Z,
alpha=0.3,
levels=np.arange(n_classes + 1) - 0.5,
cmap='rainbow',
zorder=1
)
plt.show()
6. 과적합이 발생한 결정트리 예제
6-1. 트리 제약 없이 학습한 경우
from sklearn.tree import DecisionTreeClassifier
# 아무 제약 없는 결정트리
dt_clf_1 = DecisionTreeClassifier(random_state=156)
dt_clf_1.fit(X_features, y_labels)
# 결정 경계 시각화
visual_boundary(dt_clf_1, X_features, y_labels)

결과 해석
- 학습 데이터는 거의 완벽하게 분리
- 아주 작은 영역까지 세밀하게 분할됨
- 노이즈와 아웃라이어까지 학습한 전형적인 과적합 상태
학습 데이터 성능은 높지만,
실제 테스트 데이터에는 매우 취약합니다.
7. 과적합을 제어한 결정트리 예제
7-1. min_samples_leaf 적용
# 과적합 제어를 위해 리프 노드 최소 샘플 수 제한
dt_clf_2 = DecisionTreeClassifier(
random_state=156,
min_samples_leaf=6
)
dt_clf_2.fit(X_features, y_labels)
# 결정 경계 시각화
visual_boundary(dt_clf_2, X_features, y_labels)
결과 해석
- 결정 경계가 훨씬 부드러워짐
- 과도한 분할 감소
- 일반화 성능 향상 가능성 증가
실무에서는
**“조금 덜 맞추더라도, 더 일반적인 모델”**이 훨씬 중요합니다.

이번 글에서는 결정트리에서 반드시 이해해야 할 두 가지 핵심을 정리했습니다.
핵심 요약
- 속성 중요도는 모델 내부 기준에서의 상대적 중요도입니다.
- 중요도가 높다고 해서 반드시 실제 의미적으로 중요한 것은 아닙니다.
- 결정트리는 과적합에 매우 취약한 알고리즘입니다.
- 하이퍼파라미터(max_depth, min_samples_leaf 등)를 통해 과적합을 반드시 제어해야 합니다.
- 시각화를 통해 과적합 여부를 직관적으로 확인할 수 있습니다.
반응형
'Programming' 카테고리의 다른 글
| 앙상블 학습 2편: 배깅(Bagging)과 랜덤 포레스트(Random Forest) 완전 이해 (0) | 2025.12.17 |
|---|---|
| 앙상블 학습(Ensemble Learning) 완전 정리 1편: 개념 이해와 보팅(Voting) 실습 (0) | 2025.12.17 |
| 결정트리(Decision Tree) 완전 정리: 개념부터 앙상블, 하이퍼파라미터, 시각화까지 (0) | 2025.12.16 |
| [분류 성능 평가지표 4편] 피마 인디언 당뇨병 예측 실습으로 이해하는 분류 평가의 모든 것 (2) | 2025.12.13 |
| [분류 성능 평가지표 3편] F1 Score·ROC-AUC로 이진 분류 성능 완성하기 (0) | 2025.12.13 |