이 글은 CPU만으로 돌릴 수 있는 경량 한국어 PII(개인정보) 탐지 모델 Veil-PII-Ko-Lite를 개발한 과정과 결과를 정리한 리포트입니다.
1. 개발 동기#
- 매 프로젝트마다 폐쇄망 환경에서의 PII 모델 적용은 필수 요소. But 아쉬운 한국어 PII 성능
- 폐쇄망에서 GPU 없이 가볍게 구동할 수 있는 PII 모델 필요
- 다양한 한국어 PII Category에 대한 탐지 기능 필요
- 좋은 PII 성능을 보이는 것으로 알려진 모델들을 후보군으로 두고 탐색
| 후보 | 내용 | 적용 한계 |
|---|---|---|
| BCCard MoAI-Privacy-Filter (2026-08) | openai/privacy-filter(1.4B) 한국어 파인튜닝, 29 라벨, Apache 2.0 | 1.4B 규모로 CPU 서빙 부담. 모델카드가 상업 사용 시 고지를 요구. / 실제 대화체(KDPII)에서는 F1 0.45로 낮은 탐지 성능 |
| FrameByFrame privacy-filter-korean (2026-04) | 동일 1.4B 백본, KDPII 학습, 9 라벨 | 주민번호·카드·사업자번호 등 금융 필수 라벨이 없음. |
| Azure AI Language PII | 관리형 API, 한국어 지원 | 한국어 주소 F1 0.08, 계좌 미검출 등 한국어 PII 탐지 성능 낮음 |
| Microsoft Presidio | 규칙 + NER 프레임워크 | 대체재가 아니라 NER 모델을 끼워 쓰는 프레임워크 개념. 한국어 인식 기능이 있긴 하지만 정형 카테고리 5종만 탐지를 지원함. 전후처리에 특화되어 있어 PII 모델과 패키징에 활용 예정 |
결론적으로 “경량 · 한국어 강함 · 카테고리 넓음 · 라이선스 부담 없음” 네 조건을 동시에 만족하는 공개 모델이 없어 직접 개발하기로 하였습니다.
2. 개발 목표#
| 목표 | 정량기준 | 결과 |
|---|---|---|
| 정확도 | 공개 벤치 및 합성데이터 3종의 평가결과가(KDPII test · BCCard validation · 자체 합성 데이터) BCCard PII 모델 이상의 성능을 보일 것 · BCCard의 PII 모델을 목표모델로 설정한 이유는, 해당 모델이 현재 한국어 PII 모델 중 가장 많은 Category를 커버하면서도 높은 성능을 보이기에 (다만 크기가 큼..), 경량 모델로 해당 모델 이상의 성능을 달성하는 것을 목표로 설정하였습니다. | 달성 |
| 경량성 | 파라미터 ≤ 150M, 배포 파일 ≤ 150MB, 정확도 손실 없이 INT8 | 달성 — 110M · 143MB · 무손실 |
| 카테고리 | BCCard가 제공하는(현재 기준 가장 많음, 29개) 카테고리 수준으로 지원할 것 | 달성 — 32종 (차량번호·가입번호·단말 S/N 추가) |
| 라이선스 | 고지 의무 없는 상업 사용 | 달성 — 백본·데이터 모두 Apache 2.0 |
3. Backbone 선택#
작은 크기 및 한국어를 지원하는 PII backbone 모델 5종을 비교분석하였습니다.
후보 5종을 같은 데이터(2종 - KDPII, BCCard val 데이터), 같은 하이퍼파라미터로 2 epoch로 학습하여 비교하였습니다.
최종 판단 기준은 F1 Score 기준 정확도 및 모델의 크기를 가장 중요한 요소로 두어 평가하였고, 최종적으로 ‘ko-electra-base-v3’ 모델을 최종 backbone 모델로 선택하였습니다.
백본에 대해 PII 탐지(라벨을 인식할 수 있도록)가 가능하도록 동일 조건으로 fine-tuning 진행했고, train loss는 동일하게 떨어졌습니다.
다만, validation set에 대해서는 아래와 같이 Ko-Electra-base-v3 모델이 가장 좋은 성능을 보였습니다.
| 백본 | 파라미터 | 라이선스 | KDPII test F1 | BCCard val F1 | 판정 |
|---|---|---|---|---|---|
| monologg/koelectra-base-v3 | 110M | Apache 2.0 | 0.8789 | 0.9613 | 채택 — 가장 작고 KDPII 스코어는 최고 득점 |
| kakaobank/kf-deberta-base | 185M | MIT | 0.8798 | 0.9661 | +0.1~0.5pt 이나 INT8 245MB로 크기 목표 초과 |
| lucid/deberta-v3-base-korean | 185M | Apache 2.0 | 0.8533 | 0.9527 | 탈락 |
| beomi/KcELECTRA-base | 110M | MIT | KoELECTRA 하회 | 탈락 (구어체 특화 이점 확인 안 됨) | |
| microsoft/mdeberta-v3-base | 276M | MIT | exact 0.39 → 오프셋 보정 후 회복 | 탈락 — 크기 2.5배, SentencePiece 선행공백 오프셋 문제로 디코더 보정 필요했음 |
4. 학습 데이터#
학습셋 구성 (행 수, full 셋 기준 · 총 128k)
| 출처 | 행 수 | 비고 |
|---|---|---|
| BCCard | 57,851 | ko 43k · en 15k |
| KDPII | 40,109 문장 + 3,664 대화 | |
| 합성 (자체) | 26,540 | 템플릿 4.7k × 생성기 |
막대 길이 = 행 수 비례. 학습 시 반복 가중 ×2를 쓴 출처(BCCard)는 epoch당 실효 256k 행.
4-1. BCCard/privacy-filter-openpii-masking (CC-BY-4.0) - 약 72,400행#
- 데이터 설명
- 아래 세 가지 출처의 데이터에 대한 통합본으로 확인
openpii-1.5m-ko/none26,268행 (ai4privacy 영문 데이터의 한국어 번역본)openpii-1.5m-ko/perturbation16,268행 (위 데이터를 한국 포맷으로 변경, 생성한 데이터)domain/synthesis11,170행 (BC카드 자체 합성데이터)
- 아래 세 가지 출처의 데이터에 대한 통합본으로 확인
<데이터 샘플>
원문 : 연락을 위해 jimin3292@webmail.example.net 또는 공오일 일일구 공육육육 로 문의해 주세요.
정답 : EMAIL jimin3292@webmail.example.net · PHONE 공오일 일일구 공육육육
수집 사유
- 한국어 PII Category 29개의 라벨을 기준으로, 공개된 가장 큰 데이터이며, 비교 대상(BCCard 모델)과 같은 분포로 학습 후 평가해야 BCCard validation 비교가 공정해지기에, 해당 데이터를 사용하게 되었습니다.
데이터 품질 검수
- 학습 이전에, 데이터 품질이 적합한지에 대한 전수 검사를 실시
- 탐지된 문제점
- 주민번호·전화·카드·면허·여권·사업자번호 등 21종 포맷 검증기로 행 단위 감사(정규표현식)를 수행했으며, 세 하위 출처 중
openpii-1.5m-ko(ai4privacy 번역본 21k)는 35% 행이 불량이었음- 운전면허 라벨의 61%가
0W4SB7XAR4같은 랜덤 대문자열 - 이름의 13%가
선임Blattnig같은 혼합 문자. - 한국 포맷으로 변경 전 데이터라, 당연한 결과로 보이며 BC Card에서 해당 데이터를 왜 학습에 포함했는지 이유는 모르겠으나, 해당 데이터 포함 시 모델 성능이 하락하는 것을 확인하였고 포맷검증기에서 확인된 문제 데이터는 모두 제거함.
- 운전면허 라벨의 61%가
- BCCard 모델 Description에서 언급된, 스스로 약하다고 밝힌 ACCOUNT_NUMBER·ZIPCODE·PORT의 원인이 “랜덤 문자열 = 식별자” 데이터를 제거하지 않고 학습한 결과로 예상.
- 처리: 불량 행을 제외한 clean 셋과 전체 full 셋을 모두 만들어 절제 실험으로 비교했음(6절).
- 주민번호·전화·카드·면허·여권·사업자번호 등 21종 포맷 검증기로 행 단위 감사(정규표현식)를 수행했으며, 세 하위 출처 중
<불량 데이터 예시>
- 정상 :
DRIVER_LICENSE = 11-23-456789-01 - 불량 :
DRIVER_LICENSE = 0W4SB7XAR4
4-2. KDPII (고려대, CC-BY-4.0) — 약 40,000개 문장 · 3,664 대화#
- 데이터 설명
- 실제 구어체 대화 기반의 유일한 한국어 PII 실데이터입니다.
<데이터 샘플>
원문 : 나 캐나다에서 태어나서 5년 전 17살에 한국 왔어.
정답 : LCP_COUNTRY 캐나다 · QT_AGE 17살 · LC_ADDRESS 한국 → 매핑 후: ADDRESS 한국 (라벨에서 LCP_COUNTRY와 같은 국가 개념은 잡을 필요없는 데이터이기에 학습데이터에서 제거했음)
수집 사유
- 공식 train/valid/test 분할이 있어 외부 비교 기준이 되며, 문장 평균 25자로 짧은 데이터로 구성되어 있습니다.
처리
- 33개의 KDPII category 중 키·몸무게·혈액형·종교 등 우리 모델에서 탐지하지 않는 카테고리들은 비 라벨로 처리하고 우리 모델에서 탐지하는 16개의 라벨로 매핑. 카드·계좌 스팬에 붙은 은행명은 숫자 구간만 남기도록 정규화하여 BCCard 데이터셋의 포맷과 일치시킴.
4-3. 합성 데이터 (자체) — 26,500행#
- 데이터 설명
- 위 수집한 데이터에서 미비된 라벨에 대한 데이터는 gpt-5.4 모델을 활용하여 생성하여 채웠습니다.
- 데이터는 원본에서 빈칸을 맞추는(PII를 맞추는 위의 데이터들과 같은 형식) 데이터와 빈
<데이터 샘플>
원문 : 연규도우 과장님 고객님, 문의하신 4918-5036-****-3258 카드 승인번호 55276357 건은 등록 계좌 587918-37-599711 로 1995.07.15 환불됩니다. 요금제 5G 프리미어 80입니다.
정답 : PERSON 연규도우 · CARD_NUMBER 4918-5036-****-3258 · TRANSACTION_APPROVAL_ID 55276357 · ACCOUNT_NUMBER 587918-37-599711 · DATE 1995.07.15 (뒤의 “요금제 5G 프리미어 80"은 삽입된 hard-negative)
수집 사유
- 공개 데이터에 없는 라벨(차량번호·가입번호·단말 S/N)과 부족한 도메인(금융·통신·보험 상담문)을 보강하고, 주문번호·상품코드·금액 같은 hard-negative를 정답과 같은 빈도로 넣어 “숫자면 다 잡는” 과탐을 억제하는 효과를 주었습니다.
방식
- BC Card에 잘못된 포맷의 데이터가 들어가서 데이터 품질을 떨어뜨렸던 사례를 참고하여, LLM이 아래와 같은 원문을 생성하되, 원문 내에 들어가는 pii 카테고리 데이터에 대해서는 로직 기반으로 채우도록 하여, 환각으로 인한 잘못된 label 데이터가 포함되는 사례를 방지하였습니다.
LLM생성 :
{PERSON} 고객님, 문의하신 {CARD_NUMBER} 카드 승인번호 {TRANSACTION_APPROVAL_ID} 건은 등록 계좌 {ACCOUNT_NUMBER} 로 {DATE} 환불됩니다.위 category별 삽입되는 데이터는 프로그래미틱하게 생성 반영
5. 학습#
5-1. 학습방식 → Full Fine-Tuning#
- Ko-Electra-base-v3 백본 모델의 경우, 기존 transformer encoder에 이진판별(토큰 간 동일여부)이 가능한 헤더가 붙어있었습니다. (768 → 1). 해당 헤더 부분을 PII category 분류 목적에 맞게, 768 → 129dim의 헤더로 변경하였습니다.
- 그리고, 가중치 업데이트는 전체 모델 architecture에서 기본적으로 Embedding layer, Encoder layer를 학습합니다. (헤더만 학습하는 게 아니라, 전체 레이어에 대해 기울기 계산을 진행하여 백본 전체를 갱신함!!)
- 그리고 위에서 언급한 토큰 분류헤더에 대해, 각 토큰별로 총 129개 라벨에 대한 확률값을 예측하는 형태로 학습을 진행하였습니다.
- 라벨이 129개인 이유는, 총 32개의 카테고리 라벨이 있고, 각 라벨별로 ‘시작 B(Begin)’, ‘중간 I(Inside)’, ‘끝 E(End)’, ‘단일 S(그냥 혼자서 하나의 엔티티를 가지는 경우, Single)’ 총 4가지 정보로 분류되고, 마지막으로 엔티티가 아님에 해당하는 라벨까지 더해서, 32 x 4 + 1 = 129개의 예측해야 할 라벨이 발생합니다.
- 다만, 모델의 output이 129개 라벨에 대한 토큰별 확률값이고, 각 토큰별 129개의 확률값 중 제일 높은 확률의 라벨을 고르는 방식(argmax)을 선택할 경우, 토큰의 시작-중간-끝 요렇게 문법(?)에 맞게 배치되어야 하는데, 단순 높은 확률값으로만 채택할 경우, 단일 토큰의 예측라벨이 IBBS…BBI, 뭐 이런 식으로 말도 안 되는 토큰 배치가 발생할 수 있습니다.
- 이에, Viterbi 알고리즘을 적용하여, 앞단의 토큰의 라벨이 예측되었을 때, 뒷 토큰의 경우 그 자리에 올 수 있는 라벨 중에서 가장 높은 확률을 가진 라벨을 채택하는 방식으로 보정하여 분류하였습니다.
- Tuning Params
- Loss Function :
nn.CrossEntropyLoss - Label Smoothing : 0.05 (v1~v3a) → 0 (v3b·v4)
- Optimizer : AdamW
- weight decay : 0.01
- dropout : 0.1
- learning rate : 3e-5 → 5e-5 (v3b·v4)
- warmup : 10%
- epoch : 5→6
- Loss 제외 토큰 반영(ignore_index=-100 처리)
- padding, [CLS], [SEP] 토큰은 학습 제외
- Loss Function :
5-2. 학습과정#
버전별 데이터셋
| 버전 | BCCard 공개 데이터 | KDPII 공개 데이터 | 자체 생성 | 합계 | 버전 특이점 |
|---|---|---|---|---|---|
| v1 | 57,851 | 43,504 | 17,231 | 118,586 | 같은 조건으로 두 벌을 학습 - 불량 데이터 제거 분(108,538) vs All(118,586) |
| v2 | 57,851 | 43,494 | 26,540 (+9,309) | 127,885 | BCCard, KDPII 데이터에는 없는 차량번호, 가입번호, 단말 S/N 데이터를 추가하여 총 합성 데이터를 1.5배로 증강 |
| v3-1 | 57,851 | 43,494 | 26,540 | 127,885 | 데이터 변경 없음 / 학습 시, BCCard 부분만 데이터를 x2하여 같은 데이터를 강조 학습 |
| v3-2 | 57,851 | 43,494 | 26,540 | 127,885 | 데이터 변경 없음. 학습률, label smoothing, epoch만 조정하여 학습 |
| v4 / 최종 | 57,851 | 43,498 | 26,875 / +335 | 128,224 | 합성 데이터 내 라벨 오류 수정 → “김철수, 홍길동, 이영희"가 정답 없이 박혀 있던 1,300여 곳을 개인정보로 정정하고, 이름이 문맥에 등장하는 문장 139개 추가 생성 반영 |
버전별 의도 및 결과
| 버전 | train set | 의도 | 기대했던 효과 | 결과 |
|---|---|---|---|---|
| v1 | train_clean 108,538 / train_full 118,586 | 같은 조건에서 불량 행 제외(clean) vs 전체(full) 두 벌 병렬로 학습 진행 | 불량 데이터 제거한 데이터셋에서 더 높은 스코어 기대.. | 반대로 나옴! / 원인은, BC Card 데이터에 불량데이터의 분포가 train, valid set 모두 동일한 분포로 포함되어 있었는데, train에서 제거하여, 해당 패턴을 학습하지 못한 상태에서 validation에서 해당 패턴의 데이터를 맞추지 못한 문제 |
| v2 | train_*_v2 127,885 | 다양한 category에 대한 추가 합성 데이터를 1.5배로 늘려서, 추가 category에 대한 학습 반영 | BCCard, KDPII에 없는 라벨(차량번호/가입번호/단말 S/N)을 채워 학습 카테고리 범위 확대 | KDPII, BCCard 두 벤치는 성능향상 없었고, 합성 데이터 한정 0.977 달성함. |
| v3a | train_full_v2 (v2와 동일) | 확인 결과 BCCard 데이터에 대한 성능이 좋지 않았고, 그 부분에 대한 강화 전략 | 확인 결과, 데이터 자체에 문제는 없어 보여 강조 학습을 진행하면 좀 더 성능 향상을 얻을 수 있지 않을까..? | 효과 없음. BCCard val 0.980 (오히려 −0.2pt), KDPII 0.934 동일. 이미 충분히 보고 학습에 반영된 것으로 이해 |
| v3b | train_full_v2 (v2와 동일) | 데이터 동일, 하이퍼파라미터만 수정 → lr 3e-5→5e-5, ls 0.05→0, epoch 6 | 학습 파라미터 조정을 통해 최적화…성능 수렴 지점을 개선해보고자 함 | KDPII 0.935 (+0.1pt), BCCard 0.982. loss는 400배 내려갔으나 점수는 사실상 동률 |
| v4 (최종) | train_full_v3 128,224 / + 이름 문맥 템플릿 139행, 더미 이름 1,300건 라벨 수정 | 합성 템플릿에 플레이스홀더 없이 박혀 있던 김철수, 홍길동, 이영희를 자동으로 {PERSON}으로 치환하고, 이름이 문맥에 등장하는 수작업 템플릿 추가 | v3b가 이 세 이름을 개인정보로 못 잡는 문제(O로 학습된 것 확인) 해결 | 벤치는 v3b와 동률(KDPII 0.934 · BCCard 0.983)이나 테스트용 17문장 11/17 → 17/17로 향상. 실사용 성능 개선으로 이해 |
6. 학습 결과#
exact-match span F1 · 같은 스코어러 · 같은 디코더
| 평가셋 | Veil-PII-Ko-Lite (110M) | BCCard MoAI-PF-INT8 (1.4B) | FrameByFrame (1.4B, 9라벨) | Azure AI Language PII |
|---|---|---|---|---|
| KDPII test (4,891) | .934 | .453 | .516 | .463 |
| BCCard val · ko (10,743) | .983 | .959 | n/a | .403 |
| 합성 heldout v2 (670) | .965 | .533 | n/a | .504 |
6-0. Veil-PII-Ko-Lite 모델 제공 32개 Label#
Veil-PII-Ko-Lite 모델 제공 32개 Label 목록#
| 라벨 | 한글 | 설명 | 예시 | 유형 |
|---|---|---|---|---|
| 사람·조직 | ||||
| PERSON | 사람 이름 | 실명·호칭·서명. 성만 있어도 잡는다 | 김철수 / 이영희 씨 / 홍길동 대리 | 문맥 |
| ORGANIZATION | 기관·회사명 | 회사·학교·병원·관공서. 상호에 사람 이름이 섞이면 문맥으로 가른다 | 대한항공 / 김민수 치과 / 강남구청 | 문맥 |
| USER_ID | 계정 아이디 | 로그인 ID·닉네임·핸들 | wtkim89 / @veil_admin | 혼합 |
| 연락처·위치 | ||||
| ADDRESS | 주소 | 도로명·지번·건물명. 행정구역만 있어도 대상 | 서울 강남구 테헤란로 152 / 판교빌딩 3층 | 문맥 |
| 이메일 주소 | RFC 형식 주소 | hong@example.co.kr | 정형 | |
| PHONE | 전화번호 | 휴대폰·유선·대표번호. 구분자가 없거나 점이어도 잡는다 | 010-1234-5678 / 02)555-1234 | 정형 |
| IPADDRESS | IP 주소 | IPv4·IPv6 | 192.168.0.11 / 2001:db8::1 | 정형 |
| MACADDRESS | MAC 주소 | 네트워크 인터페이스 물리 주소 | 00:1B:44:11:3A:B7 | 정형 |
| PORT | 포트 번호 | 호스트 뒤에 붙는 서비스 포트 | :8443 | 정형 |
| URL | 웹 주소 | http(s)·www 로 시작하는 링크 | https://veil.example.com/u/1123 | 정형 |
| ZIPCODE | 우편번호 | 5자리 신 우편번호, 6자리 구 번호 | 06236 / 135-080 | 정형 |
| 한국 신원 | ||||
| SSN | 해외 사회보장번호 | 미국 SSN 등 국외 신원번호 | 123-45-6789 | 정형 |
| RRN | 주민등록번호 | 13자리. 생년월일 + 성별코드 + 검증번호 | 900101-1234567 | 정형 |
| FRN | 외국인등록번호 | 주민번호와 같은 자릿수, 성별코드가 5~8 | 900101-5234567 | 정형 |
| BUSINESS_ID | 사업자등록번호 | 3-2-5 자리 | 123-45-67890 | 정형 |
| PASSPORT | 여권번호 | M/S/R/O + 8자리 | M12345678 | 정형 |
| DRIVER_LICENSE | 운전면허번호 | 지역코드-연도-일련-검증 | 11-22-123456-78 | 정형 |
| CI | 연계정보 | 본인확인기관이 발급하는 88자 해시 식별자 | 88자 Base64 문자열 | 정형 |
| IPIN | 아이핀 번호 | 인터넷 개인식별번호 | 13자리 영숫자 | 정형 |
| 금융 | ||||
| CARD_NUMBER | 카드번호 | 14~19자리. 발급사·BIN·검증번호 구조 | 4111-1111-1111-1111 | 정형 |
| CARD_EXPIRY | 카드 유효기간 | MM/YY 또는 MM/YYYY | 12/28 | 정형 |
| CVC | 카드 검증코드 | 카드 뒷면 3~4자리 | 123 | 정형 |
| VIRTUAL_CARD_NUMBER | 가상 카드번호 | 일회용·토큰화 카드번호 | 실카드와 자릿수 동일 | 정형 |
| ACCOUNT_NUMBER | 계좌번호 | 은행별 자릿수·구분자가 제각각 | 110-234-567890 | 정형 |
| TRANSACTION_APPROVAL_ID | 거래 승인번호 | 결제 승인 시 발급되는 식별자 | 30123456 | 정형 |
| 기기·가입 | ||||
| IMEI | 단말 식별번호 | 휴대폰 15자리 고유번호 | 356938035643809 | 정형 |
| VEHICLE_PLATE | 차량번호 | 지역·분류·한글·일련번호 | 12가 3456 / 서울 32나 1234 | 정형 |
| SUBSCRIBER_ID | 가입번호 | 통신·서비스 가입 식별자 | 가입자 관리번호 | 혼합 |
| DEVICE_SERIAL | 기기 일련번호 | 셋톱·공유기 등 단말 시리얼 | SN-4F2A-90C1 | 정형 |
| 기타 | ||||
| DATE | 날짜 | 생년월일·가입일 등 개인을 특정하는 날짜 | 1996년 12월 8일 / 2026-09-23 | 혼합 |
| GENERIC_ID | 기타 식별자 | 유형을 특정하기 어려운 식별자를 받는 자리 | 주문번호 / 회원번호 | 혼합 |
| SECRET | 비밀값 | 비밀번호·API 키·토큰 | sk-live-9f3a… | 혼합 |
6-1. 모델별 F1-Score#
| 평가셋 | n | Veil INT8 | Veil fp32 | BCCard 1.4B | FrameByFrame | Azure PII |
|---|---|---|---|---|---|---|
| KDPII test | 4,891 | 0.9327 | 0.9333 | 0.4533 | 0.5156 | 0.4631 |
| KDPII · 대화 단위 | 458 | 0.9420 | 0.9423 | 0.4661 | — | 0.4626 |
| BCCard validation · ko | 10,743 | 0.9825 | 0.9820 | 0.9594 | — | 0.4031 |
| BCCard validation · en | 3,781 | 0.9697 | 0.9700 | 0.9653 | — | 0.5295 |
| 합성 heldout v2 | 670 | 0.9643 | 0.9652 | 0.5328 | — | 0.5035 |
- FrameByFrame의 경우, 벤치를 측정하지 않은 이유는, 해당 모델이 9개 카테고리밖에 지원하지 않아, 직접적 비교가 힘들어, 제외 처리함.
“PII의 경우, 오답의 리스크가 크기 때문에, Recall 및 Precision도 중요하며, 제공하는 카테고리가 많아졌기 때문에 카테고리별 스코어도 확인할 필요가 있었습니다.”
6-2. 각 모델별 전체 라벨에 대한 Recall Score#
1) KDPII test set 기준 Recall (라벨 17개 보유)#
| 라벨 | n | Veil INT8 | Veil fp32 | BCCard 1.4B | Azure PII | FrameByFrame | 정규식 |
|---|---|---|---|---|---|---|---|
| ORGANIZATION | 467 | 0.886 | 0.889 | 0.006 | 0.392 | 0.000 | 0.000 |
| PERSON | 274 | 0.912 | 0.909 | 0.307 | 0.671 | 0.792 | 0.000 |
| ADDRESS | 208 | 0.899 | 0.899 | 0.082 | 0.048 | 0.447 | 0.000 |
| PHONE | 124 | 1.000 | 1.000 | 1.000 | 0.927 | 1.000 | 0.218 |
| 81 | 1.000 | 1.000 | 0.975 | 1.000 | 1.000 | 1.000 | |
| CARD_NUMBER | 78 | 1.000 | 1.000 | 1.000 | 0.077 | 0.000 | 0.000 |
| ACCOUNT_NUMBER | 77 | 1.000 | 1.000 | 0.364 | 0.000 | 0.000 | 0.416 |
| DATE | 70 | 0.971 | 0.971 | 0.914 | 0.943 | 0.986 | 0.000 |
| USER_ID | 61 | 1.000 | 1.000 | 0.656 | 0.000 | 1.000 | 0.000 |
| VEHICLE_PLATE | 52 | 1.000 | 1.000 | 0.000 | 0.000 | 0.000 | 0.000 |
| URL | 39 | 1.000 | 1.000 | 0.795 | 0.974 | 1.000 | 0.744 |
| RRN | 18 | 0.944 | 0.944 | 1.000 | 0.278 | 0.000 | 0.000 |
| PASSPORT | 18 | 1.000 | 1.000 | 0.889 | 0.000 | 0.000 | 0.278 |
| DRIVER_LICENSE | 17 | 1.000 | 1.000 | 1.000 | 0.000 | 0.000 | 0.000 |
| IPADDRESS | 11 | 1.000 | 1.000 | 0.909 | 1.000 | 1.000 | 0.273 |
| FRN | 9 | 1.000 | 1.000 | 0.000 | 0.000 | 0.000 | 0.000 |
| micro | 1,604 | 0.9370 | 0.9370 | 0.3797 | 0.4358 | 0.4333 | 0.1103 |
2) BCCard-val-ko set 기준 Recall (라벨 29개 보유)#
| 라벨 | n | Veil INT8 | Veil fp32 | BCCard 1.4B | Azure PII | 정규식 |
|---|---|---|---|---|---|---|
| PERSON | 8,214 | 0.983 | 0.980 | 0.969 | 0.522 | 0.000 |
| DATE | 7,910 | 0.997 | 0.997 | 0.998 | 0.757 | 0.000 |
| ADDRESS | 7,201 | 0.991 | 0.992 | 0.932 | 0.171 | 0.000 |
| 4,535 | 1.000 | 1.000 | 0.968 | 0.528 | 0.531 | |
| PHONE | 3,723 | 0.965 | 0.965 | 0.937 | 0.528 | 0.154 |
| CARD_NUMBER | 2,851 | 1.000 | 1.000 | 0.999 | 0.002 | 0.000 |
| ZIPCODE | 2,608 | 0.995 | 0.995 | 0.996 | 0.000 | 0.000 |
| GENERIC_ID | 2,021 | 0.905 | 0.903 | 0.842 | 0.000 | 0.000 |
| SSN | 1,816 | 0.958 | 0.958 | 0.958 | 0.000 | 0.000 |
| RRN | 1,743 | 0.998 | 0.998 | 0.997 | 0.332 | 0.000 |
| DRIVER_LICENSE | 1,723 | 0.911 | 0.910 | 0.867 | 0.000 | 0.000 |
| PASSPORT | 1,475 | 0.999 | 0.999 | 0.920 | 0.000 | 0.002 |
| ORGANIZATION | 467 | 0.981 | 0.981 | 0.904 | 0.559 | 0.000 |
| IPADDRESS | 241 | 1.000 | 1.000 | 1.000 | 0.996 | 0.373 |
| TRANSACTION_APPROVAL_ID | 233 | 1.000 | 1.000 | 1.000 | 0.000 | 0.000 |
| USER_ID | 225 | 0.996 | 0.996 | 0.987 | 0.000 | 0.000 |
| ACCOUNT_NUMBER | 210 | 0.952 | 0.952 | 0.929 | 0.000 | 0.409 |
| BUSINESS_ID | 207 | 0.961 | 0.961 | 0.942 | 0.000 | 0.000 |
| URL | 164 | 1.000 | 1.000 | 0.988 | 0.848 | 0.305 |
| CI | 163 | 1.000 | 1.000 | 0.994 | 0.000 | 0.000 |
| PORT | 163 | 0.932 | 0.932 | 0.896 | 0.000 | 0.000 |
| SECRET | 158 | 0.987 | 0.987 | 0.981 | 0.000 | 0.000 |
| IPIN | 151 | 0.987 | 0.987 | 0.980 | 0.000 | 0.000 |
| MACADDRESS | 148 | 1.000 | 1.000 | 1.000 | 0.000 | 0.277 |
| FRN | 120 | 1.000 | 1.000 | 0.992 | 0.000 | 0.000 |
| IMEI | 106 | 1.000 | 1.000 | 1.000 | 0.000 | 0.000 |
| CARD_EXPIRY | 104 | 1.000 | 1.000 | 1.000 | 0.000 | 0.000 |
| VIRTUAL_CARD_NUMBER | 102 | 1.000 | 1.000 | 1.000 | 0.000 | 0.000 |
| CVC | 92 | 1.000 | 1.000 | 0.978 | 0.000 | 0.000 |
| micro | 48,874 | 0.9829 | 0.9823 | 0.9589 | 0.3497 | 0.0665 |
3) 자체 합성 데이터 set 기준 Recall (라벨 32개 보유)#
| 라벨 | n | Veil fp32 | BCCard 1.4B | Azure PII |
|---|---|---|---|---|
| PERSON | 208 | 0.947 | 0.827 | 0.870 |
| ORGANIZATION | 161 | 0.981 | 0.074 | 0.509 |
| PHONE | 157 | 0.994 | 0.987 | 0.796 |
| DATE | 150 | 0.987 | 0.800 | 0.767 |
| ADDRESS | 126 | 1.000 | 0.706 | 0.444 |
| 113 | 1.000 | 0.982 | 0.761 | |
| USER_ID | 41 | 1.000 | 0.244 | 0.000 |
| SECRET | 40 | 1.000 | 0.650 | 0.000 |
| IPADDRESS | 38 | 1.000 | 1.000 | 0.974 |
| SUBSCRIBER_ID | 36 | 0.972 | 0.000 | 0.000 |
| ACCOUNT_NUMBER | 35 | 1.000 | 0.686 | 0.000 |
| URL | 33 | 1.000 | 0.818 | 0.970 |
| GENERIC_ID | 32 | 0.781 | 0.313 | 0.000 |
| DEVICE_SERIAL | 32 | 0.969 | 0.000 | 0.000 |
| VEHICLE_PLATE | 29 | 1.000 | 0.000 | 0.000 |
| ZIPCODE | 28 | 1.000 | 0.464 | 0.000 |
| PASSPORT | 27 | 1.000 | 0.963 | 0.000 |
| TRANSACTION_APPROVAL_ID | 27 | 1.000 | 0.259 | 0.000 |
| RRN | 26 | 0.923 | 0.962 | 0.808 |
| CI | 26 | 1.000 | 0.808 | 0.000 |
| FRN | 22 | 1.000 | 0.000 | 0.000 |
| VIRTUAL_CARD_NUMBER | 22 | 0.909 | 0.227 | 0.000 |
| PORT | 21 | 0.952 | 0.048 | 0.000 |
| IMEI | 21 | 1.000 | 0.524 | 0.000 |
| DRIVER_LICENSE | 20 | 0.950 | 0.700 | 0.000 |
| BUSINESS_ID | 19 | 1.000 | 0.263 | 0.000 |
| MACADDRESS | 19 | 1.000 | 0.737 | 0.000 |
| IPIN | 18 | 1.000 | 0.000 | 0.000 |
| CVC | 17 | 0.941 | 0.294 | 0.000 |
| SSN | 15 | 1.000 | 1.000 | 0.000 |
| CARD_NUMBER | 15 | 0.933 | 1.000 | 0.933 |
| CARD_EXPIRY | 12 | 1.000 | 0.000 | 0.000 |
| micro | 1,586 | 0.9786 | 0.6122 | 0.4723 |
6-3. 각 모델별 전체 라벨에 대한 Precision Score#
1) KDPII test set 기준 Precision (라벨 17개 보유)#
| 라벨 | n | Veil INT8 | Veil fp32 | BCCard 1.4B | Azure PII | FrameByFrame | 정규식 |
|---|---|---|---|---|---|---|---|
| ORGANIZATION | 467 | 0.914 | 0.912 | 0.375 | 0.518 | 0.000 | 0.000 |
| PERSON | 274 | 0.877 | 0.886 | 0.273 | 0.570 | 0.670 | 0.000 |
| ADDRESS | 208 | 0.858 | 0.858 | 0.110 | 0.345 | 0.830 | 0.000 |
| PHONE | 124 | 1.000 | 1.000 | 0.855 | 0.412 | 1.000 | 1.000 |
| 81 | 1.000 | 1.000 | 0.975 | 1.000 | 1.000 | 1.000 | |
| CARD_NUMBER | 78 | 1.000 | 1.000 | 0.709 | 1.000 | 0.000 | 0.000 |
| ACCOUNT_NUMBER | 77 | 1.000 | 1.000 | 0.966 | 0.000 | 0.000 | 0.244 |
| DATE | 70 | 0.919 | 0.919 | 0.762 | 0.229 | 1.000 | 0.000 |
| USER_ID | 61 | 0.984 | 0.984 | 0.952 | 0.000 | 0.984 | 0.000 |
| VEHICLE_PLATE | 52 | 0.981 | 0.981 | 0.000 | 0.000 | 0.000 | 0.000 |
| URL | 39 | 1.000 | 1.000 | 0.838 | 0.974 | 1.000 | 0.744 |
| RRN | 18 | 1.000 | 1.000 | 0.667 | 0.833 | 0.000 | 0.000 |
| PASSPORT | 18 | 1.000 | 1.000 | 0.889 | 0.000 | 0.000 | 1.000 |
| DRIVER_LICENSE | 17 | 1.000 | 1.000 | 0.809 | 0.000 | 0.000 | 0.000 |
| IPADDRESS | 11 | 1.000 | 1.000 | 0.909 | 1.000 | 1.000 | 1.000 |
| FRN | 9 | 0.900 | 0.900 | 0.000 | 0.000 | 0.000 | 0.000 |
| micro | 1,604 | 0.9284 | 0.9295 | 0.5623 | 0.4940 | 0.6364 | 0.6189 |
2) BCCard-val-ko set 기준 Precision (라벨 29개 보유)#
| 라벨 | n | Veil INT8 | Veil fp32 | BCCard 1.4B | Azure PII | 정규식 |
|---|---|---|---|---|---|---|
| PERSON | 8,214 | 0.984 | 0.980 | 0.975 | 0.520 | 0.000 |
| DATE | 7,910 | 0.996 | 0.996 | 0.996 | 0.815 | 0.000 |
| ADDRESS | 7,201 | 0.988 | 0.989 | 0.931 | 0.175 | 0.000 |
| 4,535 | 0.999 | 0.999 | 0.968 | 0.877 | 0.883 | |
| PHONE | 3,723 | 0.965 | 0.965 | 0.937 | 0.250 | 0.892 |
| CARD_NUMBER | 2,851 | 0.997 | 0.997 | 0.996 | 0.114 | 0.000 |
| ZIPCODE | 2,608 | 0.995 | 0.995 | 0.992 | 0.000 | 0.000 |
| GENERIC_ID | 2,021 | 0.885 | 0.884 | 0.833 | 0.000 | 0.000 |
| SSN | 1,816 | 0.968 | 0.967 | 0.958 | 0.000 | 0.000 |
| RRN | 1,743 | 0.999 | 0.999 | 0.997 | 0.903 | 0.000 |
| DRIVER_LICENSE | 1,723 | 0.922 | 0.921 | 0.873 | 0.000 | 0.000 |
| PASSPORT | 1,475 | 0.999 | 0.999 | 0.919 | 0.000 | 1.000 |
| ORGANIZATION | 467 | 0.985 | 0.985 | 0.944 | 0.163 | 0.000 |
| IPADDRESS | 241 | 1.000 | 1.000 | 0.992 | 0.988 | 0.978 |
| TRANSACTION_APPROVAL_ID | 233 | 0.996 | 0.996 | 1.000 | 0.000 | 0.000 |
| USER_ID | 225 | 0.996 | 0.996 | 1.000 | 0.000 | 0.000 |
| ACCOUNT_NUMBER | 210 | 0.995 | 0.995 | 1.000 | 0.000 | 0.029 |
| BUSINESS_ID | 207 | 0.971 | 0.971 | 0.970 | 0.000 | 0.000 |
| URL | 164 | 0.988 | 0.988 | 0.982 | 0.837 | 0.301 |
| CI | 163 | 1.000 | 1.000 | 0.994 | 0.000 | 0.000 |
| PORT | 163 | 0.950 | 0.950 | 0.936 | 0.000 | 0.000 |
| SECRET | 158 | 1.000 | 1.000 | 1.000 | 0.000 | 0.000 |
| IPIN | 151 | 0.987 | 0.987 | 0.993 | 0.000 | 0.000 |
| MACADDRESS | 148 | 1.000 | 1.000 | 1.000 | 0.000 | 1.000 |
| FRN | 120 | 1.000 | 1.000 | 0.984 | 0.000 | 0.000 |
| IMEI | 106 | 1.000 | 1.000 | 0.973 | 0.000 | 0.000 |
| CARD_EXPIRY | 104 | 1.000 | 1.000 | 1.000 | 0.000 | 0.000 |
| VIRTUAL_CARD_NUMBER | 102 | 0.971 | 0.971 | 1.000 | 0.000 | 0.000 |
| CVC | 92 | 0.968 | 0.968 | 1.000 | 0.000 | 0.000 |
| micro | 48,874 | 0.9822 | 0.9816 | 0.9599 | 0.4759 | 0.3880 |
3) 자체 합성 데이터 set 기준 Precision (라벨 32개 보유)#
| 라벨 | n | Veil fp32 | BCCard 1.4B | Azure PII |
|---|---|---|---|---|
| PERSON | 208 | 0.791 | 0.619 | 0.694 |
| ORGANIZATION | 161 | 0.975 | 0.600 | 0.506 |
| PHONE | 157 | 0.987 | 0.901 | 0.316 |
| DATE | 150 | 0.987 | 0.645 | 0.567 |
| ADDRESS | 126 | 0.984 | 0.315 | 0.409 |
| 113 | 1.000 | 0.982 | 0.761 | |
| USER_ID | 41 | 0.976 | 0.556 | 0.000 |
| SECRET | 40 | 1.000 | 0.867 | 0.000 |
| IPADDRESS | 38 | 1.000 | 0.927 | 0.974 |
| SUBSCRIBER_ID | 36 | 0.921 | 0.000 | 0.000 |
| ACCOUNT_NUMBER | 35 | 0.972 | 0.558 | 0.000 |
| URL | 33 | 1.000 | 0.818 | 0.970 |
| GENERIC_ID | 32 | 1.000 | 0.115 | 0.000 |
| DEVICE_SERIAL | 32 | 0.969 | 0.000 | 0.000 |
| VEHICLE_PLATE | 29 | 0.967 | 0.000 | 0.000 |
| ZIPCODE | 28 | 0.966 | 0.076 | 0.000 |
| PASSPORT | 27 | 1.000 | 0.377 | 0.000 |
| TRANSACTION_APPROVAL_ID | 27 | 1.000 | 0.184 | 0.000 |
| RRN | 26 | 0.960 | 0.207 | 0.750 |
| CI | 26 | 1.000 | 0.778 | 0.000 |
| FRN | 22 | 0.917 | 0.000 | 0.000 |
| VIRTUAL_CARD_NUMBER | 22 | 0.952 | 0.385 | 0.000 |
| PORT | 21 | 0.909 | 1.000 | 0.000 |
| IMEI | 21 | 0.955 | 1.000 | 0.000 |
| DRIVER_LICENSE | 20 | 1.000 | 0.119 | 0.000 |
| BUSINESS_ID | 19 | 0.905 | 1.000 | 0.000 |
| MACADDRESS | 19 | 1.000 | 1.000 | 0.000 |
| IPIN | 18 | 0.818 | 0.000 | 0.000 |
| CVC | 17 | 1.000 | 0.417 | 0.000 |
| SSN | 15 | 1.000 | 0.263 | 0.000 |
| CARD_NUMBER | 15 | 0.933 | 0.190 | 0.933 |
| CARD_EXPIRY | 12 | 1.000 | 0.000 | 0.000 |
| micro | 1,586 | 0.9487 | 0.4716 | 0.5392 |
6-4. 양자화 및 Latency#
- Dynamic INT8로 양자화를 진행한 경우, 모델크기는 113까지 감소했으나, activation까지 양자화(정수)되면서 recall이 대폭 하락했습니다. 원인은 129개 라벨의 1,2등의 prediction score가 매우 근소한 차이라, 성능하락이 컸습니다.
- 이에, weight는 int8로 양자화하고, activation은 fp32를 유지하여 성능은 유지하고, 모델크기는 143MB로 축소했습니다.
| 변형 | 크기 | KDPII F1 | 4스레드 128 / 512 tok | 16스레드 512 tok | 판정 |
|---|---|---|---|---|---|
| fp32 ONNX | 450MB | 0.9339 | 77 / 274 ms | 118 ms | 기준 |
| 동적 INT8 | 113MB | ~0.80 | 25 / 95 ms | 58 ms | 재현율 붕괴, 미채택 |
| weight-only INT8 + fp16 emb | 143MB | 0.9342 | 61 / 237 ms | 108 ms | |
| weight-only INT4 + fp16 emb | 100MB | 0.9321 | 67 / 261 ms | 119 ms | 실험값, 미공개 |
6-5. 종합 비교#
| 항목 | Veil-PII-Ko-Lite | BCCard MoAI-PF | FrameByFrame | Azure AI Language PII |
|---|---|---|---|---|
| 최초 공개 | 2026-09 | 2026-08 | 2026-04 | 2021 GA |
| 파라미터 · 배포 크기 | 110M · 143MB | 1.4B · INT8 수 GB | 1.4B · fp32 5.6GB | — (API) |
| 라벨 수 | 32 | 29 | 9 | 13 |
| KDPII test F1 | 0.934 | 0.453 | 0.516 | 0.463 |
| BCCard val(ko) F1 | 0.983 | 0.959 | — | 0.403 |
| 폐쇄망 배포 | 가능 (CPU ONNX) | 가능(but, 무거움) | 가능(but, 무거움) | - |
| 라이선스 | Apache 2.0 | Apache 2.0 + 고지 요구 | Apache 2.0 | - |
7. 최종 결과 분석#
7-1. 최종 결과#
- CPU 환경에서 운용이 가능한 110M의 작은 PII 모델 확보
- 1.4B에 달하는 모델 대비 더 많은 Category를 지원(32개)
- 경쟁 모델 대비 작은 크기(10배)로 더 높은 탐지 성능 확보
7-2. 매우 작은 모델로도 높은 성능을 낼 수 있었던 이유#
- PII detection 시, 비교모델(bccard)의 경우 autoregressive하게 생성하여 output을 도출하는 방식인데, veil 모델의 경우, 각 토큰에 PII 라벨을 찍는 token classification 방식으로 탐지합니다. 즉, veil에서 필요한 출력은 {start, end, label}로 단순하나, 비교모델은 문장을 생성한 뒤, 원문에 다시 맞추는 alignment 과정이 필요한데, 이 부분에서 mask token 길이가 원문과 다르거나, 조사, 공백, 어순이 틀어지는 현상으로 인해 낮은 성능을 보일 수밖에 없습니다.
openpii-1.5m-ko데이터 전수검사를 통해 약 30%가량의 불량데이터를 검출하여 제거하여, 학습 노이즈를 최소화- 실제 대화체에 개인정보가 포함되는 형태의 학습데이터를 대거 추가하여, 구어체의 긴 문장이 포함된 벤치에서도 높은 성능을 보임.
- 합성데이터를 LLM으로 형태만 맞추어 무작위 생성하지 않고, 각 데이터별 포맷 규칙(프로그래미틱하게 작성)하에 작성하여 최대한 리얼월드 데이터와 유사하게 생성하고, 할루시네이션 가능성을 차단하였습니다.
- 예) Luhn 알고리즘 활용하여, 실제 카드번호와 동일한 형태로 생성 →
ISO/IEC 7812 — 앞 6자리 BIN(발급사) + 계좌번호 + 마지막 체크디지트
- 예) Luhn 알고리즘 활용하여, 실제 카드번호와 동일한 형태로 생성 →
7-3. 추후 보완사항#
- 새롭게 추가된 카테고리 중, S/N 기기단말번호 또는 가입번호와 같은 회사마다/도메인마다 정형화된 포맷이 아니라, 예측이 어려운 부분에 대해서도 학습에 추가했으나, 실제로 예측하지 못하는 케이스가 더러 발생했다.
- 비정형 타입의 카테고리의 경우, 전후 맥락 및 토큰의 위치정보 등을 통해 최대한 예측했으나, 아래 실제 테스트 결과와 같이 아직 탐지하지 못하는 케이스들이 확인되었고, 전체 카테고리의 탐지성능을 균일하게 끌어올리는 것이 추후 과제로 보여진다. (물론 실제 고객 환경에 적용 시, 해당 문제가 발생할 경우 모델단이 아닌 presidio를 통한 후처리를 통해 domain specific한 부분들은 어느 정도 처리가 가능하긴 함)
- 마지막으로, 실제 기업 내부 프로덕션에 올려서 실 데이터 기반으로 검증이 필요함.
| 입력 | 결과 | 평가 |
|---|---|---|
| 가입번호: KT469200698 (학습 포맷) | SUBSCRIBER_ID 1.00 | 정답 |
| 계약번호 884102993317 | SUBSCRIBER_ID 0.999 | 정답 |
| 가입번호: SKT-99-AB-77312 | MACADDRESS 0.49 | 위치는 잡았으나, 라벨 오답 |
| 증권번호 HW-LIFE-2024-88231 | GENERIC_ID 0.59 | 위치는 잡았으나, 라벨 오답 |
| 단말 S/N RF5IC1T7P97 | DEVICE_SERIAL 1.00 | 정답 |
| 공유기 시리얼 넘버 QX-88Z-41772-KR | MACADDRESS 0.998 | 문맥이 있어도 틀림… |
| 884102993317 | RRN 0.985 | 문맥이 없어 완전 틀려버림.. |
8. 사용방법#
- HuggingFace
- DockerHub → 모델만 담아 패키징한 이미지와 presidio 내에 모델을 담아 패키징한 이미지를 아래 dockerhub에 올려두었습니다.
- PlayGround
- https://20-249-59-7.sslip.io/
- 직접 여러 모델과 비교해서 실행해보실 수 있게 플레이그라운드를 호스팅해두었습니다.
- 해당 플레이그라운드에서는 아래 4개 모델의 PII 성능을 비교 확인하실 수 있게 구성해두었습니다.
- Veil-PII-Ko-Lite (우리 모델)
- MoAI-Privacy-Filter (BCCard 모델)
- FrameByFrame
- Azure AI Language PII
