--- language: - ko pipeline_tag: token-classification tags: - korean - ner - pii - kpfbert license: mit base_model: KPF/KPF-bert-ner datasets: - townboy/korean-pii-dataset metrics: - precision - recall - f1 model-index: - name: KPF-BERT Korean PII NER results: - task: type: token-classification name: Korean PII NER dataset: name: Private synthetic final holdout type: synthetic metrics: - type: f1 value: 0.8642728407 name: Micro F1 - type: precision value: 0.7965755175 name: Micro precision - type: recall value: 0.9445454545 name: Micro recall --- # KPF-BERT Korean PII NER 한국어 문장에서 33종의 개인정보(PII)를 문자 span 단위로 탐지하도록 `KPF/KPF-bert-ner`를 파인튜닝한 BERT 토큰 분류 모델입니다. ## 모델 개요 | 항목 | 값 | |---|---| | 기반 모델 | `KPF/KPF-bert-ner` | | 구조 | BERT token classification | | 출력 | BIO 태깅 | | PII 유형 | 33 | | BIO 라벨 | 67 (`O` 포함) | | 최대 입력 길이 | 512 토큰 | | 학습 데이터 | `townboy/korean-pii-dataset` | 전체 라벨 매핑은 `config.json`과 `label_map.json`에서 확인할 수 있습니다. 실제 학습된 파라미터는 `model.safetensors`에 들어 있습니다. ## 라이선스 및 출처 이 저장소의 파인튜닝 결과물과 함께 제공되는 메타데이터·후처리 코드는 MIT License로 제공합니다. 모델은 `KPF/KPF-bert-ner`를 기반으로 파인튜닝했습니다. KPF 원본 프로젝트의 라이선스와 출처를 함께 확인해야 하며, 원본 KPF-BERT 프로젝트는 MIT License를 표시하고 있습니다. 따라서 이 파인튜닝 모델은 MIT License 조건에 따라 사용·수정·재배포·상업적 이용이 가능합니다. 재배포 시 이 저장소의 `LICENSE`와 `NOTICE`, 그리고 기반 프로젝트의 저작권·라이선스 고지를 함께 유지해야 합니다. - 기반 모델: [KPF/KPF-bert-ner](https://huggingface.co/KPF/KPF-bert-ner) - 원본 프로젝트 및 라이선스: [KPF-bigkinds/BIGKINDS-LAB](https://github.com/KPF-bigkinds/BIGKINDS-LAB) - 이 저장소의 라이선스 전문: `LICENSE` `KPF/KPF-bert-ner` Hugging Face 카드에는 별도의 라이선스 메타데이터가 표시되지 않으므로, 배포·상업적 이용 전에는 기반 모델의 최신 조건을 직접 확인해야 합니다. 이 저장소의 라이선스 표시는 제가 추가한 파인튜닝 산출물에 대한 것이며, 기반 모델의 권리를 대체하지 않습니다. ## License The original fine-tuning artifacts, metadata, and auxiliary code in this repository are released under the MIT License. See [`LICENSE`](./LICENSE) and [`NOTICE`](./NOTICE). This model is a derivative of `KPF/KPF-bert-ner`; users must also comply with the applicable terms of the upstream model. Accordingly, this fine-tuned model may be used, modified, redistributed, and used commercially under the MIT License, provided that the copyright and license notices in `LICENSE`, `NOTICE`, and the upstream projects are retained. ## 사용법 ```python from transformers import AutoModelForTokenClassification, AutoTokenizer, pipeline model_id = "townboy/kpfbert-ner" tokenizer = AutoTokenizer.from_pretrained(model_id) model = AutoModelForTokenClassification.from_pretrained(model_id) ner = pipeline( "token-classification", model=model, tokenizer=tokenizer, aggregation_strategy="simple", ) text = "회원 이름은 홍길동이고 이메일은 hong@example.com입니다." print(ner(text)) ``` 512 토큰을 넘는 입력은 문장 경계나 겹치는 window 단위로 나눠 추론한 뒤 원문 위치로 합쳐야 합니다. 단순히 뒷부분을 잘라내면 해당 부분의 PII를 탐지할 수 없습니다. ### 주민등록번호와 외국인등록번호 정규화 모델은 성별·세기 코드가 문맥 단어와 충돌하는 counterfactual 예시도 학습했습니다. 그래도 구조가 명확한 13자리 번호는 확률 모델에만 맡기지 않고 7번째 숫자로 최종 라벨을 정규화하는 것이 안전합니다. `korean_id_postprocess.py`가 전체 span을 합치고 코드 `1`~`4`를 `RRN`, `5`~`8`을 `ALIEN_NUMBER`로 보정합니다. ```python from korean_id_postprocess import normalize_korean_id_entities raw_entities = ner(text) entities = normalize_korean_id_entities(text, raw_entities) ``` 이 정규화는 번호 종류만 판별합니다. 실제 유효성은 별도의 주민등록번호·외국인등록번호 체크섬 검증을 함께 적용해야 합니다. 직접 모델 회귀 테스트에서는 문맥 단어가 번호 코드와 충돌하는 4개 사례를 모두 통과했고, 체크섬이 유효한 무라벨 명단의 이름·번호 8개도 모두 탐지했습니다. 499토큰 입력 끝의 번호 역시 탐지했습니다. 상세 결과와 의도적으로 잘못 만든 번호에 대한 한계는 `korean_id_regression_results.json`에 기록했습니다. ## 학습 - 학습 문서: 9,227 - Validation 문서: 1,510 - 최종 counterfactual 정제: 1 epoch, learning rate `5e-6` - 일반 라벨 안정화: 1 epoch, learning rate `1e-6` - Effective batch size: 32 - 최대 길이: 512 - Loss: standard cross-entropy - Validation micro-F1: `0.996463` 마지막 정제 학습은 class-weighted loss가 아니라 표준 cross-entropy를 사용했습니다. 이는 모델 가중치가 빠졌다는 의미가 아닙니다. 학습된 모델 가중치는 `model.safetensors`에 있으며, class weight는 학습 중 loss에만 적용되는 선택 설정입니다. 상세 값은 `training_config.json`과 `training_provenance.json`에 있습니다. 저장소의 `class_weights.json`은 이전 파일 경로를 사용하는 코드가 혼동하지 않도록 남겨 둔 호환성 안내 파일입니다. 최종 모델 학습에는 class weight를 적용하지 않았습니다. ## 최종 평가 방법 공개 학습 corpus 및 임계값 보정 세트와 템플릿·값이 겹치지 않는 별도 합성 holdout 1,320문서를 사용했습니다. - 라벨별 정답 PII span: 100개 - 전체 정답 span: 3,300개 - 매칭: 라벨·문자 시작·문자 끝이 모두 같은 exact span - Confidence: span을 구성하는 토큰 확률의 최솟값 - 평가 입력 최대 길이: 512 - 이 holdout은 최종 모델에 한 번만 사용했습니다. | 지표 | Precision | Recall | F1 | |---|---:|---:|---:| | Macro | 0.8435 | 0.9445 | 0.8834 | | Micro | 0.7966 | 0.9445 | 0.8643 | 이 점수는 문장당 PII 하나와 짧은 정형 문장 중심의 validation 점수보다 훨씬 엄격한 조건에서 측정했습니다. 표·목록·CSV·JSON, 라벨 안내어가 없는 문장, 여러 사람과 여러 PII가 함께 등장하는 긴 문서를 포함합니다. ## 라벨별 최종 성능 | Label | Precision | Recall | F1 | |---|---:|---:|---:| | ACCOUNT_NUMBER | 0.821 | 0.870 | 0.845 | | ADDRESS | 0.874 | 0.900 | 0.887 | | AGE | 0.893 | 1.000 | 0.943 | | ALIEN_NUMBER | 0.907 | 0.970 | 0.937 | | BIRTHDATE | 0.980 | 1.000 | 0.990 | | BLOOD_TYPE | 0.672 | 0.860 | 0.754 | | CARD_NUMBER | 0.870 | 1.000 | 0.930 | | CITY | 1.000 | 1.000 | 1.000 | | DEPARTMENT | 0.958 | 0.920 | 0.939 | | DRIVER_LICENSE | 0.833 | 0.900 | 0.865 | | EMAIL | 0.926 | 1.000 | 0.962 | | EMPLOYEE_ID | 0.633 | 1.000 | 0.775 | | GENDER | 0.908 | 0.990 | 0.947 | | HEIGHT | 0.882 | 0.970 | 0.924 | | IP_ADDRESS | 0.797 | 0.940 | 0.862 | | MAJOR | 0.893 | 1.000 | 0.943 | | MEMBER_ID | 0.887 | 0.940 | 0.913 | | NAME | 0.798 | 0.990 | 0.884 | | NATIONALITY | 1.000 | 1.000 | 1.000 | | NICKNAME | 0.262 | 0.710 | 0.383 | | PARTICIPANT_ID | 0.405 | 0.980 | 0.573 | | PASSPORTNUM | 0.990 | 0.990 | 0.990 | | PHONE | 0.990 | 1.000 | 0.995 | | POSITION | 0.786 | 0.990 | 0.876 | | RELIGION | 1.000 | 0.800 | 0.889 | | RRN | 0.912 | 0.930 | 0.921 | | SCHOOL | 0.980 | 1.000 | 0.990 | | URL | 0.826 | 0.900 | 0.861 | | USER_ID | 0.715 | 0.880 | 0.789 | | VEHICLE_NUMBER | 0.807 | 0.880 | 0.842 | | WEIGHT | 0.883 | 0.980 | 0.929 | | WORKPLACE | 0.907 | 0.980 | 0.942 | | ZIPCODE | 0.841 | 0.900 | 0.870 | 계산에 사용한 TP/FP/FN과 전체 소수점 값은 `per_label_metrics.json`, 원시 평가 결과는 `raw_evaluation_results.json`에 있습니다. ## Confidence 임계값 `label_thresholds.json`과 `threshold_policy.json`에는 별도의 합성 calibration 세트 3,960문서에서 계산한 라벨별 low/high 값이 있습니다. 이 값은 모델 F1 자체가 아니라 예측 confidence를 후속 처리할 때 참고하는 보수적 정책입니다. 기존 경로인 `label_thresholds_calibration_v1.json`은 삭제하지 않고 새 파일 위치를 알려 주는 호환성 안내 파일로 유지합니다. 실제 임계값은 반드시 `label_thresholds.json` 또는 `threshold_policy.json`을 사용해야 합니다. 초기 정책에서는 low 미만 후보도 자동 폐기하지 않습니다. Calibration에서 엄격한 조건을 통과한 `EMAIL`, `NATIONALITY`, `SCHOOL`만 `0.999` 이상에서 자동 채택 대상으로 표시하고, 나머지는 추가 검토가 필요한 후보로 둡니다. 새 독립 최종 holdout에서 이 자동 채택 구간의 FP는 0건이었습니다. 운영 분포에서 로그와 정답이 충분히 쌓이기 전까지 low 값만 보고 탐지 후보를 폐기하는 것은 권장하지 않습니다. ## 한계 - 학습 및 평가 데이터가 합성이므로 실제 개인정보, RAG 응답, OCR 문서, 업무 도메인의 성능을 보장하지 않습니다. - `NICKNAME`, `PARTICIPANT_ID`, `BLOOD_TYPE`, `EMPLOYEE_ID`, `USER_ID`는 최종 holdout에서 상대적으로 낮았습니다. 특히 `NICKNAME`의 raw F1은 `0.383`이므로 자동 확정에 사용하면 안 됩니다. - 이름·소속·직급처럼 형태가 고정되지 않은 PII는 문맥과 도메인의 영향을 많이 받습니다. - 정규식·체크섬으로 판별 가능한 구조적 PII는 NER 결과와 별도로 검증하는 것이 좋습니다. - 512 토큰보다 긴 문서는 window 추론이 필요합니다. - 모델 하나만으로 개인정보 보호나 법적 준수를 보장할 수 없습니다.