10만 건의 당뇨병 진료 기록을 활용한 누출(Leakage) 인식 재입원 예측 모델 구축
요약
본 기사는 당뇨병 환자의 재입원 예측 모델을 구축하는 과정을 다루며, 데이터 누출(Leakage) 방지 및 평가 설계의 중요성을 강조합니다. 특히 진료 기록 단위로 데이터를 파악하고, 단순 무작위 분할 시 발생하는 정보 유출 문제를 해결한 것이 핵심입니다.
핵심 포인트
- 재입원 예측은 모델 적합보다 '무엇을' 예측할지 정의하는 것이 더 중요하다.
- 단순 무작위 분할 대신 환자 단위로 데이터를 분리하여 누출(Leakage)을 방지해야 한다.
- 임상적 의미를 보존하기 위해 결측값 처리 시 데이터 로딩 방법을 신중하게 설계해야 한다.
scikit-learn을 사용하여 병원 재입원 예측 시 결과 정의, 환자 그룹 분할 및 정직한 지표에 대해 배운 점.
문제점
병원 재입원은 의료 시스템에 비용이 많이 들고 환자에게 해롭습니다. 당뇨병 환자의 경우, 병원으로의 재방문은 종종 혈당 조절, 약물 조정 또는 추적 관찰의 격차를 시사합니다. 만약 병원이 퇴원 전에 어떤 환자가 돌아올 가능성이 가장 높은지 식별할 수 있다면, 부족한 추적 관찰 자원을 가장 필요한 곳에 집중할 수 있습니다. 저는 UCI Machine Learning Repository의 공개 데이터셋인 Diabetes 130-US Hospitals (1999년과 2008년 사이에 130개 미국 병원의 101,766건 입원 진료 기록)를 사용하여 이러한 모델을 처음부터 끝까지 구축하는 것을 목표로 했습니다.
저의 임상 교육은 초기에 한 가지 사실을 분명히 했습니다. 어려운 부분은 모델을 적합(fitting)시키는 것이 아니었습니다. 무엇을, 누구를 위해, 언제 예측할지 결정하고, 평가가 실제로 모델이 어떻게 사용될지를 반영하도록 만드는 것이었습니다.
1단계: 행(Row) 파악하기
각 행은 환자가 아니라 병원 진료 기록입니다. 101,766건의 진료 기록은 71,518명의 환자에게 속하며, 재입원한 환자들이 거의 절반에 가까운 행(46.2%)을 차지합니다. 이 단 하나의 사실이 평가 설계를 바꿉니다. 같은 사람의 진료 기록이 학습 세트와 테스트 세트에 모두 포함되면, 모델은 일반화 가능한 패턴을 학습하는 대신 그 사람 자체를 부분적으로 인식할 수 있습니다. 제가 확인한 결과, 단순 무작위 분할(naive random split)에서는 6,791명의 환자가 두 세트에 모두 배치되었고, 테스트 진료 기록의 **41.6%**는 모델이 이미 본 사람에게 속했습니다.
2단계: 문서화된 대로 데이터 로드하기
결측값(Missing values)은 ?로 코딩되었습니다. 하지만 HbA1c와 glucose 열에서 'None'이라는 텍스트는 _검사가 수행되지 않았음_을 의미하며, 이는 실제 임상 범주입니다. pandas는 기본적으로 'None'을 결측값으로 조용히 변환합니다. 제 데이터에서는 이로 인해 96,420개의 '수행 안 됨(not performed)' glucose 결과가 모두 지워졌습니다. na_values=['?']와 keep_default_na=False를 사용하여 로드함으로써 임상적 의미를 온전히 보존할 수 있었습니다. 또한, 추가적인 '결측성'은 정수 코드로 숨겨져 있었습니다(입원 유형 5, 6, 8은 사용 불가, NULL 및 매핑되지 않음을 의미하며, 전체 사례의 10.2%를 차지합니다). 저는 이들을 데이터셋 자체의 ID 매핑 파일을 사용하여 결측값으로 매핑했습니다.
단계 3: 결과(outcome)와 예측 시점 정의
readmitted 열은 세 가지 값(<30일 이내), (>30일 후), 그리고 'NO'를 가집니다. 저는 두 개의 별도 이진 결과를 만들었고, 이를 절대 혼합하지 않았습니다. 즉, 모든 재입원(1차, 유병률 47.1%)과 30일 재입원(2차, 11.4%)입니다. 예측 시점은 퇴원 전 입원 기간의 끝으로 고정했습니다. 사망 또는 호스피스로 끝난 2,423개의 사례는 제거했습니다. 데이터 분석 결과, 사망으로 끝난 모든 1,652개 사례가 'NO'로 레이블링되어 있었는데, 이는 모델에게 무의미한 규칙을 학습시킬 수 있었습니다.
단계 4: 누출(leakage) 검토, 열별로
타겟 누출(Target leakage)은 의료 모델의 조용한 살인자입니다. 저는 모든 변수에 대해 짧은 근거를 작성했습니다. 퇴원 처분(Discharge disposition)은 코호트 정의에만 사용되었을 뿐, 예측 변수로는 사용되지 않았습니다. 왜냐하면 이는 입원 기간의 아주 마지막에 결정되기 때문입니다. 지불자 코드(Payer code, 청구 데이터, 40% 결측)와 체중(weight, 97% 결측)은 제외되었습니다. 주 진단 코드는 퇴원 전에 근무 진단이 알려져 있다는 명시적 가정 하에 아홉 개의 광범위한 임상 범주로 그룹화되었습니다. 종종 퇴원 후 코더에 의해 최종 확정되는 이차 진단 코드(Secondary diagnosis codes)는 첫 번째 모델에서 제외되었습니다. 어떤 금지된 열이라도 피처 행렬(feature matrix)에 들어오면 노트북이 멈추도록 단언문(assertion)을 설정했습니다.
단계 5: 파이프라인 및 환자 그룹 분할(patient-grouped split)
전체 환자를 80/20 학습(train)/테스트(test) 분할에 할당하면서 재입원율을 균형 있게 유지하기 위해 StratifiedGroupKFold를 사용했고 (두 그룹 모두에서 0.471), 환자 중복이 없음을 확인했습니다. 모든 학습된 전처리 과정은 scikit-learn의 Pipeline 내부에 ColumnTransformer와 함께 구현되었습니다: 중앙값 대체(median imputation), 표준 스케일링(standard scaling), 그리고 희귀 카테고리 풀링을 사용한 원-핫 인코딩(min_frequency=100, handle_unknown='infrequent_if_exist')입니다. 교차 검증 과정 중에도 테스트 데이터에 아무것도 적합(fit)시키지 않았습니다.
outer_split = StratifiedGroupKFold(n_splits=5, shuffle=True, random_state=42)
train_idx, test_idx = next(outer_split.split(X, y, groups))
assert not set(groups.iloc[train_idx]) & set(groups.iloc[test_idx]) # 공유 환자 없음
6단계: 기준 모델(Baselines) 및 실제 모델(Models)
대부분 클래스 기반의 기준 모델은 재입원한 환자를 식별하지 못하는 상태에서 약 52.9%의 정확도에 도달했습니다. 이는 정확도가 임상 지표가 아님을 상기시켜주는 유용한 예시입니다. 로지스틱 회귀(Logistic regression)는 유병률 0.471 대비 테스트 ROC-AUC가 0.679 (95% 환자 수준 부트스트랩 신뢰 구간 CI 0.670–0.687) 및 PR-AUC가 0.651을 달성했습니다. 학습 데이터에 대한 교차 검증은 매우 안정적이었습니다 (0.672 ± 0.003). 히스토그램 그래디언트 부스팅(histogram gradient-boosting) 모델은 약 10초의 학습 비용으로 ROC-AUC 0.691 (95% CI 0.683–0.700)에 도달했습니다.
부스팅이 로지스틱 회귀보다 의미 있게 성능을 개선했는가? 그다지 그렇지 않았습니다. 신뢰 구간(CI)들이 겹치고, AUC의 이득은 0.013이며, 동일한 민감도(sensitivity)에서 특이도를 단지 2.3 퍼센트 포인트만 향상시켰기 때문입니다. 신호는 하나의 특징, 즉 지난 해 입원 횟수라는 대략적으로 단조로운(monotonic) 특징에 의해 지배됩니다. 이 특징을 무작위로 섞으면 ROC-AUC가 0.075 떨어지는데, 이는 다른 어떤 특징보다 약 네 배나 큰 수치입니다. 저는 투명성 때문에 로지스틱 회귀를 유지했습니다.
7단계: 임계값(Thresholds) 및 보정(Calibration)
저는 테스트 세트가 아닌 폴드 외부(out-of-fold) 학습 예측을 사용하여 결정 임계값(decision threshold)을 설정했습니다. 이때 '최소 70% 민감도(sensitivity)에 도달하는 가장 높은 임계값'이라는 규칙을 사용했고, 그 결과는 0.428이었습니다. 이 임계값에서 테스트 민감도는 0.697, 특이도(specificity)는 0.547, 정밀도(precision)는 0.579였으며, 환자의 56.8%를 플래그 지정했습니다. 이 규칙은 학습 데이터(70.0%)에서 새로운 환자군(69.7%)으로 거의 완벽하게 전이되었습니다. 솔직히 말하자면, 10명 중 7명의 재입원을 포착하려면 퇴원한 모든 환자의 절반 이상을 추적 관찰해야 한다는 의미입니다.
보정 곡선(calibration curve)은 대각선을 따라 붙어 있었습니다. 평균 예측 위험도는 0.469로 관측된 값인 0.471과 비슷했고, 보정 기울기(calibration slope)는 1.05였으며, 모든 십분위수(decile)가 관측 비율에서 5% 포인트 이내에 있었습니다. 퇴원 팀에게 보정은 중요합니다: '60% 위험'이라는 라벨은 대략 10명 중 6명의 환자가 돌아올 것임을 의미해야 합니다. 여기서는, 적어도 이 인구 집단에서는 그렇습니다.
다른 초보자에게 해주고 싶은 말
- 알고리즘보다 데이터와 질문에 첫 시간을 투자하세요.
- 특징(feature)을 선택하기 전에 예측 시점을 기록하세요.
- 환자가 반복되는 경우 환자별로 분할하세요.
- 학습된 모든 변환(transformation)을 파이프라인(pipeline) 안에 넣으세요.
- 정확도(accuracy)만 보고하지 말고, PR-AUC, 보정 결과 및 임계값 표를 보고하세요.
- 모델이 할 수 없는 것들에 대해 명시적으로 밝히세요. 제 모델은 1999년~2008년의 미국 데이터를 기반으로 학습되었으므로 케냐에서 지역 검증 없이 환자 치료에 사용되어서는 안 됩니다.
전체 코드, 그림 및 보고서는 GitHub에 있습니다: https://github.com/Mwaivictor/diabetes-readmission-prediction. 데이터셋: Clore, Cios, DeShazo and Strack (2014), UCI Machine Learning Repository, CC BY 4.0 (DOI 10.24432/C5230J). 원본 연구: Strack et al. (2014), BioMed Research International.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기