1. 학습에 앞서
선형회귀 vs 회귀트리
데이터 분석을 처음 시작할 때는 선형회귀(Linear Regression) 를 많이 사용하지만, 실제 현실 데이터는 직선으로 표현하기 어려운 비선형적인 관계를 가지는 경우가 많습니다. 이런 복잡한 패턴을 잡아내는 데 효과적인 모델이 바로 의사결정나무(Decision Tree) 입니다.
| 모델 | 특징 | 장점 | 단점 |
|---|---|---|---|
| 선형회귀 | 변수 간 관계가 직선 형태일 때 적합 | 단순하고 계산이 빠름 | 비선형 관계를 잘 설명하지 못함 |
| 회귀트리 | 데이터를 조건에 따라 반복 분할 | 비선형 관계, 변수 간 상호작용 자동 학습 결과 해석이 시각적으로 쉬움 | 과적합(Overfitting)에 취약할 수 있음 |
2. 회귀트리의 구조와 구성 요소
의사결정나무는 노드(node) 와 가지(branch) 로 이루어진 계층적 구조를 가집니다.
| 구성 요소 | 설명 |
|---|---|
| 루트 노드 (Root Node) | 전체 데이터를 포함하는 최상위 노드 |
| 내부 노드 (Internal Node) | 조건에 따라 데이터를 분할하는 중간 노드 |
| 리프 노드 (Leaf Node) | 최종 예측값을 가지는 말단 노드 |
| 가지 (Branch) | 노드를 연결하는 경로 |
| 깊이 (Depth) | 트리의 레벨 수준 |
시작
/ | \
1 2 3
/ \ | |
2 3 3 (끝)
/ | |
(끝) | (끝)
|
(끝)
3. 실습으로 배우는 회귀트리
(1) 데이터 준비와 전처리
import pandas as pd
df = pd.read_csv("public/TravelInsurancePrediction.csv")
# 불필요 컬럼 제거 및 결측치 처리
data = df.drop(columns='Unnamed: 0').dropna().drop(index=[1989])
# 특성 변수와 목표 변수 분리
feature_columns = [
'Age', 'Employment_Type_Encoded', 'GraduateOrNot_Encoded',
'FamilyMembers', 'ChronicDiseases', 'FrequentFlyer_Encoded',
'EverTravelledAbroad_Encoded', 'TravelInsurance'
]
X = data[feature_columns]
y = data['AnnualIncome'](2) 회귀트리 모델 학습
from sklearn.tree import DecisionTreeRegressor
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
model = DecisionTreeRegressor(max_depth=3, random_state=42)
model.fit(X_train, y_train)(3) 분할 기준 찾기 — MSE 최소화
‘Age’ 컬럼을 기준으로 데이터를 나누는 최적의 분할점을 찾기 위해 MSE(Mean Squared Error) 를 직접 계산해볼 수 있습니다.
import numpy as np
def cal_mse(age_val):
feature_value = X['Age']
left_mask = feature_value <= age_val
right_mask = feature_value > age_val
left_y, right_y = y[left_mask], y[right_mask]
left_pred, right_pred = np.mean(left_y), np.mean(right_y)
left_mse = np.mean((left_y - left_pred) ** 2)
right_mse = np.mean((right_y - right_pred) ** 2)
return left_mse + right_mse
min_age = X['Age'].min()
max_age = X['Age'].max()
age_value_list = []
age_mse_list = []
for age_val in range(int(min_age), int(max_age) + 1):
mse_result = cal_mse(age_val)
if not np.isnan(mse_result):
age_value_list.append(age_val)
age_mse_list.append(mse_result)
idx = np.argmin(age_mse_list)
age_value_list[idx] # MSE가 가장 작은 값(4) 과적합 방지 (Hyperparameter Tuning)
회귀트리는 매우 유연하기 때문에 과적합(Overfitting) 이 쉽게 발생합니다.
이를 방지하기 위한 대표적인 하이퍼파라미터 설정은 아래와 같습니다.
| 설정 이름 | max_depth | min_samples_split | min_samples_leaf |
|---|---|---|---|
| 제한 없음 | None | 2 | 1 |
| 깊이 제한 | 5 | 2 | 1 |
| 샘플 수 제한 | None | 50 | 20 |
| 종합 제한 | 5 | 50 | 20 |
과적합 방지 전략
import matplotlib.pyplot as plt
from sklearn.metrics import r2_score, mean_squared_error
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
hyperparams = {
'제한 없음': {'max_depth': None, 'min_samples_split': 2, 'min_samples_leaf': 1},
'깊이 제한': {'max_depth': 5, 'min_samples_split': 2, 'min_samples_leaf': 1},
'샘플 수 제한': {'max_depth': None, 'min_samples_split': 50, 'min_samples_leaf': 20},
'종합 제한': {'max_depth': 5, 'min_samples_split': 50, 'min_samples_leaf': 20},
}
results = {}
for name, params in hyperparams.items():
# 모델 훈련
model = DecisionTreeRegressor(random_state=42, **params)
model.fit(X_train, y_train)
# 예측
train_pred = model.predict(X_train) # 학습한 데이터로 예측
test_pred = model.predict(X_test) # 검증용(테스트) 데이터로 예측
# 성능 계산
train_mse = mean_squared_error(y_train, train_pred)
test_mse = mean_squared_error(y_test, test_pred)
train_r2 = r2_score(y_train, train_pred)
test_r2 = r2_score(y_test, test_pred)
results[name] = {
'train_mse': train_mse,
'test_mse': test_mse,
'train_r2': train_r2,
'test_r2': test_r2,
'depth': model.get_depth(),
'n_leaves': model.get_n_leaves()
}(5) 트리 구조 시각화
from sklearn.tree import plot_tree, export_text
import matplotlib.pyplot as plt
plt.figure(figsize=(12,10))
plot_tree(model, feature_names=X.columns, filled=True)
plt.show()
print(export_text(model, feature_names=list(X.columns)))⚠️ 주의 트리의 MSE가 너무 클 경우, 불필요하게 복잡한 ‘비정상 트리’가 만들어집니다.
- 정상적인 트리
시작
/ \
(끝) 1
/ \
(끝) 2
/ \
(끝) ...
- 비정상적인 트리
시작
/ \
1 2
/ \ / \
1 2 3 4
/\ /\ /\ /\
...
(6) 모델 성능 평가
- 평가지표: MSE(평균제곱오차), R² 점수
- 비교 모델: 선형회귀 vs 회귀트리
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score, mean_squared_error
linear_model = LinearRegression()
linear_model.fit(X_train, y_train)
tree_model = DecisionTreeRegressor(max_depth=5, min_samples_split=50, min_samples_leaf=20)
tree_model.fit(X_train, y_train)
linear_y_pred = linear_model.predict(X_test)
tree_y_pred = tree_model.predict(X_test)| 모델 | MSE | R² |
|---|---|---|
| 선형회귀 | 예: 1530.45 | 0.71 |
| 의사결정나무 | 예: 920.12 | 0.83 |
(7) 시각적 비교
- 예측값 시각화
import matplotlib.pyplot as plt
plt.scatter(y_test, linear_y_pred)
plt.scatter(linear_y_pred, tree_y_pred)
plt.xlabel('Actual Values')
plt.ylabel('Predicted Values')
plt.legend()
plt.show()- mse, r2값 시각화 확인
import seaborn as sns
linear_mse = mean_squared_error(y_test, linear_y_pred)
tree_mse = mean_squared_error(y_test, tree_y_pred)
linear_r2 = r2_score(y_test, linear_y_pred)
tree_r2 = r2_score(y_test, tree_y_pred)
sns.barplot([linear_mse, tree_mse])
# sns.barplot([linear_r2, tree_r2])✅ 마무리 요약
| 구분 | 내용 |
|---|---|
| 모델명 | DecisionTreeRegressor |
| 장점 | 비선형 패턴 자동 탐색, 해석 용이 |
| 단점 | 과적합에 취약 |
| 핵심 파라미터 | max_depth, min_samples_split, min_samples_leaf |
| 활용 예시 | 고객소득예측, 보험가입확률, 상품가격예측 등 |
📘 결론
의사결정나무는 단순하면서도 강력한 비선형 회귀 모델입니다.
적절한 파라미터 조정과 시각화 분석을 병행하면, 데이터의 숨은 구조를 이해하는 데 큰 도움이 됩니다.