회귀트리(Decision Tree) 회귀 모델 완벽 이해

2025-10-12


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_depthmin_samples_splitmin_samples_leaf
제한 없음None21
깊이 제한521
샘플 수 제한None5020
종합 제한55020

과적합 방지 전략

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
선형회귀예: 1530.450.71
의사결정나무예: 920.120.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
활용 예시고객소득예측, 보험가입확률, 상품가격예측 등

📘 결론

의사결정나무는 단순하면서도 강력한 비선형 회귀 모델입니다.
적절한 파라미터 조정과 시각화 분석을 병행하면, 데이터의 숨은 구조를 이해하는 데 큰 도움이 됩니다.

댓글

GitHub 계정으로 댓글을 남겨보세요!