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)# pip install -U transformers accelerate # 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
- 24
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