Masker (v2)

masker is a multilingual token classifier for personally identifiable information (PII) in 23 European languages. It is the neural half of a two-part redaction system. This model tags contextual PII: names, addresses, ages and dates.

A separate, dependency-free rules layer handles structured PII (IBANs, cards, national IDs) with real checksum validation. Where both fire on the same span, the checksum-validated rule wins. Everywhere else, the model decides. This repository is the neural half and the full-accuracy reference model of the family.

📱 Need something small enough to run on a phone or in a web browser? See masker-mini, a 6-layer distillation shipped as ONNX and 4-bit Core ML, down to ~18 MB.

What's new in v2

v2 is about person names in real-world text. v1 was trained only on synthetic data. It scored 0.98 strict F1 on that data but missed nearly half of the name characters in real English court judgments.

out-of-distribution names v1 v2
names F1 (mean of both) 0.767 0.938
TAB (EN legal) leak recall 0.552 0.912
TAB strict F1 0.377 0.866
CoNLL-2002 NL leak recall 0.796 0.963
CoNLL-2002 NL strict F1 0.718 0.915

Two changes in the training data made the difference:

  • Name-form augmentation. openpii writes every person as "Given Surname". Real text mostly doesn't: 41% of v1's misses were bare surnames ("Price said…") and 37% were initials ("R. Ryssdal", "A.B. Baka"). v2 rewrites openpii name pairs into initials, title + surname ("mevrouw De Vries"), and bare-surname forms in all 23 languages, following each language's conventions (for example, Dutch tussenvoegsel capitalisation).
  • Real annotated text. v2 adds 293K sentences of human-annotated NER in 14 languages (listed under Credits). Person annotations are used as gold. The other entities (companies, places, works) teach the model which capitalised words are not people.

Dutch improved as much as English even though no real Dutch text was in the mix. The gain transferred from augmentation and the other languages.

Also changed:

  • v2 keeps mDeBERTa's full 250K vocabulary. v1 pruned it to the tokens in its synthetic training corpus. On real text, rare pieces matter: a 64K prune of v2 cost 3 points of out-of-distribution names F1. The weights are bfloat16 (~557 MB).
  • Same 12 entity types and label map as v1, so it's a drop-in replacement. v1 is kept under the v1 tag.

Model type & training

Masker is a DeBERTa-v3 token classifier (278M parameters) fine-tuned from microsoft/mdeberta-v3-base with boundary-first BIOES supervision. It was trained for 1 epoch on 1.44M rows: name-form-augmented ai4privacy/pii-masking-openpii-1m plus the real-text corpora. In the real text, person spans come from the gold annotations (split into given name and surname by masker v1, or by rule), and the other PII types are pseudo-labelled by masker v1.

The head predicts 12 entity types (48 BIOES labels + O):

GIVEN_NAME, SURNAME, CITY, STREET_NAME, BUILDING_NUMBER, ZIP_CODE, PHONE, EMAIL, CREDIT_CARD, DATE, AGE, GOVERNMENT_ID.

GOVERNMENT_ID covers passport, national-ID, SSN, tax and driver-license numbers; the rules layer tells them apart by checksum and format. Other structured types (IBAN, IP, URL, etc.) are owned entirely by the rules layer and are not part of this head.

Usage

from transformers import AutoTokenizer, AutoModelForTokenClassification
import torch

tok = AutoTokenizer.from_pretrained("divergentlabs/masker")
model = AutoModelForTokenClassification.from_pretrained("divergentlabs/masker").eval()

text = "Kundin Beatrix Möller, geb. 04.07.1979, Rosenweg 8, 50667 Köln."
enc = tok(text, return_tensors="pt", return_offsets_mapping=True)
offsets = enc.pop("offset_mapping")[0]
with torch.no_grad():
    labels = model(**enc).logits.argmax(-1)[0]
for (a, b), lab in zip(offsets.tolist(), labels.tolist()):
    if b > a and model.config.id2label[lab] != "O":
        print(text[a:b].strip(), "->", model.config.id2label[lab])

The raw output is per-token BIOES labels. The companion mask package turns them into character spans and merges them with the rules layer. The tokenizer's offsets include the leading space of word-initial pieces, so trim whitespace from span edges.

Evaluation

Out-of-distribution: person names on real text

These scores come from real documents that neither masker nor redact trained on. Metrics are person-level (given name + surname merged into one span) and character-exact.

  • TAB: the Text Anonymization Benchmark, 127 English ECHR court judgments.
  • CoNLL-2002 NL: Dutch news, test split. Used for evaluation only.
model names F1 (mean) TAB overlap F1 TAB leak recall CoNLL NL overlap F1 CoNLL NL leak recall CoNLL NL false-person rate
masker v2 0.938 0.949 0.912 0.928 0.963 2.1%
masker v1 0.767 0.707 0.552 0.827 0.796 4.5%
masker-mini v2 0.774 0.833 0.840 0.716 0.897 16.4%
nationaldesignstudio/rampart 0.690 0.769 0.757 0.611 0.743 11.0%
desert-ant-labs/redact 0.626 0.498 0.361 0.755 0.794 7.3%

redact v0.4.0 was run through its own SDK pipeline (min confidence 0.6 plus its rules).

rampart v0.1.3 was run through its own SDK (createGuard().detect() with its default policy, the spans protect() redacts). On long documents it returns some paragraph-sized spans, so its TAB strict F1 is only 0.04 and the overlap figures above flatter it.

  • Leak recall is the fraction of gold name characters that get redacted.
  • False-person rate is the share of other named entities (companies, places) wrongly tagged as a person.

In-distribution: openpii-1m validation

These are span-level, boundary-exact scores on 8,000 synthetic openpii validation documents.

metric v2 v1
Strict span F1 0.980 0.982
Typed F1 0.994 0.995
Typed precision 0.994 0.994
Typed recall 0.995 0.996
Leak-safe recall 0.9995 0.9996

Per-type strict F1

entity F1 entity F1
EMAIL 1.000 CITY 0.995
DATE 0.999 STREET_NAME 0.993
PHONE 0.999 BUILDING_NUMBER 0.992
GOVERNMENT_ID 0.999 AGE 0.985
CREDIT_CARD 0.998 GIVEN_NAME 0.932
ZIP_CODE 0.997 SURNAME 0.923

v2 gives up 0.2 points of strict F1 on synthetic text, all of it on name boundaries, in exchange for +17 points of names F1 on real text. Synthetic openpii and real corpora don't always agree on where a name span starts and ends.

Limitations & biases

  • The out-of-distribution sets cover English legal text and Dutch news only. Other languages were trained on real text but have not been measured out-of-distribution. Validate on your own data before relying on the model.
  • The remaining TAB misses are mostly partial spans: overlap F1 is 0.949 but strict F1 is 0.866.
  • Not a substitute for review in high-stakes settings. No detector is perfect, so design for residual leakage.

Credits & attribution

This model is a derivative work and would not exist without:

Please retain these credits in downstream use.

License

Licensed under the Apache License, Version 2.0. See LICENSE and NOTICE.

The underlying components keep their own terms, listed in Credits above.

Versions before v2 (the v1 tag) were released under the Offchain Studio Source License, Version 1.0, and remain under those terms.

Citation

@software{masker,
  title   = {masker: multilingual PII detection for 23 European languages},
  year    = {2026},
  version = {2},
  note    = {DeBERTa-v3 token classifier, boundary-first BIOES supervision},
  url     = {https://huggingface.co/divergentlabs/masker}
}
Downloads last month
38
Safetensors
Model size
0.3B params
Tensor type
BF16
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for divergentlabs/masker

Finetuned
(307)
this model
Finetunes
1 model

Datasets used to train divergentlabs/masker

Collection including divergentlabs/masker

Paper for divergentlabs/masker

Evaluation results

  • Strict span F1 on pii-masking-openpii-1m (validation)
    self-reported
    0.980
  • Typed F1 on pii-masking-openpii-1m (validation)
    self-reported
    0.994
  • Leak-safe recall on pii-masking-openpii-1m (validation)
    self-reported
    1.000
  • Person overlap F1 on TAB (ECHR court judgments, EN, test)
    self-reported
    0.949
  • Person leak recall on TAB (ECHR court judgments, EN, test)
    self-reported
    0.912