RyanNerd
라덕'Story
RyanNerd
  • 분류 전체보기 (117)
    • Study Note (80)
      • Python (3)
      • R (1)
      • Airflow (7)
      • 통계 (26)
      • Machine Learning (16)
      • Streamlit (9)
      • Django (1)
      • Deep Learning (12)
      • dbt (2)
      • SQL (3)
    • 빅데이터분석기사 (1)
      • 필기 (1)
    • Programmers (28)
      • Python (13)
      • SQL (15)
    • Project (3)
      • Django (3)
    • Mac (2)

블로그 메뉴

  • NaverBlog
  • 홈

최근 글

전체 방문자
오늘
어제
hELLO · Designed By 정상우.
RyanNerd

라덕'Story

Study Note/Machine Learning

응급실 예측, 디테일의 차이: Metric 정의부터 타겟 분리(Target Splitting)까지

2025. 12. 23. 12:03

머신러닝 모델링의 후반부는 "평가(Evaluation)"와 "최적화(Optimization)"의 싸움입니다. 단순히 fit()하고 predict()하는 것을 넘어, 비즈니스(의료) 로직을 코드에 녹여내는 과정을 실습해 봅니다.

오늘의 시나리오: "응급실 총 환자 수(Total Count)를 예측하라" 하지만 우리는 알고 있습니다. 긴급하게 실려오는 환자(Ambulance)와 제 발로 걸어오는 환자는 발생하는 패턴이 완전히 다르다는 것을요!

 

1. Metric: 평가 지표 정의 (RMSLE)

먼저, 우리가 목표로 하는 점수판을 정의합니다. 앞서 배운 RMSLE(로그 오차)를 코드로 구현하고, 사이킷런 모델들이 알아들을 수 있도록 scorer로 만듭니다.

import numpy as np
from sklearn.metrics import make_scorer

# 1. RMSLE 계산 함수 (의료 현장의 과소평가 방지용)
def get_rmsle(y_actual, y_pred):
    # 예측값이 음수가 나오면 로그 변환 시 에러가 나므로 0으로 처리
    y_pred = np.maximum(0, y_pred)
    
    # log(x+1) 변환
    log_true = np.log1p(y_actual)
    log_pred = np.log1p(y_pred)
    
    # 오차 제곱의 평균 -> 루트
    squared_error = (log_true - log_pred) ** 2
    return np.sqrt(np.mean(squared_error))

# 2. Scikit-learn용 Scorer 생성
# greater_is_better=False: 오차(Error)는 작을수록 좋다는 뜻
rmsle_scorer = make_scorer(get_rmsle, greater_is_better=False)

print("Metric 준비 완료: RMSLE Scorer")

 

2. Model Evaluation: 타겟 분리 전략 (Target Splitting)

이 부분이 오늘 실습의 하이라이트입니다. 응급실 데이터에서 성격이 다른 두 그룹을 따로 예측해서 합치는 전략을 사용합니다.

  • Ambulance (구급차): 교통사고, 심정지 등 중증 환자. 시간대나 날씨 영향이 큼.
  • Walk-in (도보): 경증 환자, 외래 진료가 끝난 야간/주말에 몰림.

[Code] 분할 예측 함수 구현

def predict_er_total(model, X_train, df_train, X_test):
    """
    모델 하나를 받아서 '구급차'와 '도보'를 각각 학습시키고,
    최종적으로 두 예측값을 더해서 반환하는 함수
    """
    
    # --- 1. 구급차(Ambulance) 예측 ---
    # 타겟을 로그 변환하여 학습
    y_train_amb = np.log1p(df_train['ambulance_count'])
    model.fit(X_train, y_train_amb)
    
    # 예측 후 지수 변환(복원)
    pred_amb_log = model.predict(X_test)
    pred_amb = np.expm1(pred_amb_log)
    
    # --- 2. 도보(Walk-in) 예측 ---
    # 타겟을 로그 변환하여 학습
    y_train_walk = np.log1p(df_train['walkin_count'])
    model.fit(X_train, y_train_walk)
    
    # 예측 후 지수 변환(복원)
    pred_walk_log = model.predict(X_test)
    pred_walk = np.expm1(pred_walk_log)
    
    # --- 3. 최종 합산 (Total = Ambulance + Walk-in) ---
    total_pred = pred_amb + pred_walk
    return total_pred

 

Why? 뭉뚱그려 예측하는 것보다, 각각의 패턴을 전문적으로 학습한 뒤 합치는 것이 훨씬 정확도가 높습니다.

 

3. Model Evaluation Test: 모델별 성능 검증

이제 준비된 Metric과 전략을 바탕으로 실제 모델들을 테스트해 봅니다. 가상의 데이터를 만들어 실험해 보겠습니다.

① 데이터 준비 (Mock Data)

import pandas as pd

# 가상의 데이터 생성 (1000시간)
data_size = 1000
df_er = pd.DataFrame({
    'hour': np.random.randint(0, 24, data_size),
    'dayofweek': np.random.randint(0, 7, data_size),
    'temp': np.random.uniform(-10, 35, data_size)
})

# 타겟 데이터 생성 (패턴이 다름을 가정)
# 구급차: 무작위성이 강함
df_er['ambulance_count'] = np.random.randint(0, 10, data_size) 
# 도보: 시간대 영향을 많이 받음
df_er['walkin_count'] = df_er['hour'].apply(lambda x: np.random.randint(10, 50) if x > 18 else np.random.randint(0, 10))
# 총 환자 수
df_er['total_count'] = df_er['ambulance_count'] + df_er['walkin_count']

# 학습/테스트 분리
train_size = 800
df_train = df_er.iloc[:train_size]
df_test = df_er.iloc[train_size:]
X_train = df_train[['hour', 'dayofweek', 'temp']]
X_test = df_test[['hour', 'dayofweek', 'temp']]

 

② 모델 테스트: Linear Regression vs Random Forest

from sklearn.linear_model import LinearRegression
from sklearn.ensemble import RandomForestRegressor

# 1. 선형 회귀 (Linear Regression) 테스트
lr_model = LinearRegression()
lr_pred = predict_er_total(lr_model, X_train, df_train, X_test)

# 성능 평가 (RMSLE)
lr_score = get_rmsle(df_test['total_count'], lr_pred)
print(f"Linear Regression RMSLE: {lr_score:.4f}")


# 2. 랜덤 포레스트 (Random Forest) 테스트
rf_model = RandomForestRegressor(n_estimators=100, random_state=42)
rf_pred = predict_er_total(rf_model, X_train, df_train, X_test)

# 성능 평가 (RMSLE)
rf_score = get_rmsle(df_test['total_count'], rf_pred)
print(f" Random Forest RMSLE    : {rf_score:.4f}")

 

[실행 결과 해석] 어떤 모델이 '응급실'을 더 잘 이해했을까?

위 코드를 실행하면 다음과 같은 결과가 나옵니다 (이미지 참조).

  • Linear Regression RMSLE: 0.6256
  • Random Forest RMSLE: 0.5817

Insight: 왜 Random Forest가 이겼을까?

  1. 점수의 의미 (Lower is Better):
    • RMSLE는 '오차'이므로 작을수록 좋은 모델입니다.
    • 랜덤 포레스트(0.5817)가 선형 회귀(0.6256)보다 오차가 더 적으므로 성능이 우수합니다.
  2. 데이터의 비선형성 (Non-linearity):
    • 선형 회귀: "시간이 지날수록 환자가 늘어난다" 같은 단순한 직선 관계만 찾습니다.
    • 랜덤 포레스트: "오전 9시엔 확 늘었다가, 오후 2시에 줄었다가, 밤 10시에 다시 늘어난다" 같은 구불구불한(비선형) 패턴을 잘 잡아냅니다.
    • 응급실 데이터는 시간대별로 변동이 심하기 때문에(Peak Time 존재), 직선보다는 나무(Tree) 모델이 훨씬 유리합니다.

결론: 의료 시계열 데이터에서는 단순한 선형 모델보다, 복잡한 패턴을 학습할 수 있는 트리 기반 모델(Random Forest, XGBoost)을 사용하고, 타겟을 세분화(Splitting)하는 전략이 승리의 열쇠입니다.

 

팁

  1. Metric의 역할: get_rmsle를 통해 모델이 얼마나 '비율적으로' 잘 맞췄는지 검증했습니다. 점수가 낮을수록 좋습니다.
  2. Evaluation 전략: 단순히 total_count를 학습시킨 것보다, ambulance와 walkin을 따로 학습시켜 더한 것이, 각 환자군의 특성(심야 시간 급증 등)을 더 잘 반영합니다.
  3. 모델 비교: 보통 시계열 패턴이 복잡한 의료 데이터에서는 단순한 Linear Regression보다 Random Forest나 XGBoost 같은 트리 기반 모델이 더 좋은 성능(낮은 RMSLE)을 보여줍니다.

 

1. Metric의 핵심 정의: "우리가 합의한 성공의 기준"

  • 학교 시험: 학생이 문제를 풀면, 선생님이 "100점 만점에 몇 점인지" 매기죠? 여기서 '점수 계산법'이 바로 Metric입니다.
  • 병원: 의사 선생님이 환자 상태를 볼 때, "건강하다"라고 뭉뚱그려 말하지 않죠? "혈압 120/80, 당화혈색소 5.6" 같은 숫자 기준을 봅니다. 이 기준이 바로 Metric입니다.

즉, Metric이란? "내 AI 모델이 일을 잘했니?"라고 물었을 때, "응, 90점이야" 또는 "아니, 오차가 10명이야"라고 대답해주는 숫자입니다.

 

2. 왜 그냥 '점수'라고 안 하고 Metric이라고 할까요?

머신러닝에서는 "공부하는 방법(Loss Function)"과 "성적 매기는 방법(Metric)"이 다를 수 있기 때문입니다. 이 차이가 헷갈리지만 중요합니다.

  • 손실 함수 (Loss Function):
    • 누가 봄? 모델(기계)이 봅니다.
    • 목적: 정답을 맞히기 위해 학습(미분)할 때 쓰는 수학 공식입니다. (예: logloss, mse)
    • 비유: 학생이 공부할 때 "틀린 문제 오답 노트 쓰면서 외우는 과정".
  • 평가 지표 (Metric):
    • 누가 봄? 사람(분석가, 의사)이 봅니다.
    • 목적: 비즈니스적으로 이 모델이 쓸모 있는지 판단하는 기준입니다. (예: Accuracy, RMSLE, F1-Score)
    • 비유: 기말고사 성적표. "너 공부 어떻게 했든 상관없고, 그래서 몇 점인데?"

3. Metric의 종류 (상황별 채점표)

상황에 따라 채점 기준(Metric)을 바꿔야 합니다.

문제 종류 Metric 예시 의료 데이터 비유
희귀 (숫자 예측) MAE, RMSE, RMSLE "응급실 환자 수 예측 시, 실제랑 몇 명 차이 나냐?"
분류 (O/X 맞추기) Accuracy (정확도) "질환자 100명 중 몇 명 맞췄냐?"
분류(심화) Recall (재현율) "실제 질병환자를 놓치지 않고 다 찾았냐?" (가장 중요)

 

요약

Metric은 모델에게 주는 "시험 채점표"입니다.  분석하는 사람이 채점표를 RMSE로 할지, RMSLE로 할지, 정확도로 할지 상황에 맞게 골라주는 출제위원 역할을 하시는 것

'Study Note > Machine Learning' 카테고리의 다른 글

의료 AI의 협진 시스템: Random Forest와 XGBoost 섞어 쓰기 (Blending)  (0) 2025.12.29
응급실 예측 2편: 로그의 마법과 5대 회귀 모델 총출동 (Linear부터 XGBoost까지)  (0) 2025.12.24
응급실은 언제 붐빌까? : 시계열 회귀, 로그(Log)의 마법, 그리고 4대 평가지표  (0) 2025.12.23
수치 예측의 정석과 과적합을 막는 방패: 회귀(Regression)와 규제(Regularization) 정복하기  (0) 2025.12.22
정확도(Accuracy)의 함정에서 탈출하기: OOB Score와 ROC-AUC 완벽 해설  (0) 2025.12.22
    'Study Note/Machine Learning' 카테고리의 다른 글
    • 의료 AI의 협진 시스템: Random Forest와 XGBoost 섞어 쓰기 (Blending)
    • 응급실 예측 2편: 로그의 마법과 5대 회귀 모델 총출동 (Linear부터 XGBoost까지)
    • 응급실은 언제 붐빌까? : 시계열 회귀, 로그(Log)의 마법, 그리고 4대 평가지표
    • 수치 예측의 정석과 과적합을 막는 방패: 회귀(Regression)와 규제(Regularization) 정복하기
    RyanNerd
    RyanNerd
    라이언 덕후의 일상 스토리~

    티스토리툴바