Post

[Python] ML-XGBoost

[Python] ML-XGBoost

1. 왜 등장했는가

기존 Gradient Boosting은 정확하지만 느리고, 과적합 제어 수단이 부족했습니다.
XGBoost는 2차 미분(헤시안)을 이용한 정밀한 분기 탐색과 다양한 정규화 기법으로
속도·성능·과적합 제어를 동시에 개선해 캐글 대회를 석권했습니다. (Chen & Guestrin, 2016)

Gradient Boosting이 “방향만 알고 내려가는 등산객”이라면,
XGBoost는 “방향과 경사의 곡률까지 파악해 보폭을 조절하는 영리한 등산객” 입니다.


2. 핵심 아이디어 — Gradient Boosting의 수학적 완성판

XGBoost는 본질적으로 Gradient Boosting에 정밀도와 정규화를 더한 알고리즘입니다.

xgb_idea

1
2
3
4
5
6
7
8
9
10
11
12
Gradient Boosting:                XGBoost:
1차 미분(기울기)만 사용            1차 + 2차 미분 사용
                                   → 더 정확한 분기 방향과 크기

정규화 없음                        L1, L2 정규화 + 트리 복잡도 패널티
                                   → 과적합 제어 수단 풍부

결측값 처리 없음                    결측값 자동 처리
                                   → 분기 방향 학습

컬럼 샘플링 없음                    subsample + colsample_bytree
                                   → 더 다양한 트리, 빠른 학습

3. 실제 예시로 보기 (분류 / 회귀)

예시 1 — 타이타닉 생존 예측 (분류)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
훈련 데이터:
┌────────┬────────┬─────┬──────────┬──────────┐
│ 이름   │ 성별   │ 나이 │ 객실 등급 │ 생존 여부 │
├────────┼────────┼─────┼──────────┼──────────┤
│ Alice  │ 여성   │  29  │   1등석   │    ✅    │
│ Bob    │ 남성   │  31  │   3등석   │    ❌    │
│ Carol  │ 여성   │  45  │   3등석   │    ✅    │
│ Dave   │ 남성   │  12  │   2등석   │    ✅    │
│ Eve    │ 남성   │  38  │   3등석   │    ❌    │
└────────┴────────┴─────┴──────────┴──────────┘

Round 1: 초기 예측 = log(생존수/사망수) = log(3/2) ≈ 0.405
  → 1차 미분 gᵢ + 2차 미분 hᵢ 계산
  → 최적 분기: "Sex_male ≤ 0.5"
  → 리프값: -Σg / (Σh + λ)   ← λ는 L2 정규화

Round 2: 업데이트된 예측으로 새 gᵢ, hᵢ 계산
  → 다음 트리 학습

...n_estimators번 반복...

예시 2 — 2차 미분의 의미

1
2
3
4
5
6
7
8
9
언덕 내려가기 비유:

Gradient Boosting (1차만):
  "아래로 내려가라" (방향만)
  → 보폭을 고정 → 최솟값에 정확히 멈추기 어려움

XGBoost (1차 + 2차):
  "아래로 내려가되, 경사 완만해지면 보폭 줄여라" (방향 + 곡률)
  → 보폭도 최적화 → 더 정확하게 최솟값에 도달

4. 알고리즘 구성 요소

xgb_component

구성 요소설명비유
gᵢ (1차 미분)손실 함수의 기울기어느 방향으로 틀렸나
hᵢ (2차 미분)손실 함수의 곡률얼마나 확실하게 틀렸나
Gain분기로 얻는 손실 감소량이 질문이 얼마나 도움이 되나
λ (lambda)L2 정규화리프값에 세금
γ (gamma)분기 최소 Gain 임계값의미 있는 분기만 허용

1차와 2차 미분이 분기 탐색에서 어떻게 활용되는지 아래 그래프에서 확인할 수 있습니다.

xgb_gradient_hessian

읽는 법:
왼쪽(1차 미분 / Gradient): 손실 함수의 기울기. 어느 방향으로 예측을 수정해야 하는지를 나타냅니다.
오른쪽(2차 미분 / Hessian): 손실 함수의 곡률. 기울기 변화 속도를 나타내며, 보폭을 얼마나 크게 할지 결정합니다.
두 값을 함께 사용하면 Gradient만 쓸 때보다 더 정확하게 최솟값에 도달할 수 있습니다.


5. XGBoost가 Gradient Boosting보다 나은 이유

5-1. 정규화 세 가지

\[\Omega(f) = \gamma T + \frac{1}{2}\lambda\sum_j w_j^2 + \alpha\sum_j |w_j|\]
1
2
3
γ (gamma): 리프 수 패널티 → 트리를 단순하게 유지
λ (lambda): 리프값 L2 패널티 → 리프값을 작게 유지
α (alpha): 리프값 L1 패널티 → 일부 리프값을 0으로

5-2. Gain이 γ보다 작으면 분기 안 함

1
2
3
4
5
Gain < γ:  "이 분기로 얻는 이득이 너무 적다" → 분기 취소
           → 불필요한 가지 자동 제거 (사후 가지치기)

γ=0: 모든 분기 허용
γ=1: 손실 감소 < 1인 분기 차단

γ 값에 따라 트리의 복잡도가 어떻게 달라지는지 아래 그래프에서 확인할 수 있습니다.

xgb_gamma_complexity

읽는 법:
왼쪽(γ=0): 모든 분기를 허용해 트리가 복잡해집니다. 훈련 성능은 높지만 과적합 위험이 있습니다.
오른쪽(γ 증가): Gain이 γ보다 작은 분기가 차단되어 트리가 단순해집니다.
최적 γ는 GridSearchCV로 탐색하지만, 일반적으로 0~1 사이의 작은 값부터 시작합니다.


6. XGBoost 장・단점

6-1. ✅ XGBoost 장점

1
2
3
4
5
6
7
8
9
10
11
1. 높은 예측 성능
   → 2차 미분 + 정규화 → 정확하고 안정적

2. 풍부한 정규화
   → γ, λ, α, subsample, colsample_bytree 등 다양한 과적합 제어

3. 결측값 자동 처리
   → 분기 방향 자동 학습 → 별도 imputation 불필요

4. 조기 종료 (Early Stopping)
   → 검증 성능 개선 없으면 자동 중단

6-2. ❌ XGBoost가 약한 상황

1
2
3
4
5
6
7
8
9
10
1. 대용량 데이터
   → LightGBM이 더 빠름 (히스토그램 방식)

2. 하이퍼파라미터 많음
   → n_estimators, learning_rate, max_depth,
      min_child_weight, subsample, colsample_bytree,
      gamma, lambda, alpha 동시 조정 필요

3. 범주형 특성
   → 인코딩 필요 (LightGBM은 직접 처리 가능)

6-2-1. 과적합 문제

XGBoost의 주요 주의사항입니다.

훈련 AUC: 0.99, 검증 AUC: 0.82 ← 과적합 징후

해결책 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 방법 1: 정규화 강화
xgb = XGBClassifier(
    gamma=1,         # 분기 최소 Gain (클수록 단순한 트리)
    reg_alpha=0.1,   # L1
    reg_lambda=1.0,  # L2
    random_state=0
)

# 방법 2: 샘플링
xgb = XGBClassifier(
    subsample=0.8,          # 행 샘플링
    colsample_bytree=0.8,   # 열 샘플링
    random_state=0
)

# 방법 3: GridSearchCV로 최적값 탐색
from sklearn.model_selection import GridSearchCV

param_grid = {
    'n_estimators':     [100, 200, 300],
    'learning_rate':    [0.01, 0.05, 0.1],
    'max_depth':        [3, 4, 5],
    'min_child_weight': [1, 3, 5],
    'subsample':        [0.8, 1.0],
    'colsample_bytree': [0.8, 1.0]
}
xgb_cv = GridSearchCV(
    XGBClassifier(random_state=0, eval_metric='auc'),
    param_grid,
    cv=5,
    scoring='roc_auc'
)
xgb_cv.fit(X_train, y_train)
print(f"Best params : {xgb_cv.best_params_}")
print(f"Best AUC    : {xgb_cv.best_score_:.4f}")

7. 한눈에 요약

항목내용
알고리즘 유형지도학습 / 분류 & 회귀 모두 가능
핵심 아이디어2차 미분 + 다양한 정규화로 개선된 Gradient Boosting
기반 모델얕은 Decision Tree
스케일링 필요?❌ 불필요
결측값✅ 자동 처리
핵심 파라미터n_estimators, learning_rate, max_depth, subsample
실전 사용정확도 최우선, 캐글 대회, 중소규모 정형 데이터

8. 다른 알고리즘과 무엇이 다른가

Gradient Boosting vs XGBoost vs LightGBM

항목GradientBoostingXGBoostLightGBM
미분1차1차+2차1차+2차
트리 성장Level-wiseLevel-wiseLeaf-wise
속도느림보통빠름
정규화제한적풍부풍부
결측값✅ 자동✅ 자동
범주형✅ 직접
소규모 데이터⚠️
대규모 데이터

세 알고리즘의 포지션을 한마디로 정리하면:
Gradient Boosting → 원리 이해용 교과서
XGBoost → 중소규모 정형 데이터의 표준
LightGBM → 대용량 데이터의 속도왕


9. 코드로 보기 — 타이타닉 생존 예측

1
from xgboost import XGBClassifier

9-1. 전처리 (짧게)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import pandas as pd
from sklearn.model_selection import train_test_split

titanic = pd.read_csv('./Data/Titanic.csv')
titanic['FamSize'] = titanic['SibSp'] + titanic['Parch']

use_cols = ['Survived', 'Pclass', 'Sex', 'Age', 'FamSize', 'Fare', 'Embarked']
titanic = titanic[use_cols].dropna(subset=['Age'])
titanic['Age'] = titanic['Age'].astype(int)
titanic = pd.get_dummies(titanic, columns=['Pclass', 'Sex', 'Embarked'], drop_first=True)

y = titanic['Survived']
X = titanic.drop('Survived', axis=1)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=0)

# ⚠️ XGBoost는 트리 기반 → 스케일링 불필요
# StandardScaler 생략

Note: XGBoost는 트리 기반이므로 StandardScaler가 필요 없습니다. 결측값도 자동으로 처리합니다.


9-2. 모델 학습

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
xgb = XGBClassifier(
    n_estimators=300,         # 트리 수
    learning_rate=0.05,       # 각 트리 기여도
    max_depth=4,              # 트리 최대 깊이
    min_child_weight=3,       # 리프의 최소 가중치 합 (과적합 방지)
    subsample=0.8,            # 행 샘플링 비율
    colsample_bytree=0.8,     # 열 샘플링 비율
    gamma=0.1,                # 분기 최소 Gain
    reg_alpha=0.1,            # L1 정규화
    reg_lambda=1.0,           # L2 정규화
    eval_metric='auc',
    random_state=0
)
xgb.fit(
    X_train, y_train,
    eval_set=[(X_test, y_test)],
    early_stopping_rounds=50,
    verbose=100
)

ParameterDefault역할과적합 방향
n_estimators100트리 수클수록 과적합 ↑
learning_rate0.3트리 기여도클수록 과적합 ↑
max_depth6트리 깊이클수록 과적합 ↑
min_child_weight1리프 최소 가중치작을수록 과적합 ↑
subsample1.0행 샘플링작을수록 과적합 ↓
colsample_bytree1.0열 샘플링작을수록 과적합 ↓
gamma0분기 최소 Gain클수록 과적합 ↓
reg_alpha0L1 정규화클수록 과적합 ↓
reg_lambda1L2 정규화클수록 과적합 ↓
  • n_estimators : 트리를 몇 개 쌓을지
    • 값 변화별 효과
      • 클수록 → 더 정교한 학습, 과적합 ↑
      • Early Stopping과 함께 사용 권장
  • learning_rate : 각 트리의 기여도 축소 계수
    • 값 변화별 효과
      • 작을수록 → 보수적 학습, n_estimators 늘려야 함
      • 큰 값(기본 0.3)은 빠른 수렴이지만 과적합 위험
  • max_depth : 개별 트리의 최대 깊이
    • 값 변화별 효과
      • 기본값 6은 과적합 위험 → 3~5 권장
      • 클수록 → 복잡한 상호작용, 과적합 ↑
  • min_child_weight : 리프 노드의 최소 가중치 합
    • 값 변화별 효과
      • 클수록 → 분기 덜 함 → 과적합 ↓
      • 작을수록 → 계속 분기 → 과적합 ↑
  • subsample & colsample_bytree : 행/열 샘플링
    • 값 변화별 효과
      • 0.8 설정 → 과적합 방지 + 학습 다양성 ↑
      • 1.0 → 전체 사용
  • gamma : 분기 최소 Gain 임계값
    • 값 변화별 효과
      • 클수록 → 의미 없는 분기 차단 → 단순한 트리
      • 0 → 모든 분기 허용

9-3. 평가

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from sklearn.metrics import (
    accuracy_score, confusion_matrix,
    classification_report, roc_auc_score
)

pred      = xgb.predict(X_test)
pred_prob = xgb.predict_proba(X_test)[:, 1]

cfx         = confusion_matrix(y_test, pred)
sensitivity = cfx[1, 1] / (cfx[1, 0] + cfx[1, 1])
specificity = cfx[0, 0] / (cfx[0, 0] + cfx[0, 1])
roc_auc     = roc_auc_score(y_test, pred_prob)

print(f"Accuracy    : {accuracy_score(y_test, pred) * 100:.2f}%")
print(f"Sensitivity : {sensitivity * 100:.2f}%")
print(f"Specificity : {specificity * 100:.2f}%")
print(f"ROC AUC     : {roc_auc:.4f}")
print()
print(classification_report(y_test, pred, target_names=['Died (0)', 'Survived (1)']))

9-4. Early Stopping 결과 확인

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import matplotlib.pyplot as plt

results = xgb.evals_result()

plt.figure(figsize=(8, 4))
plt.plot(results['validation_0']['auc'], color='tomato', label='Validation AUC')
plt.axvline(xgb.best_iteration, color='black', linestyle='--',
            label=f'Best iteration: {xgb.best_iteration}')
plt.xlabel('Boosting Round')
plt.ylabel('AUC')
plt.title('XGBoost Early Stopping')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

9-5. 특성 중요도

1
2
3
4
5
6
7
8
9
10
11
12
import pandas as pd
import matplotlib.pyplot as plt

importances = pd.Series(xgb.feature_importances_, index=X.columns)
importances = importances.sort_values(ascending=True)

plt.figure(figsize=(7, 5))
importances.plot(kind='barh', color='steelblue')
plt.xlabel('Feature Importance')
plt.title('XGBoost Feature Importance')
plt.tight_layout()
plt.show()
This post is licensed under CC BY 4.0 by the author.