--- language: - ko license: cc-by-sa-4.0 base_model: klue/roberta-large library_name: onnx tags: - emergency-medical-services - information-extraction - korean - onnx - int4 --- # MF_BERT — 119 구급대-병원 통화 정보 추출 모델 (처치판, 4비트 ONNX) 119 구급대와 병원 응급실 사이의 통화 텍스트를 넣으면, 이송할 환자 수와 환자마다 다음 15개 항목을 뽑습니다. | 항목 | 형식 | |---|---| | `gender` | 남성 / 여성 / 판단 보류 | | `age_group` | 10대 미만 ~ 90대이상 / 판단 보류 | | `cc_major`, `cc_minor` | 주증상 대분류 17개 · 소분류 169개(Pre-KTAS 주증상 목록) / 판단 보류 | | `avpu` | A / V / P / U / 기타 / 언급 없음 | | `sbp` `dbp` `hr` `rr` `spo2` `temp` `glucose` `gcs` | 통화에서 말한 수치(숫자), 말하지 않았으면 null | | `treatments` | 구급대원이 한(하는 · 할) 처치를 말한 원문 구절 목록, 없으면 `[]` | | `treat_categories` | 처치 범주 28개 중 있다고 고른 것의 키 목록(아래 표 순서), 없으면 `[]` | ```json {"patient_count": 1, "patients": [{"gender": "남성", "age_group": "60대", "cc_major": "I 심혈관계", "cc_minor": "흉통(심장성)", "avpu": "A", "sbp": 150, "dbp": 90, "hr": 110, "rr": null, "spo2": 92, "temp": null, "glucose": null, "gcs": null, "treatments": ["산소 마스크로 10리터 주고 있고", "정맥로 잡아서 생리식염수 들어가고 있습니다"], "treat_categories": ["oxygen", "iv", "fluid"]}]} ``` **KTAS 등급은 예측하지 않습니다.** `v3-2026-10-20-rtn`에 `treatments`·`treat_categories` 두 항목이 더해졌고, 앞의 13개 항목은 형식이 같습니다. `v2-2026-10-02` 이하와는 출력 형식이 완전히 다릅니다. > **판단 보조용입니다.** 합성 데이터와 AI 라벨로 학습했습니다. ### 처치 범주 | 묶음 | 키 | 내용 | |---|---|---| | 기도·호흡 | `oxygen` | 산소 투여 (비강 캐뉼라, 마스크, 비재호흡 마스크) | | | `suction` | 흡인 | | | `foreign_body` | 기도 이물 제거 (하임리히, 등 두드리기, 입안 이물 꺼냄) | | | `airway_basic` | 도수 기도 확보, 구인두·비인두 기도유지기 | | | `airway_advanced` | 성문상 기도기 (i-gel, LMA), 기관삽관 | | | `ventilation` | 양압 환기 (BVM, 인공호흡기) | | 순환·소생 | `cpr` | 가슴압박 (수동, 기계식) | | | `defib` | 제세동 · 심율동전환 (쇼크를 준 경우) | | | `iv` | 정맥로 · 골내 확보 | | | `fluid` | 수액 투여 | | | `bleeding` | 지혈 (직접 압박, 거즈로 누름, 압박 드레싱, 지혈대) | | 약물 | `med_aspirin` | 아스피린 | | | `med_nitro` | 니트로글리세린 | | | `med_glucose` | 포도당 · 당 공급 | | | `med_epinephrine` | 에피네프린 | | | `med_bronchodilator` | 기관지확장제 (네뷸라이저, 흡입기) | | | `med_other` | 그 밖의 모든 약 | | 고정 | `spinal` | 경추 보호대, 척추고정판 · KED · 스쿠프 들것 | | | `splint` | 팔다리 부목 · 삼각건, 골반 고정대 | | 상처·노출 | `wound` | 소독, 드레싱, 거즈 · 붕대로 덮거나 감기 | | | `burn` | 화상 냉각 · 드레싱 | | | `irrigation` | 오염물 세척 (눈 · 피부 세척, 입 헹굼) | | | `cold_pack` | 국소 냉찜질 | | 체온 | `cooling` | 전신 냉각 (고체온) | | | `warming` | 보온 (담요, 핫팩, 히터, 젖은 옷 벗김) | | 기타 | `position` | 치료 목적의 자세 (회복자세, 다리 올림, 반좌위, 임신부 왼쪽으로 눕힘) | | | `delivery` | 분만 처치 | | | `other` | 그 밖의 처치 (반지 · 벌침 제거, 안대, 억제대, 미주신경 자극 등) | ## 구조 - 인코더: `klue/roberta-large`(전체 미세조정). 512토큰보다 긴 통화는 128토큰씩 겹치게 나눠 인코딩한 뒤 원문 토큰 줄로 이어 붙입니다. - 결정 디코더 2층: 질문 글자("1번째 환자의 성별은?")를 벡터로 만든 질문들이 통화 토큰을 읽습니다. 질문 벡터 하나에 항목 종류별 헤드가 붙습니다. - 선택 항목(성별·나이대·주증상·AVPU): 후보 선택지 중 점수가 가장 높은 것 - 활력징후: 통화 토큰 위에서 수치가 적힌 구간의 시작·끝을 고른 뒤 그 글자를 숫자로 읽습니다 - 처치(`treatments`): 통화 토큰마다 O(밖) · B(구절 첫 토큰) · I(구절 안)를 골라, B·I로 이어진 토큰을 원문 글자로 되돌립니다 - 처치 범주(`treat_categories`): 28개 범주마다 있음 점수(로짓)를 내고, 0보다 큰(확률 0.5 초과) 범주를 고릅니다 - 추론은 두 단계입니다. 환자 수 질문으로 환자 수 n을 정하고, 환자 1..n의 질문 15개씩을 한 번에 넣습니다. - **4비트 양자화 (HQQ)**: 인코더 MatMul 가중치를 블록 32 단위 4비트(onnxruntime MatMulNBits)로 줄였습니다. 블록마다 scale·zero point를 원래 가중치와의 오차가 작아지게 반복 최적화합니다(Half-Quadratic Quantization). 인코더 마지막 4개 층(20~23)과 디코더는 fp32입니다. 보정 데이터는 쓰지 않았습니다. CPU(onnxruntime)에서 돕니다. ## 파일 | 파일 | 내용 | |---|---| | `encoder.onnx` | 인코더. 입력 `input_ids`·`attention_mask` (조각 수, 길이) → 출력 `token_vectors` (조각 수, 길이, 1024) | | `decoder.onnx` + `decoder.onnx.data` | 디코더. 입력 `token_vectors` (1, 토큰 수, 1024)·`question_ids` (1, 질문 수) → `scores` (1, 질문 수, 233), `span_start`·`span_end` (1, 질문 수, 토큰 수 + 2), `tags` (1, 질문 수, 토큰 수, 3), `categories` (1, 질문 수, 28) | | `catalog.json` | 질문 목록(`questions` 436개, 질문 번호 = 1 + (환자 번호 - 1) × 15 + 항목 순서, 0번은 환자 수), 항목 순서(`fields`), 선택지 233개(`options`), 항목별 후보 선택지 번호(`candidates`, 소분류는 대분류별 `cc_minor`), 환자 수 후보(`count`), AVPU 값(`avpu_values`), 활력징후 항목(`span_fields`), 처치 항목(`tag_fields`), 처치 범주 항목(`category_fields`)과 범주 키 순서(`categories`) | | `tokenizer/` | 토크나이저 | 토큰 줄 위치는 [CLS]가 0, 본문 토큰이 1..n, [SEP]가 n+1입니다. - 활력징후 구간 점수의 마지막 두 칸은 "말하지 않음"과 "0"(무호흡·맥박 없음처럼 숫자 글자 없이 0인 경우) 자리입니다. - 처치 태그 점수는 본문 토큰(1..n)에서 읽습니다. 태그 번호는 O=0, B=1, I=2이고, 앞에 B 없이 나온 I도 새 구절로 엽니다. 구간 글자를 숫자로 읽는 규칙(한글 수 읽기)과 조각 나누기 코드는 이 저장소에 들어 있지 않습니다. ## 학습 데이터 - 합성 119 통화 텍스트와 Claude 계열 모델이 붙인 라벨입니다. 사람 검수는 거치지 않았습니다. - 환자 수·성별·나이대·주증상 라벨(활력징후 제외)은 [podongchip/119-call-transcript-synthetic-labels](https://huggingface.co/datasets/podongchip/119-call-transcript-synthetic-labels)로 공개돼 있습니다. 이 모델은 그 뒤에 보강한 데이터와 활력징후·AVPU·처치 라벨을 더해 학습했습니다. - 처치 라벨은 2026-10-29에 Claude Sonnet 5.5가 구절과 범주를 함께 붙였습니다(통화 48,872건, 처치가 있는 환자 20,858명, 항목 41,663개). 300건을 두 번 따로 붙여 대조한 일치율은 95~96%입니다(구절이 겹치고 범주가 같은지 기준). - val을 떼지 않고 전체 데이터로 2 에폭 학습한 마지막 체크포인트입니다. ## 평가 실제 119 통화를 바탕으로 만든 테스트셋(612통화, 환자 617명, 학습에 쓰지 않음)에서 이 4비트 ONNX로 잰 결과입니다. | 항목 | 값 | |---|---| | score (환자 수 · 성별 · 나이대 · 주증상 대분류 · 소분류 · AVPU 정확도 평균) | 0.845 | | 환자 수 / 성별 / 나이대 | 0.997 / 0.976 / 0.942 | | 주증상 대분류 / 소분류 | 0.681 / 0.556 | | AVPU | 0.919 | | 활력징후 (8개 정확도 평균) | 0.960 | | 처치 구절 F1 (정밀도 / 재현율) | 0.754 (0.722 / 0.789) | | 처치 범주 micro-F1 (정밀도 / 재현율) | 0.809 (0.948 / 0.705) | | 처치 범주가 정확히 같은 환자 비율 | 0.964 | - 처치 구절은 경계가 라벨러마다 조금씩 달라, 예측 구절이 같은 환자의 정답 구절과 글자가 겹치면 맞은 것으로 셉니다. - 테스트셋에서 처치가 있는 환자는 72명뿐이고 그중 54명이 산소입니다. 28개 범주 중 절반 넘게는 테스트셋에 한 번도 나오지 않아, 범주별 성능은 확인되지 않았습니다(범주 macro-F1 0.417). - 양자화 전 fp32 ONNX와의 차이는 score 0.001, 활력징후 0.001 이내입니다. ## 버전 | revision | 날짜 | 내용 | |---|---|---| | `main`, `v4-2026-10-29-hqq` | 2026-10-29 | 이 모델. 환자별 15개 항목(처치 구절 · 처치 범주 28개 추가), 4비트 ONNX(HQQ) | | `v3-2026-10-20-rtn` | 2026-10-20 | 환자별 13개 항목(활력징후 8개 추가), 여러 환자, 4비트 ONNX | | `v2-2026-10-02` | 2026-10-02 | PyTorch `best.pt`, v2 스키마 17개 필드(KTAS 포함) | | `v1-2026-09-30` | 2026-09-30 | 첫 공개 모델(PyTorch) | 이전 모델은 `snapshot_download("podongchip/MF_BERT", revision="v3-2026-10-20-rtn")`처럼 받습니다. ## 한계 - 학습 데이터와 라벨이 모두 AI가 만든 것입니다. 실제 음성 인식(STT) 출력에서의 성능은 확인되지 않았습니다. - 테스트셋은 실제 통화를 바탕으로 했지만, 보강 데이터가 이 테스트셋의 오답 유형을 참고해 만들어져 점수가 실제보다 높게 나왔을 수 있습니다. - 주증상 분류가 약합니다(대분류 0.68, 소분류 0.56). - 처치 범주는 재현율(0.705)이 정밀도(0.948)보다 낮습니다. 고른 범주는 대체로 맞지만, 말한 처치를 놓칠 수 있습니다. - 성별·나이대 라벨 일부는 이전 MF_BERT가 붙인 것입니다. - 활력징후 수치의 범위 검사(예: 혈압 300 초과 거르기)는 하지 않습니다. ## 라이선스 [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/). 이 모델은 [KLUE](https://github.com/KLUE-benchmark/KLUE)의 `klue/roberta-large`(CC BY-SA 4.0)를 미세조정하고 디코더를 더한 뒤 인코더를 4비트로 양자화한 것입니다. 이 모델을 바탕으로 만든 것은 같은 라이선스로 배포해야 합니다.