/ Модель

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

Контекст
512 токенов
Лицензия
Apache 2.0
Архитектура
distilbert · dense
Библиотека
onnxruntime
Опубликована
9 сен 2026

piiscan

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

Мультиязычная модель на базе DistilBERT для поиска персональных данных в тексте с возвратом точных символьных смещений найденных фрагментов.

95скачиваний за 30 дней1лайквеса обновлены11 сен 2026

Бенчмарки

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

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

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

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

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

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

PiiScan — мультиязычная модель для выявления персональных данных в тексте. Её разработал Arnab Pal на основе distilbert-base-multilingual-cased: это DistilBERT для классификации токенов с разметкой BIO по 27 меткам, из которых 13 типов сущностей обучены на данных, а ещё 11 выявляются отдельными правилами в сопутствующем сервисе. Модель распространяется в форматах ONNX и safetensors; вариант ONNX с INT8-квантизацией рассчитан на работу через onnxruntime на CPU и без подключения к сети. Контекст — до 512 токенов.

Модель находит фрагменты персональных данных и возвращает их тип и точные смещения символов в исходной строке. Среди обученных типов — имена, адреса, телефоны, даты, электронная почта и номера документов; README описывает поддержку 30 языков. Репозиторий также упоминает отдельные правила проверки форматов и REST API для редактирования данных, но это возможности сопутствующего пакета, а не самой модели.

На тесте, созданном тем же генератором, что и обучающие данные, результаты высокие; на размеченных реальных судебных документах качество заметно ниже. Модель не предназначена для HIPAA-деидентификации или обработки медицинских идентификаторов, слабо поддерживает длинную реальную прозу и не рассчитана на языки с письмом деванагари и бенгали. Для ответственной редактуры персональных данных нужна проверка человеком. Лицензия — Apache-2.0; обучающие данные получены из Ai4Privacy OpenPII 1.5M под CC-BY-4.0.

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

  • поиск имён, адресов, телефонов и электронной почты в документах
  • обнаружение номеров удостоверений и платёжных карт
  • поиск персональных данных в многоязычном тексте
  • подготовка документов к обезличиванию с последующей проверкой человеком

Ограничения

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

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

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

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

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

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

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

Открыть на Hugging Face

PiiScan — multilingual PII detection with exact character offsets

For any input text, PiiScan returns every personal-data span it finds, with the exact character offsets into your original string. It ships as a CPU-only INT8 ONNX model (~139 MB with tokenizer) that runs fully offline — no GPU, no network.

The guarantee everything rests on: original_text[entity.start:entity.end] == entity.value.

  • Base model: distilbert-base-multilingual-cased
  • Task: token classification, BIO over 27 labels (13 entity types)
  • Types: 13 model-trained + 11 rule-only = 24 usable; 3 experimental, 12 blocked
  • Languages: 30 (no Indic scripts — see limitations)
  • Runtime: onnxruntime (CPU), ~13 ms / document
  • Offsets: Unicode code points, zero-based, end-exclusive

Results — held-out test set (policy balanced, exact-span match)

TypePrecisionRecallF1Support
POSTAL_CODE0.9970.9950.9962,566
PAYMENT_CARD_NUMBER0.9950.9930.9941,894
CITY0.9930.9910.9924,146
PHONE_NUMBER0.9880.9960.9923,361
EMAIL_ADDRESS0.9870.9880.9884,400
STREET_ADDRESS0.9890.9780.9833,909
AGE0.9920.9620.9772,991
TAX_ID0.9900.9410.9651,540
DRIVER_LICENSE_NUMBER0.9840.9400.9611,662
PERSON_NAME0.9760.9310.9538,810
DATE_TIME0.9800.9220.9509,592
PASSPORT_NUMBER0.9770.9170.9461,260
GOVERNMENT_ID0.9770.9120.9433,439
macro-F10.972
micro-F10.9850.9540.96949,570

Scored at document level on 8,328 held-out documents, exact [start, end) and type match.

These are in-distribution numbers. The test split comes from the same generator as the training data. On 127 human-annotated real court judgments in the same split, the two types those documents annotate score considerably lower:

SlicePrecisionRecallF1
Same generator as training0.9900.9780.984
Real human-annotated documents0.8890.6410.745

Expect the lower number on your own data. Validate in your domain before relying on it.

Entity tiers — what is actually supported

Not all types are equal, and this card says so rather than advertising one big number.

TierCountTypes
Model-trained13AGE, CITY, DATE_TIME, DRIVER_LICENSE_NUMBER, EMAIL_ADDRESS, GOVERNMENT_ID, PASSPORT_NUMBER, PAYMENT_CARD_NUMBER, PERSON_NAME, PHONE_NUMBER, POSTAL_CODE, STREET_ADDRESS, TAX_ID
Rule-only (checksums/format)11AADHAAR_NUMBER, CRYPTO_WALLET, DEVICE_IDENTIFIER, FAX_NUMBER, GEO_COORDINATES, IBAN, IP_ADDRESS, MAC_ADDRESS, SOCIAL_SECURITY_NUMBER, SWIFT_BIC, VEHICLE_IDENTIFIER
Experimental (opt-in)3COUNTRY, URL, USERNAME
Blocked (insufficient data)12ADDRESS, BANK_ACCOUNT_NUMBER, BIOMETRIC_IDENTIFIER, COUNTY_DISTRICT, DATE_OF_BIRTH, HEALTH_PLAN_ID, LICENSE_PLATE, MEDICAL_RECORD_NUMBER, OTHER_UNIQUE_ID, PROVIDER_ID, STATE_PROVINCE, VOTER_ID

Rule-only types are excluded from the model by design. MOD-97 validates an IBAN exactly; training a network to approximate a checksum would be strictly worse.

Quick start (Python + onnxruntime)

pip install onnxruntime tokenizers huggingface_hub numpy
import json, numpy as np, onnxruntime as ort
from huggingface_hub import hf_hub_download
from tokenizers import Tokenizer

REPO   = "vectorsense/piiscan"
sess   = ort.InferenceSession(hf_hub_download(REPO, "model.int8.onnx"),
                              providers=["CPUExecutionProvider"])
tok    = Tokenizer.from_file(hf_hub_download(REPO, "tokenizer.json"))
labels = json.loads(open(hf_hub_download(REPO, "labels.json"), encoding="utf-8").read())

def detect(text: str, threshold: float = 0.75) -> list[dict]:
    enc    = tok.encode(text)
    ids    = np.array([enc.ids], dtype=np.int64)
    mask   = np.array([enc.attention_mask], dtype=np.int64)
    feeds  = {i.name: (mask if "mask" in i.name else ids) for i in sess.get_inputs()}
    logits = sess.run(None, feeds)[0][0]
    e      = np.exp(logits - logits.max(-1, keepdims=True))
    probs  = e / e.sum(-1, keepdims=True)

    spans, current = [], None
    for i, (start, end) in enumerate(enc.offsets):
        if start == end:                       # special token
            continue
        label = labels[int(probs[i].argmax())]
        if label == "O":
            current = None
            continue
        prefix, entity_type = label.split("-", 1)
        if prefix == "B" or current is None or current["type"] != entity_type:
            current = {"type": entity_type, "start": start, "end": end,
                       "scores": [float(probs[i].max())]}
            spans.append(current)
        else:
            current["end"] = end
            current["scores"].append(float(probs[i].max()))

    out = []
    for s in spans:
        score = sum(s["scores"]) / len(s["scores"])
        if score >= threshold:
            out.append({"type": s["type"], "start": s["start"], "end": s["end"],
                        "value": text[s["start"]:s["end"]], "score": round(score, 4)})
    return out

for entity in detect("Patient John Smith was seen on 01/15/2026 at 80331 München"):
    print(entity)
# {'type': 'PERSON_NAME', 'start': 8, 'end': 18, 'value': 'John Smith', 'score': 0.8427}
# {'type': 'DATE_TIME', 'start': 31, 'end': 41, 'value': '01/15/2026', 'score': 0.9948}
# {'type': 'POSTAL_CODE', 'start': 45, 'end': 50, 'value': '80331', 'score': 0.9925}

This snippet is the model only, single window. It does not include the rule layer (IBAN, Aadhaar, payment-card checksums), sliding windows for long documents, or the hybrid resolver. For those, use the package below.

Run it as a REST API (request / response service)

The companion PiiScan repo ships a FastAPI service that wraps this model with the rule layer, policy presets, nested spans, and redaction.

pip install fastapi "uvicorn[standard]" onnxruntime tokenizers numpy pyyaml
uvicorn service.app:app --port 5001      # auto-loads model.int8.onnx from ./models
curl -s http://127.0.0.1:5001/detect_pii \
  -H "Content-Type: application/json" \
  -d '{"texts":["Contact: jsmith@email.com or 555-123-4567"]}'
# PowerShell — send UTF-8 bytes, or non-ASCII input is mangled by Windows PowerShell 5.1
$body  = '{"texts":["Contact: jsmith@email.com or 555-123-4567"]}'
$bytes = [Text.Encoding]::UTF8.GetBytes($body)
Invoke-RestMethod -Uri http://127.0.0.1:5001/detect_pii -Method Post `
  -ContentType 'application/json; charset=utf-8' -Body $bytes

Response fields per entity: entitytype, start, end, value, score, length, subtype, source (rule / model / hybrid), policy_tags, parent_id. Endpoints: GET /, POST /detect_pii, GET /health/ready, GET /metadata, POST /redact.

Policy presets

strict (0.50) / balanced (0.75) / precision (0.90) set the score threshold; override per request with options.score_threshold. Published results use balanced.

Scores are not calibrated probabilities — 0.90 does not mean 90% correct. Rule-layer spans carry a fixed confidence by evidence strength, not a learned score. See thresholds.json.

Training record

Full provenance lives in this repo under training/ (best checkpoint, configs, dataset manifest, source licences, rule-layer benchmark). Trained 3 epochs on 1,299,487 rows, 4 h 20 m on one RTX 2000 Ada.

Limitations & licensing

  • No PHI claim. MEDICAL_RECORD_NUMBER and HEALTH_PLAN_ID are blocked for lack of data. This is not a HIPAA de-identification tool.
  • No Indic support. The corpus has 30 languages but zero Devanagari or Bengali script. Aadhaar and PAN still work inside Indic text because those formats are script-independent.
  • Recall is never 1.0. High-stakes redaction needs human review.
  • Long-form real-world prose is weaker than the headline table. Person names in legal documents are the known worst case; initials and single-letter anonymised parties are frequently missed.
  • Place names can be tagged as people, occasionally with high confidence.
  • CJK has a small span-alignment ceiling from wordpiece merging across entity boundaries.
  • Training data derived from Ai4Privacy OpenPII 1.5M (CC-BY-4.0), © Ai Suisse SA — https://huggingface.co/datasets/ai4privacy/pii-masking-openpii-1.5m Evaluation also uses the Text Anonymization Benchmark (MIT).
  • Only a redistributable subset of the corpus is published — see the dataset card.

Author

Arnab Pal — LinkedIn · GitHub

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

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

GPU в ailist

Универсальный рантайм · onnxruntime

Нельзя запустить

Модель выложена только в формате ONNX.

Класс архитектуры
DistilBertForTokenClassification
Ревизия весов
87e6ff6

0 моделей