Instructions to use amyrmahdy/decima-pii with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use amyrmahdy/decima-pii with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("token-classification", model="amyrmahdy/decima-pii")# pip install -U transformers accelerate # Load model directly from transformers import AutoTokenizer, AutoModelForTokenClassification tokenizer = AutoTokenizer.from_pretrained("amyrmahdy/decima-pii") model = AutoModelForTokenClassification.from_pretrained("amyrmahdy/decima-pii", device_map="auto") - Notebooks
- Google Colab
- Kaggle
decima-pii v0.2.1 (preview)
Try it in your browser: https://amyrmahdy-decima-playground.static.hf.space/pii.html
Finds personal data in Arabic, Persian, Urdu, Turkish and English text, on a CPU, in about 13 ms per message (6 threads). Names, phone numbers, emails, national IDs, IBANs, card numbers, passports, addresses, dates of birth and IP addresses, including numbers written in Arabic-Indic or Persian digits (۰۹۱۲…, ٠٥٥…).
I built it because the tools I had treat these languages as an afterthought. In my tests Presidio's defaults missed close to half of the values written in Persian or Arabic-Indic digits, and OpenAI's privacy filter scored well below its English level on Arabic, Persian and Turkish (table below). This model is small (140M parameters, 148 MB as int8), runs offline, and nothing you scan leaves your machine.
This is version 0.2. Same size and speed as 0.1, better on most outside tests, a little worse on a few (listed below).
For the previous version, pin revision="v0.1".
v0.2.1 note. Same model as 0.2, one new decoding rule (decoder="v2.1", now the default): a number right after a
phone word such as جوالي, موبايل, شماره موبایلم or cep, that has the shape of a real phone number, is marked as a phone
when the model missed it. On my own probe of such messages, recall on Gulf-dialect phones went from 0.83 to 0.96. On
the outside sets nothing moved by more than 0.0001 (worst: one Turkish student number now tagged as a phone). I wrote
this rule after looking at those results, so treat it as a fix, not a clean test. Numbers that look like phones but
are not (order numbers, serials, codes next to a phone word) are still a weak spot of the model itself.
revision="v0.2" keeps the v0.2 decoding exactly.
Results on a test set I did not make
MultiGraSCCo (Baroud et al., LREC 2026) is a set of clinical letters translated into ten languages, built by other researchers. decima-pii never trained on it. I scored the five types it shares with this model (names, phones, emails, addresses, dates of birth; 1,814 spans), relaxed typed F1 as in their paper.
| all 4 | English | Arabic | Persian | Turkish | recall | |
|---|---|---|---|---|---|---|
| decima-pii v0.2 | 0.910 | 0.957 | 0.913 | 0.849 | 0.928 | 0.98 |
| decima-pii v0.1 | 0.870 | 0.957 | 0.843 | 0.805 | 0.889 | 0.96 |
| openai/privacy-filter | 0.647 | 0.901 | 0.581 | 0.508 | 0.626 | 0.66 |
| Presidio (English defaults) | 0.117 | 0.357 | 0.048 | 0.060 | 0.110 | 0.47 |
"Lenient" scoring: a prediction on real personal data that this test does not label for these five types (a clinic name, a visit date) is not counted as a false positive. Strict scoring, which does count them: v0.2 0.871, v0.1 0.814, privacy filter 0.415, Presidio 0.099. Their paper's own models score higher on this set, but they were trained on it and are scored on all of its types, so the numbers are not comparable.
How blind is this? No text, label or name from MultiGraSCCo was used in training. But I have looked at it more than once. An earlier version (pii1) was tested on it first, and its mistakes led me to add clinical-letter-style training text; that fully blind version scored 0.652. Since then it has been one of the checks every new version had to pass. So read 0.910 as "measured after a few looks".
A fresh test, run once
Because of that, for v0.2 I set aside a test set that none of my models had seen: Sitr (Apache-2.0): 1,793 test messages, nearly all Arabic, from Saudi Arabia, the Gulf and the Levant, with made-up identifiers. Before any v0.2 data or training existed I froze the file, wrote down how it would be scored and the rule (v0.2 must score at least what v0.1 scores), and ran v0.1 on it once. I then trained three seeds, picked one with a selection rule also written down in advance, and ran that one model on Sitr once.
| Sitr test, 8 types (no names: Sitr has no name label) | v0.2 | v0.1 |
|---|---|---|
| relaxed typed F1, lenient (the pre-registered number) | 0.875 | 0.856 |
| relaxed typed F1, strict | 0.796 | 0.782 |
| exact span and type | 0.767 | 0.763 |
| messages with no personal data that got flagged | 4 of 380 | 9 of 380 |
The gain comes from national IDs (0.780 → 0.815), phones (0.892 → 0.922) and cards (0.948 → 0.966). Addresses (0.607 → 0.576) and passports (0.956 → 0.932) got a little worse. This is one Arabic set without names, so it says nothing about Persian, Urdu, Turkish or names.
More test sets made by other people
Scored zero-shot: none of these were used in training, with no exact text overlap and almost no template overlap (0–1 rows per set). All of them had been looked at before v0.2 was trained, so the v0.2 gains here are also "after a look". Each set's own system was trained on that set, and it beats decima-pii on it.
| set | language | v0.2 | v0.1 | the set's own best (in-domain) |
|---|---|---|---|---|
| Bayanat Labs arabic-pii-eval, test (1,000 sentences; MSA, Gulf, Egyptian; Arabic-Indic and Persian digits), exact typed F1 | ar | 0.980 | 0.953 | rules on ID formats 0.98–1.00, names 0.874 |
| Reza Sayar's persian-pii-masking-openpii, test, first 2,000 rows, relaxed typed F1 | fa | 0.875 | 0.881 | Sayar's mBERT, 0.974 |
| Boran Toktay's turkish-privacy-pii-ner, test (10,393 rows), relaxed typed F1 | tr | 0.900 | 0.886 | Toktay's fine-tuned filter, 0.998 |
| REDACT (Vats et al., EMNLP 2026 Industry), locked split, Arabic/Turkish/English records (51 types; 41 are not ours and count as misses), partial micro-F1 | ar / tr / en | 0.53 / 0.51 / 0.50 | 0.51 / 0.50 / 0.49 | Claude Sonnet 4.6 0.68 / 0.59 / 0.60; OpenAI privacy filter as published by the authors 0.44 / 0.39 / 0.41 |
| Urdu NER (mirfan899), test, 2,000 seeded sentences, person names only, relaxed F1 | ur | 0.743 | 0.679 | no PII system; OpenAI privacy filter 0.249 on the same rows (my run) |
The Urdu row is a stand-in: I have no Urdu PII test, so this is a news NER set scored on person names only. Every non-name prediction counts against precision there, and its licence is not stated.
On Bayanat, v0.2 now finds all Kuwaiti civil IDs and Bahraini CPRs (v0.1: about 2 in 3) and flags none of the 200 sentences without personal data (v0.1: 16 of 200). On Hasan Çağrı Güngör's Turkish benchmark it leaves all 200 look-alike rows alone, including the ALL-CAPS ones (v0.1 marked 2 of those as names).
Where it loses: Sayar's Persian set dropped slightly (0.881 → 0.875), mostly more dates read as dates of birth and some long street-plus-postcode addresses missed or split. Addresses are its weakest type in general: exact boundaries in Persian, Turkish and Gulf address formats are often off. Persian is its weakest language on MultiGraSCCo (0.849).
Results on our own test (synthetic, 6,000 messages)
Held-out templates, names and random seeds; balanced over the five languages, with 9,252 look-alike values that are not personal data (order numbers, hotlines, product codes, flight numbers).
| typed F1 | finds the value at all | look-alike values wrongly hit | messages with no PII flagged | |
|---|---|---|---|---|
| decima-pii v0.2 (int8) | 0.994 | 0.997 | 0.1% | 0.6% |
| decima-pii v0.1 (int8) | 0.994 | 0.998 | 0.1% | 0.3% |
| openai/privacy-filter | 0.481 | 0.855 | 66.7% | 70.8% |
| Presidio (English defaults) | 0.257 | 0.703 | 74.0% | 91.4% |
This test is easier than real text and the model was tuned with its error categories in mind, so read it as "these formats are covered", not as accuracy on your data. National IDs (Iran melli code, Qatar QID, Saudi ID and Iqama, Emirates ID, Pakistan CNIC, Turkey TC Kimlik, Egypt, Morocco, UK NINO, US SSN) are found every time here, in Western, Arabic-Indic and Persian digits.
Use it
pip install onnxruntime tokenizers numpy huggingface_hub
wget https://huggingface.co/amyrmahdy/decima-pii/resolve/v0.2.1/decima_pii.py
python decima_pii.py "اسم من علی رضایی است و کد ملی من ۰۰۱۲۳۴۵۶۷۸ و شماره موبایلم ۰۹۱۲۳۴۵۶۷۸۹ است."
from decima_pii import DecimaPII
pii = DecimaPII.from_pretrained("amyrmahdy/decima-pii", revision="v0.2.1")
pii.find("Merhaba, ben Ayşe Yılmaz, TC kimlik numaram 10000000146.")
# [{'start': 13, 'end': 24, 'label': 'PERSON_NAME', 'text': 'Ayşe Yılmaz'},
# {'start': 44, 'end': 55, 'label': 'NATIONAL_ID', 'text': '10000000146'}]
pii.redact("مرحبا، أنا أحمد الكعبي، رقم هاتفي +974 5512 3456")
# 'مرحبا، أنا [PERSON_NAME]، رقم هاتفي [PHONE]'
Or pip install decima-ai (2.5.1 or newer) and from decima import PII, or decima pii "..." on the command line. It
loads tag v0.2.1 by default, so a later model update will not change your results until you upgrade the package.
Pin a revision in production. revision="v0.1" gives the previous model, and decima_pii.py then switches to the v0.1
decoding (decoder="v1"), so v0.1's output is reproduced exactly. The v0.2 decoding (decoder="v2") has
two small changes: a name keeps the period of a final initial ("Anna S."), and a phone number or ID written in words
("zero five five …") is no longer dropped by the digit check.
With plain transformers (4.48 or newer, PyTorch weights):
from transformers import pipeline
tagger = pipeline("token-classification", model="amyrmahdy/decima-pii", revision="v0.2.1", aggregation_strategy="simple")
tagger("سلام، من سارا احمدی هستم. کد ملی من ۰۰۱۲۳۴۵۶۷۸ و شماره موبایلم ۰۹۱۲۳۴۵۶۷۸۹ است.")
# PERSON_NAME ' سارا احمدی', NATIONAL_ID '۰۰۱۲۳۴۵۶۷۸', PHONE ' ۰۹۱۲۳۴۵۶۷۸۹' (spans can start with a space; strip them)
For the published numbers, use decima_pii.py (or port its short functions) rather than the plain pipeline. It does two
things the raw model output needs: it keeps a spaced number such as 5185 5272 2255 5565 as one span, and it drops
spans that cannot be what they claim (a 3-digit phone, an email without "@", a span that cuts through a word). Without
the second step v0.1's outside score dropped from 0.814 to 0.730 (strict). Long texts are tagged in overlapping windows.
To check that your own integration reproduces this model, use the test pack: 500 labelled messages with the reference output of each version.
Files:
onnx/model_nbits8.onnx: int8, 148 MB, the default (all numbers above use it).onnx/model_quantized.onnx: int8 for the browser (onnxruntime-web / transformers.js), 143 MB; 0.975 on our test, about 2 points below the default.onnx/model.onnx: fp32, 563 MB.model.safetensors+config.json: the PyTorch weights (ModernBertForTokenClassification), if you want to fine-tune.
Labels
PERSON_NAME, PHONE, EMAIL, NATIONAL_ID, IBAN, CARD, PASSPORT, ADDRESS, DATE_OF_BIRTH, IP_ADDRESS
(BIO tags; see pii_meta.json).
Languages and scripts in training: Arabic (MSA, Gulf, Egyptian, Levantine, Maghrebi, Arabizi), Persian (and Finglish), Urdu (and Roman Urdu), Turkish, English.
Limits
- What it finds is personal identifiers. It does not tag organisations, hospitals or other places of work, dates in general, ages, record or case numbers, or job titles. On its own it is not a full HIPAA or GDPR de-identification tool; use it as one layer and review the output.
- Outside tests cover names, phones, emails, addresses and dates of birth in all four languages (MultiGraSCCo), and national IDs, IBANs, cards and passports on Arabic sets (Sitr, Bayanat) and partly on the Persian and Turkish sets. IP addresses are measured only on our synthetic test. Urdu is measured only on names.
- A very short Gulf message where the only cue is the word جوالي ("my mobile") right before the number, such as
"جوالي 0551234567", was often missed by the model. Decoder v2.1 (default from v0.2.1) catches these; with
decoder="v2"the gap is still there. Numbers in Arabizi ("jawali 0553…") are sometimes tagged as a national ID. - Order numbers, serials and one-time codes written next to a phone word are sometimes tagged as phones.
- In medical text it can tag a drug name or a medical eponym that is also a surname (Babinski) as a name, and a visit date as a date of birth. Dates of birth in Persian and Arabic letters are its weakest cells on MultiGraSCCo (strict 0.78 and 0.80).
- Exact span edges are weaker than detection: on MultiGraSCCo, exact-span typed F1 is 0.797.
- I trained three seeds of v0.2 on the same data and they differ by a few points on some test sets. The released one is the one that passed every check I wrote down before training.
- The browser int8 file is about 2 points below the default on our test.
How it was made
The backbone is jhu-clsp/mmBERT-small (MIT), fine-tuned for token classification on 210,000 synthetic messages. The values are generated in their real formats by code (valid check digits for national IDs, mod-97 IBANs, Luhn cards), and placed in hand-written and model-written carrier sentences, so every label is exact. Test templates, names and seeds are disjoint from training, and every training row was checked for overlap against the outside test sets above.
v0.2 adds about 60,000 messages to v0.1's 150,000, aimed at v0.1's mistakes: drug names, eponyms, brands and ALL-CAPS phrases that are not names; foreign names written in Persian, Arabic and Urdu script; Kuwaiti and Bahraini IDs; TC Kimlik numbers versus Turkish phone numbers; landline formats; long Persian and Turkish addresses; numbers written in words. The training recipe is unchanged. One training run takes about three and a half hours on one GPU.
decima-pii is part of Decima, small models for decisions inside LLM systems (see also decima-guard).
If you have real, consented text where it fails, I would like to see the misses. Open a discussion on this page.
Versions
- v0.2.1 (2026-11-01): same model; decoder v2.1 (phone-word rule) is the default. This page.
- v0.2 (2026-10-29): retrained on 210,000 messages; decoder v2.
revision="v0.2". - v0.1 (2026-10-26): first release.
revision="v0.1".
Licence: Apache-2.0. The backbone is MIT; its notice is kept in NOTICE.
- Downloads last month
- 111
Model tree for amyrmahdy/decima-pii
Base model
jhu-clsp/mmBERT-small
