Instructions to use schift-io/schift-ko-pii-v5 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use schift-io/schift-ko-pii-v5 with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("token-classification", model="schift-io/schift-ko-pii-v5", trust_remote_code=True)# Load model directly from transformers import AutoTokenizer, AutoModelForTokenClassification tokenizer = AutoTokenizer.from_pretrained("schift-io/schift-ko-pii-v5", trust_remote_code=True) model = AutoModelForTokenClassification.from_pretrained("schift-io/schift-ko-pii-v5", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
schift-ko-pii-v5
34M parameter Korean PII detector โ 3x smaller, beats the 111M v4.
Hybrid detection: the model handles person names and addresses, the SDK postprocessor handles structured patterns (phone numbers, ID numbers, dates, URLs). 136 MB, CPU-only.
Benchmark (benchmark_v2, 253 cases)
Compared against every public Korean PII model on HuggingFace (as of Aug 2026).
| Model | Params | F1 | P | R |
|---|---|---|---|---|
| OpenMed-PII-Korean-NomicMed-Large | 395M | 0.041 | 0.029 | 0.071 |
| OpenMed-PII-Korean-QwenMed-XLarge | 600M | 0.084 | 0.061 | 0.138 |
| LFM2.5-Encoder-350M-PII | 350M | 0.254 | 0.207 | 0.328 |
| FrameByFrame/korean-pii-e5-base | ~110M | 0.435 | 0.319 | 0.684 |
| seungkukim/korean-pii-masking-v2 | ~110M | 0.553 | 0.672 | 0.470 |
| vmaca123/korean-pii-ner-v3 | ~125M | 0.569 | 0.810 | 0.439 |
| schift-ko-pii-v4 | 111M | 0.702 | 0.602 | 0.842 |
| schift-ko-pii-v5 | 34M | 0.823 | 0.812 | 0.834 |
All models evaluated raw (no postprocessing) except v4/v5 which use the SDK postprocessor (regex for structured patterns). v5 with postprocess disabled scores F1=0.578 (P=0.847, R=0.439) โ still competitive, and the hybrid approach pushes it to #1.
Not included: bbanany/qwen25-3b-korean-pii-* (3B CausalLM, binary PII/NOT_PII classifier on pre-extracted spans โ different task, not NER), upgle/bert-pii-korean and alphagyuu/* (no model weights uploaded), psh3333/* (gated), mncai/* (broken config).
v5 is 3x smaller than v4 and scores higher on the harder benchmark.
Raw model (no postprocess)
| Model | Params | raw F1 | raw P | raw R |
|---|---|---|---|---|
| LFM2.5-350M-PII | 350M | 0.254 | 0.207 | 0.328 |
| schift-ko-pii-v4 | 111M | 0.702 | 0.602 | 0.842 |
| schift-ko-pii-v5 | 34M | 0.578 | 0.847 | 0.439 |
v5 raw recall is low because the model only predicts private_person and private_address. All other categories (phone, email, dates, IDs) are handled by the regex postprocessor โ by design, not by limitation.
Category breakdown (benchmark_v1, 93 cases)
| Category | v5 (34M) | v4 (111M) | LFM2.5 (350M) |
|---|---|---|---|
| Person (standard) | 1.00 | 1.00 | 0.67 |
| Person (rare surnames) | 1.00 | 1.00 | 0.60 |
| Person (short names) | 1.00 | 1.00 | 0.57 |
| Address (urban) | 1.00 | 1.00 | 0.67 |
| Address (rural) | 1.00 | 1.00 | 0.60 |
| Phone (via postprocess) | 0.86 | 0.86 | 0.92 |
| Vehicle plate | 1.00 | 1.00 | 0.00 |
| Date (Korean) | 1.00 | 1.00 | 0.00 |
| Resident ID | 1.00 | 1.00 | 0.75 |
Installation
pip install schift-ko-pii
Usage
1. Basic detection
from schift_ko_pii import detect
# Raw model output (person + address only)
spans = detect("ํผ๊ณ ๊น๋ฏผ์์ ์ ํ๋ฒํธ๋ 010-1234-5678์ด๋ค.")
for s in spans:
print(f" [{s['id']}] {s['label']}: {s['text']} (score: {s['score']})")
# [person_1] private_person: ๊น๋ฏผ์ (score: 0.99)
# With postprocessing โ adds regex-based detection for phone, ID, dates, etc.
spans = detect("ํผ๊ณ ๊น๋ฏผ์์ ์ ํ๋ฒํธ๋ 010-1234-5678์ด๋ค.", postprocess=True)
# [person_1] private_person: ๊น๋ฏผ์
# [phone_1] phone_number: 010-1234-5678
2. Mask and review
from schift_ko_pii import mask
result = mask(
"ํผ์์ ๊น์ฒ ์(์ฃผ๋ฏผ๋ฒํธ 850205-1234567)๊ฐ ํผํด์ ๋ฐ์ํฌ์๊ฒ "
"์์ธํน๋ณ์ ๊ฐ๋จ๊ตฌ ํ
ํค๋๋ก 521์์ ๊ธ 3,000๋ง์์ ์ ๋ฌํ์๋ค.",
postprocess=True
)
print(result["masked"])
# ํผ์์ [์ฌ๋1](์ฃผ๋ฏผ๋ฒํธ [์ฃผ๋ฏผ๋ฒํธ1])๊ฐ ํผํด์ [์ฌ๋2]์๊ฒ
# [์ฃผ์1]์์ ๊ธ 3,000๋ง์์ ์ ๋ฌํ์๋ค.
# Entity map โ review what was detected
for e in result["entities"]:
print(f" {e['id']:>15}: {e['text']}")
# person_1: ๊น์ฒ ์
# resident_id_1: 850205-1234567
# person_2: ๋ฐ์ํฌ
# address_1: ์์ธํน๋ณ์ ๊ฐ๋จ๊ตฌ ํ
ํค๋๋ก 521
3. Selective replacement
from schift_ko_pii import mask, apply
text = ("๊ณ์ฝ์ ๋จ๊ถํ์ง(010-9876-5432)์ ๊ฑฐ์ฃผ์ง "
"๋ถ์ฐ๊ด์ญ์ ํด์ด๋๊ตฌ ์ฐ๋ 1414๋ฒ์ง ๋ง๋ฆฐ์ํฐ 102๋ 305ํธ์์ "
"์ฐธ๊ณ ์ธ Mike Johnson์ ๋ฉด๋ดํ์๋ค.")
result = mask(text, postprocess=True)
# User reviews and decides what to redact
redacted = apply(text, result["entities"], {
"person_1": "โโโ", # ๋จ๊ถํ์ง โ โโโ
"person_2": "์ธ๊ตญ์ธA", # Mike Johnson โ ์ธ๊ตญ์ธA
"phone_1": "010-****-****",
# address_1 intentionally left unmasked
})
print(redacted)
# ๊ณ์ฝ์ โโโ(010-****-****)์ ๊ฑฐ์ฃผ์ง
# ๋ถ์ฐ๊ด์ญ์ ํด์ด๋๊ตฌ ์ฐ๋ 1414๋ฒ์ง ๋ง๋ฆฐ์ํฐ 102๋ 305ํธ์์
# ์ฐธ๊ณ ์ธ ์ธ๊ตญ์ธA์ ๋ฉด๋ดํ์๋ค.
4. Bulk masking (replace all with ***)
from schift_ko_pii import mask, apply
text = "ํผํด์ ์ด๋(์ฌ๊ถ M12345678)๋ ์์ธ ์์ด๊ตฌ ๋ฐํฌ๋๋ก 58์ ๊ฑฐ์ฃผํ๋ค."
result = mask(text, postprocess=True)
# Replace everything with ***
clean = apply(text, result["entities"])
# ํผํด์ ***(์ฌ๊ถ ***)๋ ***์ ๊ฑฐ์ฃผํ๋ค.
5. Using transformers directly
from transformers import AutoModelForTokenClassification, AutoTokenizer, pipeline
model = AutoModelForTokenClassification.from_pretrained(
"schift-io/schift-ko-pii-v5", trust_remote_code=True
)
tokenizer = AutoTokenizer.from_pretrained("schift-io/schift-ko-pii-v5")
pipe = pipeline("token-classification", model=model, tokenizer=tokenizer,
aggregation_strategy="simple")
results = pipe("์ฐธ๊ณ ์ธ ์ ๊ฐ๋์ ์ฆ์ธ์ ์ฑํํ๋ค.")
for r in results:
print(f" {r['entity_group']}: {r['word']} ({r['score']:.2f})")
# private_person: ์ ๊ฐ๋ (0.95)
6. Processing files
from schift_ko_pii import mask, apply
with open("document.txt") as f:
text = f.read()
result = mask(text, postprocess=True)
# Show what was found
print(f"Found {len(result['entities'])} PII entities:")
for e in result["entities"]:
print(f" {e['id']}: {e['text']}")
# Redact all and save
clean = apply(text, result["entities"])
with open("document_redacted.txt", "w") as f:
f.write(clean)
API (free, no model download needed)
from schift import Schift
client = Schift(api_key="...") # free at schift.io
result = client.pii.redact("๊น๋ฏผ์์ ์ ํ๋ฒํธ๋ 010-1234-5678์
๋๋ค.")
# Postprocessing is always enabled on the API.
Labels
| Label | Description | Detected by | Examples |
|---|---|---|---|
private_person |
Person names (Korean, Hanja, foreign) | Model | ๊น๋ฏผ์, ๋จ๊ถํ์ง, Mike Johnson, ็ฐไธญๅคช้ |
private_address |
Street/postal addresses | Model | ์์ธํน๋ณ์ ๊ฐ๋จ๊ตฌ ํ ํค๋๋ก 521 |
phone_number |
Phone numbers | Postprocess (regex) | 010-1234-5678, 02-1234-5678 |
resident_id |
Resident registration numbers | Postprocess (regex + checksum) | 850205-1234567 |
account_number |
Bank accounts, passport, vehicle plates, business IDs | Postprocess (regex) | M12345678, 52๊ฐ1234 |
private_date |
Dates in Korean format | Postprocess (regex) | 2024๋ 3์ 15์ผ |
private_url |
URLs, IP addresses | Postprocess (regex) | 192.168.1.1 |
secret |
API keys, passwords | Postprocess (regex) |
Architecture
- Encoder: Conv + attention hybrid, 6 layers, hidden 512
- Head: Linear classifier (512 โ 33 labels)
- Params: 34M
- Format: safetensors float32 (136 MB)
- Tokenizer: klue/roberta-base
- Max length: 512 tokens
- Tagging: BIES (Begin/Inside/End/Single)
- Requires:
trust_remote_code=True
Running benchmarks
python benchmark/run_benchmark.py --benchmark benchmark/benchmark_v2.jsonl --postprocess
License
Schift License v2.0 โ Apache 2.0 base with revenue threshold. Free for everyone under $10M annual revenue. Research, education, and non-profit always permitted.
Citation
@software{schift_ko_pii_v5_2026,
author = {Schift Inc.},
title = {schift-ko-pii-v5: 34M Korean PII Detection Model},
year = {2026},
url = {https://huggingface.co/schift-io/schift-ko-pii-v5},
}
- Downloads last month
- -
Evaluation results
- F1 (with postprocess) on benchmark_v2 (253 cases)self-reported0.823
- Precision (with postprocess) on benchmark_v2 (253 cases)self-reported0.812
- Recall (with postprocess) on benchmark_v2 (253 cases)self-reported0.834