이 프로젝트는 Python의 transformers 라이브러리를 사용하여 로컬 환경에서 실행되는 ChatGLM-6B 대형 언어 모델을 활용해 환자의 진술 내용을 바탕으로 의료 기록을 자동으로 작성하는 기능을 구현합니다. 입력은 환자가 직접 기술한 증상 및 병력 정보이며, 출력은 JSON 형식의 구조화된 의료 기록입니다.
시스템 목적
환자가 병원에서 진료를 받기 위해 접수할 때, 먼저 자신의 상태를 간단히 서술하면 시스템이 해당 텍스트에서 필요한 항목들을 추출하여 표준화된 형태의 의료 기록을 자동 생성하고 이를 의사에게 전달함으로써 초기 진료 효율성을 높이는 것이 목표입니다.
JSON 출력 구조
생성되는 JSON 객체는 다음 필드를 포함합니다:
- 이름: 환자의 성명
- 나이: 환자의 연령
- 주요 증상: 현재 경험 중인 주요 불편 사항
- 지속 기간: 증상이 지속된 시간
- 과거 병력: 이전에 앓았던 질병 이력
- 알레르기 약물: 특정 약물에 대한 알레르기 반응 여부
만약 특정 정보가 제공되지 않은 경우, 해당 필드에는 ["미제공"] 값을 할당합니다.
예시
입력: '저는 김태희이고, 올해 35살입니다. 지난 일주일 동안 두통이 계속되고 있고, 과거에 편두통을 앓은 적 있으며, 페니실린에 알레르기가 있습니다.'
출력:
{
"이름": "김태희",
"나이": "35살",
"주요 증상": "두통",
"지속 기간": "일주일",
"과거 병력": "편두통",
"알레르기 약물": "페니실린"
}
해당 데이터는 웹 클라이언트로 전송되어 각각의 칸에 자동으로 매핑됩니다.
구현 단계
1. 필요 라이브러리 임포트
re: 정규 표현식을 통한 문자열 처리json: JSON 직렬화/역직렬화transformers: 사전 학습된 언어 모델과 토크나이저 사용
2. 스키마 정의 및 샘플 준비
medical_schema = ["이름", "나이", "주요 증상", "지속 기간", "과거 병력", "알레르기 약물"]
ie_template = '다음 문장에서 "{}" 항목에 해당하는 정보를 추출하여 JSON 형식으로 반환하십시오. 없을 경우 ["미제공"]으로 표기하세요.'
examples = {
"case_001": {
"text": "홍길동, 40세, 하루 전부터 발열이 있음. 과거 결핵 병력 존재하며 현재 완치됨. 항생제 알레르기 없음.",
"expected": {
"이름": "홍길동",
"나이": "40세",
"주요 증상": "발열",
"지속 기간": "하루",
"과거 병력": "결핵",
"알레르기 약물": "미제공"
}
}
}
3. 프롬프트 구성 함수
def build_prompt():
prompt_history = []
fields_description = ", ".join(medical_schema)
for case_id, data in examples.items():
input_text = data["text"]
expected_result = json.dumps(data["expected"], ensure_ascii=False)
formatted_instruction = ie_template.format(fields_description)
full_prompt = f"{formatted_instruction}\n\n입력: {input_text}"
prompt_history.append((full_prompt, expected_result))
return prompt_history
위 함수는 각 샘플에 대해 다음과 같은 형태의 프롬프트를 생성합니다:
다음 문장에서 "이름, 나이, 주요 증상, 지속 기간, 과거 병력, 알레르기 약물" 항목에 해당하는 정보를 추출하여 JSON 형식으로 반환하십시오. 없을 경우 ["미제공"]으로 표기하세요.
입력: 홍길동, 40세, 하루 전부터 발열이 있음...
4. 모델 응답 파싱
대형 언어 모델은 종종 요청 외의 추가 설명이나 비표준 형식으로 결과를 반환할 수 있으므로, 이를 표준 JSON으로 변환해야 합니다.
import re
def parse_response(response):
try:
# JSON 부분만 추출 (정규식 활용)
match = re.search(r"\{.*\}", response, re.DOTALL)
if not match:
raise ValueError("유효한 JSON 형식을 찾을 수 없습니다.")
parsed_data = json.loads(match.group())
return parsed_data
except Exception as e:
print(f"[오류] 응답 파싱 실패: {e}")
return None
5. 실제 추론 수행
from transformers import AutoTokenizer, AutoModel
model_name = "THUDM/chatglm-6b"
tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
model = AutoModel.from_pretrained(model_name, trust_remote_code=True).half().cuda()
def run_inference(patient_statement):
prompts = build_prompt()
context = "\n".join([f"질문: {q}\n답변: {a}" for q, a in prompts])
query = f"{context}\n\n새로운 질문: {patient_statement}"
inputs = tokenizer.encode(query, return_tensors="pt").cuda()
outputs = model.generate(inputs, max_new_tokens=200, do_sample=True, temperature=0.7)
decoded_output = tokenizer.decode(outputs[0], skip_special_tokens=True)
result = parse_response(decoded_output)
return result
6. 테스트 실행
if __name__ == "__main__":
test_input = "저는 박소연이고, 29살입니다. 어제 저녁부터 인후통과 미열이 시작되었습니다. 과거에扁桃腺염 병력이 있고, 항생제에 알레르기가 있습니다."
output_record = run_inference(test_input)
print(json.dumps(output_record, indent=2, ensure_ascii=False))