/ Модель

Характеристики

Контекст
512 токенов
Параметры
140 млн
Лицензия
Apache 2.0
Архитектура
electra · dense
Базовая модель
koelectra-base-v3-discriminator
Библиотека
transformers
Опубликована
17 сен 2026

veil-pii-ko-lite

Разметка токеновТекст → label5 профессий

Veil PII KO Lite — корейская модель на базе KoELECTRA для обнаружения и классификации 32 типов персональных данных в тексте.

318скачиваний за 30 дней1лайквеса обновлены12 окт 2026

Бенчмарки

Как модель справилась с задачами пользователей ailist и что о ней публикуют открытые лидерборды

Публичные оценки

Из открытых лидербордов. Сравнивайте результаты одного теста при одинаковых условиях — отсутствие оценки не значит, что модель хуже.

В подключённых источниках результатов для этой модели пока нет.

Что умеет модель

Описание составлено по карточке модели и метаданным Hugging Face

1T разработала Veil PII KO Lite — модель для обнаружения и классификации персональных данных в корейском тексте. Это дообученная KoELECTRA-base-v3 с архитектурой ELECTRA и 32 категориями PII; модель содержит 140,1 млн параметров и обрабатывает до 512 токенов за окно. Репозиторий включает веса Safetensors и оптимизированный для CPU формат ONNX INT8.

Модель предназначена для локального обнаружения и маскирования персональных данных в диалогах и документах, в том числе в изолированных on-premise-средах. README сообщает о высоких результатах на тестах корейских диалогов и данных BCCard; длинные тексты обрабатываются скользящими окнами с перекрытием. По карточке заявлена поддержка корейского и английского языков.

Качество может снижаться для имён, адресов и организаций, требующих контекстного анализа, а также для нестандартно записанных идентификаторов: например, удаление разделителей в телефонном номере заметно ухудшает F1, а пробелы между символами модель пока обрабатывает ненадёжно. Для таких входных данных README рекомендует при необходимости добавить правила на основе регулярных выражений. Лицензия — Apache-2.0.

Сценарии применения

  • поиск персональных данных в корейских диалогах поддержки
  • обнаружение идентификаторов и контактных данных в документах
  • маскирование PII перед передачей текста в другие системы
  • локальная обработка чувствительных текстов в on-premise-среде

Ограничения

По README, модель чаще пропускает имена, адреса и названия организаций, где важен контекст; нестандартные варианты записи идентификаторов, особенно без разделителей или с пробелами между символами, могут ухудшать обнаружение. Для обработки длинных документов используйте актуальную версию с исправленным разбиением на окна.

Для каких профессий подходит5

Разделы каталога профессий, в задачах которых используется эта модель

Все профессии

Также подходит для

Карточка модели

README от автора на Hugging Face, как он его опубликовал

Открыть на Hugging Face

CPU환경에서 빠르게 구동이 가능한 한국어 개인정보 탐지용 토큰 분류 모델입니다.

License ONNX GitHub Docker


개요

On-Premise 형태로 구축이 필요한 환경에서 가벼우면서도 높은 PII 탐지 성능을 목표로 만들어진 모델입니다. KoELECTRA-base-v3 (110M) 를 통해 32개의 PII Category에 대한 분류가 가능하도록 Fine-Tunining했고, INT8 ONNX 로 CPU 환경에서 빠른 구동이 가능하도록 최적화 하였습니다.

벤치마크

평가 지표는 entity-level exact match F1 (micro) 입니다. 예측한 스팬의 시작·끝 위치와 라벨이 정답과 모두 일치해야 정답으로 처리합니다. 비교 모델도 전부 같은 스코어러와 같은 디코더를 태워 측정했습니다.

벤치nVeil fp32Veil INT8BCCard 1.4BFrameByFrame 1.4BAzure AI Language
KDPII test — 실제 대화체4,8910.93390.93420.45330.51560.4631
KDPII test — 대화 단위4580.94230.94330.4661—0.4626
KDPII test — FrameByFrame 9라벨 한정4,8910.9383——0.6824—
BCCard validation · ko10,7430.98260.98240.9594—0.4031
BCCard validation · en3,7810.97000.9697¹0.9653—0.5295
합성 heldout v2 — 32 라벨6700.96520.96430.5328—0.5035

¹ 2026-09-23 재측정값입니다(창 분할 버그 수정 반영). 나머지 값은 2026-09-17 기준입니다.

비교 모델군이 서로 불리하지 않도록, 각 모델이 학습한 라벨만으로 범위를 한정하여 측정 진행하였습니다. 다만, 본 모델과 비교모델간의 성능 차이는 동일하였습니다.(BCCard 29라벨 0.4636, Azure 매핑 라벨 0.4741) 그리고, FrameByFrame 은 9개 라벨만 지원하기 때문에 KDPII 외의 벤치는 측정하지 않았습니다.

2026-09-23 재측정. 아래 문서 길이 절에 적은 창 분할 버그가 이 표에도 걸려 있었습니다. BCCard validation 은 영어 행의 5.6%, 한국어 행의 4.5% 가 512 토큰을 넘는데(영어 최대 1,351 토큰), 그 뒷부분이 모델까지 가지 못했습니다. 고친 뒤 수치입니다.

벤치nINT8 (이전)INT8 (현재)BCCard 1.4B
KDPII test4,8910.93420.93270.4533
KDPII test — 대화 단위4580.94330.94200.4661
BCCard validation · ko10,7430.98240.98250.9594
BCCard validation · en3,7810.96320.96970.9653

영어가 0.65pt 오른 건 잘려 나가던 긴 행이 살아났기 때문입니다. 전에는 영어에서 BCCard 1.4B 에 0.2pt 밀렸는데 지금은 앞섭니다. 한국어가 살짝 내려간 건 전각 정규화를 넣은 대가입니다(−0.08pt).

라벨별 수치는 PER_LABEL.md 에 있습니다. 32 라벨을 데이터셋 5종(KDPII test · KDPII 대화 단위 · BCCard ko · BCCard en · 합성 heldout) × 모델 6종으로 펼쳐 놨습니다. 측정 조건은 EVIDENCE.md, 원본 JSON 은 eval_*.json 과 baselines/ 에 있습니다.

라벨별 재현율

KDPII test 기준으로 못 잡는 쪽과 다 잡는 쪽을 같이 적습니다.

구분라벨nRecall
놓치는 라벨ORGANIZATION4670.887
ADDRESS2080.899
PERSON2740.912
RRN180.944
전부 검출PHONE · EMAIL · CARD_NUMBER · ACCOUNT_NUMBER · USER_ID · VEHICLE_PLATE · URL · PASSPORT · DRIVER_LICENSE · IPADDRESS · FRN1.000

형식이 정해진 식별자는 거의 다 잡습니다. 놓치는 건 문맥을 봐야 하는 이름·주소·기관명 쪽입니다. BCCard validation(ko) 에서는 GENERIC_ID 0.905 · DRIVER_LICENSE 0.911 · PORT 0.933 순으로 낮습니다.

학습셋과 평가셋의 중복

leakage_check.py 로 텍스트를 세 단계(원문 / 공백·대소문자 무시 / 숫자 마스킹)로 해시해 대조했습니다.

데이터traintest완전중복공백무시숫자마스킹
KDPII40,1094,89192 (1.88%)94 (1.92%)108 (2.21%)
BCCard57,85114,524000

KDPII 중복 92건은 전부 정답 스팬이 0개입니다. 알았어. 정말 죄송합니다, 고객님. 같은 맞장구가 대화 코퍼스라 train 과 test 양쪽에 걸린 것입니다. 이 행을 빼고 다시 재도 F1 은 0.9327 로 같고, 숫자만 다른 템플릿 중복까지 빼면 0.9321 로 0.05pt 내려갑니다.

문서 길이

본문 토큰을 510개씩 자르고 인접한 창을 128 토큰 겹칩니다. 겹친 구간은 양쪽에서 64 토큰씩 잘라 한 번만 세기 때문에, 경계에 걸친 스팬도 한쪽 창이 통째로 봅니다.

문서 길이문서 수PRF1
~200자6050.93900.94950.9442
~800자1600.93340.95200.9426
~1,800자720.93280.95260.9426
~3,000자440.92910.94830.9386

2026-09-23 이전 버전에는 여기에 버그가 있었습니다. 창 분할을 토크나이저에 맡겼더니 1,617 토큰 입력에서 창이 2개만 생기고 953자 이후가 모델까지 가지 못했습니다(3,000자 문서 재현율 0.284). 창 분할을 직접 짜서 고쳤습니다. 긴 문서를 다룬다면 최신 버전을 받으세요.

표기 변형

정답 스팬은 그대로 두고 표기만 바꿔서 쟀습니다. 자세한 내용은 ROBUSTNESS.md 에 있습니다.

변형예Δ F1
하이픈 → 점010.1234.5678−0.2pt
하이픈 → 공백010 1234 5678−0.6pt
구분자 제거01012345678−7.4pt
전각 숫자0100.0pt
글자 사이 공백홍 길 동−61.1pt

전각 숫자는 길이를 유지하는 정규화로 처리합니다(기본 on, Veil(..., normalize=False) 로 끌 수 있습니다). 글자 사이에 공백을 넣은 표기는 아직 못 잡습니다. 서브워드 토크나이저가 글자마다 토큰을 새로 끊어서 학습 때 본 적 없는 형태가 되기 때문입니다. 이런 표기가 자주 들어오는 환경이면 앞단에 정규식 룰을 두는 게 낫습니다.

정규식과의 관계

정형 PII 12종에 정규식과 체크섬(Luhn, 주민번호 가중합)을 걸고 같은 스코어러로 쟀습니다.

데이터방식PRF1
KDPII test정규식0.61890.11030.1873
KDPII test이 모델0.92840.93700.9327
KDPII test정규식 ∪ 모델0.92840.93700.9327
BCCard val ko정규식 ∪ 모델0.97950.98290.9812

정규식도 EMAIL 재현율 1.000, URL 0.744 로 형식이 정해진 건 잘 잡습니다. 다만 대화체(번호 뒷자리가 5678)나 이름·주소·기관명에는 아예 걸리지 않습니다. 모델 위에 얹어도 점수가 오르지 않으니, 정규식은 탐지율보다 감사 근거를 남기는 용도로 쓰는 게 맞습니다.

사용법

pip install onnxruntime transformers numpy
hf download 1T/veil-pii-ko-lite --local-dir veil
import sys; sys.path.insert(0, "veil")
from veil import Veil                                   # ONNX Runtime + 제약 Viterbi + 후처리. torch 없이 동작

det = Veil("veil/model.int8.onnx", tokenizer_dir="veil", threads=4)

det.predict("담당자 김철수(010-1234-5678)에게 문의")
# [{'start': 4, 'end': 7,  'label': 'PERSON', 'score': 0.9999},
#  {'start': 8, 'end': 21, 'label': 'PHONE',  'score': 0.9999}]

det.mask("담당자 김철수(010-1234-5678)에게 문의")
# '담당자 [PERSON]([PHONE])에게 문의'

fp32 (transformers)

from transformers import AutoTokenizer, AutoModelForTokenClassification
tok = AutoTokenizer.from_pretrained("1T/veil-pii-ko-lite")
model = AutoModelForTokenClassification.from_pretrained("1T/veil-pii-ko-lite")

로짓을 그냥 argmax 하면 BIOES 규칙이 깨진 스팬이 섞여 나옵니다. Veil 클래스는 ONNX 전용이지만, 같은 파일에 있는 전이 행렬과 tags_to_spans, strip_particles 를 fp32 로짓에 걸면 결과가 같아집니다.

출력 · 옵션

predict(text) 는 list[{"start", "end", "label", "score"}] 를 돌려줍니다. start·end 는 원문 기준 문자 인덱스라 text[start:end] 로 바로 잘라 쓰면 되고, 스팬끼리 겹치지 않습니다. 마스킹된 문자열이 필요하면 mask(text, fmt="[{label}]") 를 쓰면 됩니다.

생성자 옵션입니다.

옵션기본값설명
max_len512한 번에 처리할 토큰 수
stride128슬라이딩 창이 겹치는 폭
threads4ONNX 스레드 수
o_bias0.0양수로 올리면 재현율 쪽으로 기울어집니다 (precision 은 내려갑니다)
merge_adjacent("ADDRESS",)공백 1개 이내로 붙은 같은 라벨을 합칩니다. 벤치마크를 재현할 때는 () 로 두세요

입력 길이 제한은 없습니다. 512 토큰 창을 128 씩 겹쳐 밀면서 전체를 훑고, 경계에 걸친 스팬은 뒤에서 이어 붙입니다.

컨테이너

가중치가 이미지에 들어 있어 따로 설치할 게 없고, 네트워크가 없는 환경에서도 그대로 뜹니다.

docker run -d -p 8080:8080 --name veil zzang9680/veil-pii:slim

curl -s localhost:8080/detect -H 'Content-Type: application/json' \
  -d '{"text":"담당자 김철수(010-1234-5678)에게 문의"}'

curl -s localhost:8080/mask -H 'Content-Type: application/json' \
  -d '{"text":"김철수 010-1234-5678","policy":"partial"}'
# {"text":"김*수 010-****-5678", ...}
태그크기(압축)내용
slim235 MB탐지 + 익명화 3종(default/hash/partial)
presidio300 MBslim + Presidio 어댑터·Anonymizer 연산자

두 태그 모두 amd64·arm64 를 함께 담은 멀티 아키텍처 이미지고, 탐지 결과는 서로 같습니다. 버전을 고정하려면 slim-1.0.0 이나 presidio-1.0.0 을 쓰세요.

엔드포인트는 /detect /mask /batch /healthz /labels /docs, 환경변수는 VEIL_THREADS VEIL_HASH_SALT VEIL_MAX_CHARS 입니다. 전체 태그는 https://hub.docker.com/r/zzang9680/veil-pii/tags 에 있습니다.

Presidio 어댑터

REST 응답은 두 이미지가 같습니다. 같은 문장을 넣으면 스팬도, hash 정책의 토큰 값도 똑같이 나옵니다. HTTP 로만 쓸 거면 slim 이면 되고, 아래 세 가지가 필요할 때만 presidio 를 쓰면 됩니다.

docker run -d -p 8080:8080 -e VEIL_HASH_SALT=my-secret --name veil zzang9680/veil-pii:presidio

# 1) Presidio 표준 엔티티 타입 — 쓰던 Presidio 파이프라인에 그대로 꽂힌다
docker exec veil python -c "from veil_pii.presidio import build_analyzer; t='담당자 김철수(010-1234-5678)에게 문의. 주민번호 900101-1234567'; [print(' ', r.entity_type, t[r.start:r.end], round(r.score,4)) for r in build_analyzer().analyze(text=t, language='ko')]"
#   PERSON 김철수 0.9999 / KR_RRN 900101-1234567 0.9999 / PHONE_NUMBER 010-1234-5678 0.9998

# 2) Anonymizer 연산자
docker exec veil python -c "
from veil_pii.presidio import build_analyzer,build_anonymizer,get_operators
t='담당자 김철수(010-1234-5678)에게 문의. 주민번호 900101-1234567'
r=build_analyzer().analyze(text=t,language='ko'); a=build_anonymizer()
[print(' ',p,'→',a.anonymize(text=t,analyzer_results=r,operators=get_operators(p)).text) for p in ('default','partial')]"
#   default → 담당자 <PERSON>(<PHONE_NUMBER>)에게 문의. 주민번호 <KR_RRN>
#   partial → 담당자 김*수(010-****-5678)에게 문의. 주민번호 900101-*******

# 3) 암호화와 복호화 (hash 와 달리 원문 복원 가능)
docker exec veil python -c "
from veil_pii.presidio import build_analyzer,build_anonymizer
from presidio_anonymizer import DeanonymizeEngine
from presidio_anonymizer.entities import OperatorConfig
K='0123456789abcdef0123456789abcdef'
t='담당자 김철수(010-1234-5678)에게 문의'
r=build_analyzer().analyze(text=t,language='ko')
e=build_anonymizer().anonymize(text=t,analyzer_results=r,operators={'DEFAULT':OperatorConfig('encrypt',{'key':K})})
d=DeanonymizeEngine().deanonymize(text=e.text,entities=e.items,operators={'DEFAULT':OperatorConfig('decrypt',{'key':K})})
print('복원:',d.text,'| 일치:',d.text==t)"
#   복원: 담당자 김철수(010-1234-5678)에게 문의 | 일치: True

partial 은 REST 경로와 자릿수 규칙을 맞춰 두었습니다. 두 경로의 부분 마스킹 결과가 달라지면 감사 대조가 어긋나기 때문입니다. default 와 hash 의 출력 표기는 Presidio 쪽이 다릅니다(꺾쇠 기호, 전체 SHA-256).

두 이미지를 직접 재 봤습니다(Apple Silicon, 74자 문장 20회).

slimpresidio
탐지(중앙값)13 ms13 ms
메모리304 MB360 MB
이미지(압축)235 MB300 MB

탐지 속도는 같고 메모리만 56MB 정도 더 씁니다.

zsh 에서 여러 줄짜리 heredoc 을 붙여 넣으면 터미널의 bracketed paste 제어문자가 섞여 들어가 zsh: bad pattern: [200~docker 가 날 수 있습니다. 위 예시는 python -c "..." 한 덩어리라 이 문제를 타지 않습니다. heredoc 을 쓰셔야 한다면 docker exec -i veil python < script.py 처럼 파일로 넘기시면 됩니다. 이때 -i 를 빼면 출력이 비어 나옵니다.

라벨 (32)

그룹라벨
사람·조직PERSON ORGANIZATION USER_ID
연락처·위치PHONE EMAIL ADDRESS ZIPCODE URL IPADDRESS MACADDRESS PORT
한국 신원 식별자RRN(주민번호) FRN(외국인등록번호) CI IPIN PASSPORT DRIVER_LICENSE BUSINESS_ID(사업자번호) SSN
금융CARD_NUMBER CARD_EXPIRY CVC VIRTUAL_CARD_NUMBER ACCOUNT_NUMBER TRANSACTION_APPROVAL_ID
기기·가입IMEI DEVICE_SERIAL SUBSCRIBER_ID VEHICLE_PLATE
기타DATE GENERIC_ID SECRET

BCCard 공개 데이터의 29종에 차량번호(VEHICLE_PLATE), 가입번호(SUBSCRIBER_ID), 단말 일련번호(DEVICE_SERIAL) 를 추가했습니다. 태깅은 BIOES 방식이라 실제 클래스 수는 129개이고, 라벨 매핑은 config.json 의 id2label 에 있습니다.

실사용 시나리오

상담이나 양식에서 나올 만한 문장 11개를 4개 모델에 똑같이 넣어 봤습니다. 맞힌 개수 / 정답 개수 이고 뒤의 +숫자 는 오탐입니다. 모델 간 차이가 난 항목만 추렸습니다.

시나리오VeilBCCard 1.4BFrameByFrame 1.4BAzure
줄바꿈으로 갈라진 주소 + 우편번호4/43/4 +22/4 +62/4 +2
사업자번호·CI·차량번호·가입번호 양식5/51/5 +61/5 +61/5 +1
조사 붙은 날짜·전화·이메일 (15일에, 5432로)4/43/43/44/4
직함 붙은 이름·아이디·이메일 (최수아 대리)3/31/33/32/3
긴 상담 로그 595자 (창 경계 넘김)16/1814/18 +65/18 +38/18 +10
이름 닮은 일반명사 — 정답은 무검출오탐 2오탐 2오탐 20
코드·해시·ISBN — 정답은 무검출오탐 2오탐 4오탐 50

틀린 것도 같이 적습니다. 주소 끝의 12층 을 빠뜨렸고, 영문 조직명을 못 잡았고, 대괄호 안 ISO 날짜와 지난달 25일 도 놓쳤습니다. 반대로 전결 을 사람 이름으로, 커밋 해시를 SECRET 으로, ISBN 을 ACCOUNT_NUMBER 로 잡는 오탐도 있었습니다.

데이터셋

직접 만든 데이터 두 가지를 따로 공개했습니다.

1T/veil-pii-ko-synth — 이 모델이 학습한 합성 데이터 26,875행과 위 벤치마크 표의 합성 heldout 670행, 그리고 값을 채우기 전 템플릿 10,133개입니다. 학습 당시 파일에서 그대로 꺼낸 것이라 공개 수치와 바로 맞춰 보실 수 있습니다. 문장은 LLM 으로 템플릿을 만들고, 개인정보 값은 규칙 기반 생성기로 채웠습니다.

1T/veil-pii-ko-stress — 표기 변형과 문서 길이를 바꿔 가며 어디서 무너지는지 보려고 만든 평가셋입니다.

구성내용행
stress하이픈→점·공백, 구분자 제거, 전각 숫자, 글자 사이 공백 6종 + 각 대조군5,376
longdoc정답 스팬(1,604)은 그대로 두고 길이만 200~3,000자로 늘린 문서1,301
leakageKDPII train/test 중복을 뺀 분할과 중복 행만 모은 분할9,782

정답 스팬을 그대로 두고 표면형만 바꿔 오프셋을 다시 계산했기 때문에, 라벨을 새로 달지 않아도 값이 곧 원본 대비 하락폭입니다. 위 표기 변형·문서 길이 절의 수치가 이 평가셋으로 잰 것입니다. 원문은 KDPII(CC-BY-4.0) 이고 변형 설계와 분할 정의가 이쪽 몫입니다. 다른 한국어 PII 모델에도 그대로 쓰실 수 있습니다.

재현

git clone https://github.com/WontaeKim89/veil-pii-ko-lite && cd veil-pii-ko-lite
pip install -r scripts/requirements-repro.txt
bash scripts/reproduce_claims.sh release   # 공개 데이터 다운로드 → Veil INT8/fp32 · BCCard 베이스라인 평가 → 표 (CPU 8스레드 ~40분)

파일

파일설명
model.safetensors + config.jsonfp32 가중치 (transformers)
model.int8.onnxweight-only INT8 ONNX (ORT ≥ 1.28), fp16 임베딩
veil.py디코더 (Veil 클래스), 단일 파일
tokenizer.json vocab.txt …KoELECTRA 토크나이저
labels.yaml32 라벨 스키마, KDPII 매핑
EVIDENCE.md주장별 근거와 측정 조건
PER_LABEL.md라벨별 P/R/F1 — 본 모델 · BCCard · Azure · FrameByFrame · 정규식 비교
ROBUSTNESS.md표기 변형 · 문서 길이 스트레스 결과
eval_*.json baselines/ stress/ regex/per-entity 원본 수치
reproduce_claims.sh requirements-repro.txt재현 스크립트

인용

@misc{veil-pii-ko-lite-2026,
  title  = {Veil-PII-Ko-Lite: a 110M Korean PII detector with 32 labels},
  author = {Kim, Wontae},
  year   = {2026},
  url    = {https://huggingface.co/1T/veil-pii-ko-lite}
}

라이선스 · 고지

Apache 2.0.

  • 학습 데이터: BCCard/privacy-filter-openpii-masking (CC-BY-4.0), KDPII (CC-BY-4.0, Fei·Kang et al., IEEE Access 2024), 자체 합성.
  • 백본: monologg/koelectra-base-v3-discriminator (Apache 2.0).
  • 비교 대상: BCCard/MoAI-Privacy-Filter-INT8, FrameByFrame/privacy-filter-korean (Apache 2.0), Azure AI Language PII (API 2024-11-01).

Как запустить

На своей GPU в ailist. Цена — по текущим предложениям провайдеров

GPU в ailist

Универсальный рантайм · transformers · token-classification

Можно запустить

Подбираем GPU…

Класс архитектуры
ElectraForTokenClassification
Ревизия весов
a834303

0 моделей