비정형 텍스트가 최종 보스다: LLM으로 의사 노트를 파싱하기 🏥
Source: Dev.to
Hey devs! 👋
솔직히 말해봅시다. 우리 모두 데이터가 이렇게 생겼다고 생각하는 거죠:
{
"patient_id": 1024,
"symptoms": ["headache", "nausea"],
"severity": "moderate",
"is_critical": false
}
아름답고, 파싱 가능하고, 타입‑안전합니다. 😍
하지만 HealthTech에서 일해본 적이 있거나(또는 레거시 엔터프라이즈 시스템을 스크랩해본 적이 있다면), 현실은 보통 새벽 3 시의 피곤한 사람이 작성한 무서운 자유 텍스트 블록이라는 걸 알게 됩니다.
최근 저는 임상 노트를 표준화하려고 깊은 전쟁터에 몸을 파묻고 있었는데, 의사 노트를 다루는 일은 정규식으로 HTML을 파싱하는 것이 휴가처럼 느껴질 정도입니다.
현실 점검: “Pt c/o…”
의사들은 JSON을 쓰지 않는다. 그들은 약어, 오타, 그리고 약식 표현으로 이루어진 비밀 코드를 사용한다.
실제 “데이터”는 다음과 같다:
“Pt 45yo m, c/o SOB x 2d. Denies CP. Hx of HTN, on lisinopril. Exam: wheezing b/l. Plan: nebs + steroids.”
- “High Blood Pressure”라는 표준 키워드 검색을 실행하면, 의사가 “HTN”(고혈압)이라고 적었기 때문에 이 기록을 전혀 놓칠 수 있다.
- “Pain”(통증)을 검색하면, 기록에 “Denies CP”(흉통 부인)이라고 적혀 있어 false positive가 발생할 수 있다.
전통적인 NLP는 여기서 어려움을 겪는다. 왜냐하면 맥락이 전부이기 때문이다. “SOB”는 병원에서는 “Shortness of Breath”(숨 가쁨)를 의미하지만, Reddit 댓글 섹션에서는 전혀 다른 의미가 될 수 있다. 😂
환각 함정 👻
현대적인 해결책은 종종 이렇게 표현됩니다: “그냥 ChatGPT/LLM에 넣으면 되지?”
음… 예와 아니오.
일반적인 LLM에게 “이 환자의 상태를 요약해 주세요”라고 물으면 꽤 잘 해낼 수 있습니다—하지만 그렇지 않을 때도 있습니다. 의료 AI에서 가장 큰 위험은 환각입니다.
예시: 모델이 “당뇨병 가족력”이라는 메모를 읽고, 구조화된 JSON에 환자가 현재 당뇨병을 가지고 있다고 출력했습니다.
큰 실수. 의료 분야에서는 이런 오류가 용납될 수 없습니다.
Source: …
수정안: RAG + 파인‑튜닝 샌드위치 🥪
AI가 거짓 정보를 제공하지 않으면서 데이터를 쿼리 가능하게 만들기 위해(예: “호흡기 문제가 있는 모든 환자를 보여줘”)는 엄격한 파이프라인이 필요합니다.
1. 파인‑튜닝 (언어 교육)
gpt-3.5-turbo와 같은 기본 모델은 특수 분야의 미묘한 차이를 놓치기 쉽습니다. 작은 모델(예: Llama 3 또는 Mistral)을 의료 텍스트로 파인‑튜닝하면 bid가 “하루에 두 번”(bis in die)을 의미한다는 것을, 경매 제안이 아니라는 것을 모델에게 가르칠 수 있습니다.
2. 구조화된 추출 (번역가)
LLM에게 “채팅”하도록 요청하는 대신, Pydantic이나 Instructor와 같은 도구를 사용해 미리 정의된 스키마로 추출하도록 강제합니다.
import instructor
from pydantic import BaseModel, Field
from openai import OpenAI
# 우리가 원하는 구조 정의 (꿈)
class ClinicalNote(BaseModel):
patient_age: int
symptoms: list[str] = Field(description="신체적 불편 증상의 리스트")
medications: list[str]
diagnosis_confirmed: bool = Field(description="진단이 최종인지, 아니면 추정인지?")
client = instructor.patch(OpenAI())
text_blob = "Pt 45yo m, c/o SOB x 2d. Denies CP. Hx of HTN, on lisinopril."
resp = client.chat.completions.create(
model="gpt-4",
response_model=ClinicalNote,
messages=[
{"role": "system", "content": "You are a medical scribe. Extract data accurately."},
{"role": "user", "content": text_blob},
],
)
print(resp.model_dump_json(indent=2))
출력
{
"patient_age": 45,
"symptoms": ["Shortness of Breath"],
"medications": ["lisinopril"],
"diagnosis_confirmed": false
}
이제 SQL‑쿼리 가능한 데이터가 생겼습니다! 🚀
3. 검증을 위한 RAG (가드레일)
추출만으로는 결과를 완전히 신뢰할 수 없습니다. 원본 노트를 벡터 데이터베이스(예: Pinecone 또는 Weaviate)에 임베딩합니다. 사용자가 “이 환자에게 심장 문제가 있나요?” 라고 물으면 시스템은:
- “Denies CP”와 “Hx of HTN”을 언급한 특정 청크를 검색합니다.
- 그 청크만 LLM에 전달합니다.
- 출처를 인용합니다.
AI가 관련 청크를 찾지 못하면 추측하지 않고 “모르겠습니다” 라고 답하도록 프로그래밍합니다.
결론
자유 텍스트 임상 노트를 표준화하는 것은 고통스럽지만, 의료 기록의 가치를 끌어내는 유일한 방법입니다. 우리는 “마법 같은 블랙‑박스” AI에서 구조화된 AI 파이프라인으로 전환해야 합니다—입력을 검증하고, JSON 스키마를 적용하며, 모든 것을 검색된 컨텍스트에 기반을 두어야 합니다.
지저분한 작업이지만, 누군가는 해야 합니다! 💻✨
더 깊이 들어가고 싶나요?
깊이 있는 내용을 보려면 제 개인 블로그를 확인해 보세요: wellally.tech/blog