RMSLE(로그 오차)가 의료 데이터(응급실 예측)에서 왜 중요한지 배웠습니다. 오늘은 이론을 넘어, "로그 변환(Log Transform)"을 실제 코드에 어떻게 녹이는지, 그리고 5가지 대표 회귀 모델들이 이 데이터를 어떻게 다루는지 직접 대결을 붙여보겠습니다.
1. Model Evaluation의 핵심: 로그와 지수의 '역함수' 관계
모델을 학습시키기 전, 가장 먼저 해야 할 일은 데이터를 "학습하기 좋은 모양"으로 만드는 것입니다.
환자 수 데이터는 보통 0명에 몰려있고 가끔 폭증하는(Skewed) 형태입니다. 이를 펴주기 위해 로그를 씁니다.
- 들어갈 땐 (Log): np.log1p(y) $\rightarrow$ 데이터를 정규분포처럼 펴줌 + RMSLE 최적화
- 나올 땐 (Exp): np.expm1(pred) $\rightarrow$ 예측값을 다시 실제 '명' 단위로 복구
주의: log의 역함수는 exp입니다. 들어갈 때 로그를 썼다면, 나올 때 반드시 지수(exp)로 되돌려야(Inverse Transform) 원래 값이 됩니다. 안 그러면 "내일 환자가 3.5명 옵니다(실제론 32명)"라는 엉뚱한 보고를 하게 됩니다.
2. 데이터 준비: 응급실의 시간을 쪼개다
먼저 가상의 응급실 데이터를 생성하고, 시간 변수를 추출합니다.
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
# 1. 가상의 응급실 데이터 생성
np.random.seed(42)
data_size = 2000
dates = pd.date_range(start='2024-01-01', periods=data_size, freq='h')
df_er = pd.DataFrame({'datetime': dates})
# 시간 변수 추출 (Feature Engineering)
df_er['hour'] = df_er['datetime'].dt.hour
df_er['dayofweek'] = df_er['datetime'].dt.dayofweek
df_er['month'] = df_er['datetime'].dt.month
df_er['is_weekend'] = (df_er['dayofweek'] >= 5).astype(int)
# 환자 수 생성 (시간대별 패턴 + 노이즈)
# 1) 기본 패턴: 심야(0~5시)와 오전(9~11시)에 많음
# 2) 주말 효과: 주말엔 환자가 조금 더 많음
df_er['patient_count'] = 10 + \
5 * np.sin(df_er['hour'] / 24 * 2 * np.pi) + \
3 * df_er['is_weekend'] + \
np.random.randint(0, 10, data_size) # 노이즈
# 학습/테스트 데이터 분리
X = df_er[['hour', 'dayofweek', 'month', 'is_weekend']]
y = df_er['patient_count']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
print(f"데이터 준비 완료: {X_train.shape}")
3. 모델 대격돌: 5대 회귀 모델 비교
이제 5가지 모델을 하나씩 돌려보며 특징을 알아봅니다. 모든 과정에 Log -> Fit -> Predict -> Exp 파이프라인이 적용됩니다.
① 평가 함수 정의 (RMSLE)
from sklearn.metrics import mean_squared_error
def evaluate_model(model_name, y_true, y_pred):
# RMSLE 계산: 이미 복원된(exp) 값으로 계산
rmsle = np.sqrt(np.mean((np.log1p(y_true) - np.log1p(y_pred))**2))
print(f"[{model_name}] RMSLE: {rmsle:.4f}")
return rmsle
② Linear Regression (선형 회귀) & Lasso (규제)
- Linear Regression: 가장 기초적인 모델. "시간이 1시간 늘면 환자가 $w$명 는다"는 단순 비례 관계를 가정합니다.
- Lasso Regression: 선형 회귀에 L1 규제를 건 모델. 불필요한 변수(예: 영향력 없는 '월')의 가중치를 0으로 만들어버립니다. (Feature Selection 효과)
from sklearn.linear_model import LinearRegression, Lasso
# 1. Linear Regression
lr = LinearRegression()
lr.fit(X_train, np.log1p(y_train)) # 로그 변환 학습
pred_lr = np.expm1(lr.predict(X_test)) # 지수 변환 복구
evaluate_model("Linear Regression", y_test, pred_lr)
# 2. Lasso Regression (규제 적용)
lasso = Lasso(alpha=0.01, random_state=42)
lasso.fit(X_train, np.log1p(y_train))
pred_lasso = np.expm1(lasso.predict(X_test))
evaluate_model("Lasso Regression ", y_test, pred_lasso)

③ Random Forest (랜덤 포레스트)
- 특징: 여러 개의 결정 트리(Decision Tree)가 투표하는 방식.
- 의료 적용: 선형 모델과 달리, "오전 9시엔 급증했다가 점심엔 줄어드는" 비선형적인 패턴을 아주 잘 잡아냅니다.
from sklearn.ensemble import RandomForestRegressor
# 3. Random Forest
rf = RandomForestRegressor(n_estimators=100, random_state=42)
rf.fit(X_train, np.log1p(y_train))
pred_rf = np.expm1(rf.predict(X_test))
evaluate_model("Random Forest ", y_test, pred_rf)

④ Boosting 형제들: XGBoost & Gradient Boosting
- Gradient Boosting: 이전 트리가 틀린 오차를 다음 트리가 고쳐나가는 방식. (조금씩 정교해짐)
- XGBoost: Gradient Boosting의 속도와 성능을 획기적으로 개선한 모델. 캐글(Kaggle) 우승 치트키.
- 의료 적용: 응급실 데이터처럼 갑자기 튀는 아웃라이어나 미세한 패턴까지 놓치지 않고 학습합니다.
from xgboost import XGBRegressor
from sklearn.ensemble import GradientBoostingRegressor
# 4. Gradient Boosting
gb = GradientBoostingRegressor(n_estimators=100, random_state=42)
gb.fit(X_train, np.log1p(y_train))
pred_gb = np.expm1(gb.predict(X_test))
evaluate_model("Gradient Boosting", y_test, pred_gb)
# 5. XGBoost
xgb = XGBRegressor(n_estimators=100, random_state=42)
xgb.fit(X_train, np.log1p(y_train))
pred_xgb = np.expm1(xgb.predict(X_test))
evaluate_model("XGBoost ", y_test, pred_xgb)

[최종 결과 분석] 5대 모델 대격돌: 과연 승자는?
실제 데이터를 통해 5가지 회귀 모델을 돌려본 결과, RMSLE(로그 오차) 점수표가 나왔습니다. 점수가 낮을수록(0에 가까울수록) 예측이 정확하다는 뜻입니다.
| 순위 | 모델 | RMSLE 점수 | 비고 |
| 1 | Graident Boosting | 0.2066 | 압도적 성능 |
| 2 | Random Foreest | 0.2434 | 안정적인 상위권 |
| 3 | XGBoost | 0.2443 | Random Forest와 유사 |
| 4 | Linear Regression | 0.2517 | 단순 선형 |
| 5 | Lasso Regression | 0.2559 | 규제 적용 |
Analyst's Insight: 결과 속에 숨겨진 3가지 의미
1. "응급실은 직선이 아니다" (비선형 모델의 승리)
- Tree 기반 모델(GBM, RF, XGB) vs 선형 모델(Linear, Lasso)
- 결과를 보면 트리 기반 모델들(0.20~0.24)이 선형 모델들(0.25대)보다 확실히 성능이 좋습니다.
- 의료적 해석: 응급실 환자 수는 "시간이 흐른다고 일정하게 늘어나는 것(선형)"이 아닙니다. 오전 9시에 확 몰렸다가, 점심에 빠지고, 저녁에 다시 늘어나는 복잡한 곡선(비선형) 패턴을 그립니다. 선형 회귀는 이 '출렁임'을 따라가지 못해 점수가 낮게 나온 것입니다.
2. 오늘의 MVP는 'Gradient Boosting'
- RMSLE 0.2066이라는 놀라운 수치로 1위를 차지했습니다. 2위 그룹(0.24대)과도 격차가 꽤 큽니다.
- 의료적 해석: 이 모델은 이전 트리가 실수한 오차(잔차)를 다음 트리가 집중적으로 고치는 방식입니다. 응급실 데이터에 섞인 불규칙한 노이즈(갑작스러운 사고 등)를 가장 예민하게 잘 학습했다는 뜻입니다. 현업에 배포한다면 이 모델을 선택해야 합니다.
3. XGBoost가 항상 만능은 아니다?
- 보통 캐글의 제왕이라 불리는 XGBoost(0.2443)가 이번에는 Random Forest(0.2434)나 Gradient Boosting보다 살짝 낮은 점수를 기록했습니다.
- 이유: XGBoost는 데이터가 아주 많거나(Big Data), 하이퍼파라미터를 섬세하게 튜닝했을 때 진가를 발휘합니다. 기본 설정(Default) 상태이거나 데이터 규모에 따라서는 가볍고 강력한 Gradient Boosting이 더 나을 수도 있습니다.
- 교훈: "유명한 모델이라고 무조건 믿지 말고, 내 데이터로 직접 테스트(Model Selection) 해봐야 한다"는 분석가의 철칙을 다시 한번 확인했습니다.
단순한 선형 예측보다는 시간의 흐름과 패턴을 읽을 줄 아는 'Gradient Boosting' 모델을 사용하여, 병상 가동 계획을 세우는 것이 가장 효율적입니다.
'Study Note > Machine Learning' 카테고리의 다른 글
| 정답 없는 환자 데이터에서 그룹 찾기: 군집화(Clustering) 알고리즘 완전 정복 (0) | 2025.12.29 |
|---|---|
| 의료 AI의 협진 시스템: Random Forest와 XGBoost 섞어 쓰기 (Blending) (0) | 2025.12.29 |
| 응급실 예측, 디테일의 차이: Metric 정의부터 타겟 분리(Target Splitting)까지 (0) | 2025.12.23 |
| 응급실은 언제 붐빌까? : 시계열 회귀, 로그(Log)의 마법, 그리고 4대 평가지표 (0) | 2025.12.23 |
| 수치 예측의 정석과 과적합을 막는 방패: 회귀(Regression)와 규제(Regularization) 정복하기 (0) | 2025.12.22 |