cbx-indic-pii-extractor-58M

Finds personal information (PII) in Indian text and returns the exact position of each value with one of 27 labels, including Aadhaar, PAN, UPI IDs, IFSC codes, voter IDs and vehicle numbers. It reads 12 Indian languages and Indian English (Hindi, Bengali, Marathi, Tamil, Telugu, Kannada, Malayalam, Gujarati, Punjabi, Odia, Assamese, Urdu), in native script, romanised (Indian languages in English letters) and code-mixed text. 57.6M parameters; about 37 ms for a short message and 250 ms for a 2,000-character document on a CPU (8 threads), about 13 ms on a GPU.

This is the most accurate size and the recommended default. Also available in 36M and 17M sizes. A larger internal model, the teacher of the 36M and 17M sizes, scores higher (see Results); it is not released.

How it was made

  • Fine-tuned from mmBERT-small (a multilingual ModernBERT encoder) on our synthetic Indian PII data.
  • Vocabulary cut to 40,000 tokens (from 256,000): every token of the Indian scripts and Urdu, every byte and single character, every token the training data uses, then the most frequent rest. Any text can still be read; rarer words are split into smaller pieces. Use the tokenizer in this repo, not mmBERT's. Accuracy is unchanged by the cut; the model is 2.4x smaller than the full fine-tuned model.

Labels

Label Covers
PERSON Names, including first names or surnames on their own and initials; titles (Shri, Dr, ji) are not part of the name
PHONE Phone numbers
EMAIL Email addresses
ADDRESS Postal addresses including the PIN code, and a PIN code on its own (a city or state on its own is not labelled)
DATE_OF_BIRTH Dates of birth
DATE Every other date, in any format and script, with a time written next to it
AMOUNT Money amounts with their currency sign or word (โ‚น25,000, Rs. 500/-, เฅจเฅซ เคนเคœเคผเคพเคฐ เคฐเฅเคชเคฏเฅ‡)
BANK_ACCOUNT Bank account numbers
IFSC IFSC branch codes
UPI_ID UPI IDs
PAYMENT_CARD Card numbers, including masked ones (XXXX XXXX XXXX 1234)
AADHAAR Aadhaar numbers, including masked ones
PAN PAN numbers
VOTER_ID Voter ID (EPIC) numbers
RATION_CARD Ration card numbers
PASSPORT Passport numbers
DRIVERS_LICENSE Driving licence numbers
VEHICLE_REG Vehicle registration numbers
NATIONAL_ID Other national IDs: ABHA number and address, APAAR ID
TAX_ID GSTIN and TAN (PAN has its own label)
CUSTOMER_ID Customer, CIF, loan, policy and consumer numbers (not order, transaction or ticket numbers)
EMPLOYEE_ID Employee codes, PF UAN, ESIC numbers
PATIENT_ID MRN, UHID and hospital registration numbers
SECRET OTPs, MPINs, passwords, CVVs
USERNAME Usernames and handles
URL Web addresses
IP_ADDRESS IPv4 and IPv6 addresses

Codes that are not personal data (order, invoice, ticket, transaction, SKU, batch, tracking and booking numbers, public helplines) are not labelled.

How to use

1. Download and install

pip install -U huggingface_hub
hf download CobuildX-AI/cbx-indic-pii-extractor-58M --local-dir cbx-indic-pii-extractor-58M
cd cbx-indic-pii-extractor-58M
pip install -r requirements.txt

2. Use it in your code

PIIExtractor (in pii.py, part of this repo) loads the model once:

from pii import PIIExtractor

pii = PIIExtractor()                 # uses a GPU if one is available
pii.extract("เคจเคฎเคธเฅเคคเฅ‡, เคฎเฅ‡เคฐเคพ เคจเคพเคฎ เค…เคจเฅ€เคคเคพ เค•เฅเคฒเค•เคฐเฅเคฃเฅ€ เคนเฅˆเฅค เคฎเฅ‡เคฐเคพ เคฎเฅ‹เคฌเคพเค‡เคฒ เคจเค‚เคฌเคฐ 98204 51763 เคนเฅˆ เค”เคฐ เคˆเคฎเฅ‡เคฒ anita.k@example.in เคนเฅˆเฅค")
# [{'label': 'PERSON', 'text': 'เค…เคจเฅ€เคคเคพ เค•เฅเคฒเค•เคฐเฅเคฃเฅ€', 'start': 17, 'end': 31},
#  {'label': 'PHONE', 'text': '98204 51763', 'start': 53, 'end': 64},
#  {'label': 'EMAIL', 'text': 'anita.k@example.in', 'start': 76, 'end': 94}]

pii.redact("เคจเคฎเคธเฅเคคเฅ‡, เคฎเฅ‡เคฐเคพ เคจเคพเคฎ เค…เคจเฅ€เคคเคพ เค•เฅเคฒเค•เคฐเฅเคฃเฅ€ เคนเฅˆเฅค เคฎเฅ‡เคฐเคพ เคฎเฅ‹เคฌเคพเค‡เคฒ เคจเค‚เคฌเคฐ 98204 51763 เคนเฅˆ เค”เคฐ เคˆเคฎเฅ‡เคฒ anita.k@example.in เคนเฅˆเฅค")
# "เคจเคฎเคธเฅเคคเฅ‡, เคฎเฅ‡เคฐเคพ เคจเคพเคฎ [PERSON] เคนเฅˆเฅค เคฎเฅ‡เคฐเคพ เคฎเฅ‹เคฌเคพเค‡เคฒ เคจเค‚เคฌเคฐ [PHONE] เคนเฅˆ เค”เคฐ เคˆเคฎเฅ‡เคฒ [EMAIL] เคนเฅˆเฅค"

pii.extract(text, labels=["PERSON", "AADHAAR"])   # keep only some labels
  • The model finds the PII. redact() replaces each value it found with its label, or with your own template.
  • Context length is 8,192 tokens, read in a single pass. Longer texts are split automatically.
  • Masked values keep their mask: XXXX XXXX 4521 is returned whole as AADHAAR, **9813 as BANK_ACCOUNT.

3. Optional: check your setup

python example.py runs four sample texts (Hindi, romanised Hindi, Tamil, Bengali) and prints what it finds.

4. Optional: ONNX Runtime

onnx/model.onnx runs the same model with ONNX Runtime (1.23 or later) and gives the same tags (checked on 300 texts). python onnx_example.py shows how to use it with PIIExtractor. It uses ONNX Runtime's fused attention operator (com.microsoft.MultiHeadAttention), so other ONNX runtimes may not load it.

Results

IndicPIIDetX v0.1 (CobuildX, DOI 10.57967/hf/11457): 10,035 synthetic documents in the same 13 languages and three writing styles, five document types (chat and SMS, emails and letters, forms with scan noise, call transcripts, chatbot prompts), 1,489 of them with no PII but with look-alike codes. Made independently of our training data (different generator, different name lists). Scores on its test split (8,028 documents). Typed F1: a predicted value counts if it overlaps an annotated value of the same type. Scored on the 14 types every model in the table can name (see How we evaluated). The benchmark's own scorer pairs each prediction with at most one annotated value, so its card shows slightly lower numbers.

Model Typed F1 Precision Recall Documents without PII flagged CPU, median per text
cbx-indic-pii-extractor-58M 0.930 0.945 0.915 19.9% 16 ms
cbx-indic-pii-extractor-36M 0.888 0.943 0.839 13.4% 8 ms
cbx-indic-pii-extractor-17M 0.869 0.943 0.806 16.2% 5 ms
Larger internal model (teacher, not released) 0.957 0.959 0.955 15.0% โ€“
Chitragupta (with its rules) 0.944 0.912 0.978 53.4% 73 ms
Chitragupta (model only) 0.941 0.904 0.981 53.3% 75 ms
IndicBERT-PII 0.813 0.808 0.818 61.0% 21 ms
GLiNER-PII large 0.490 0.727 0.370 45.1% 244 ms
  • Precision and false alarms are where this model is strongest: it flags 19.9% of the documents without PII, against 53.4% for Chitragupta.
  • Chitragupta finds more values (recall), mostly names and bank / ID numbers. Our larger internal model (the teacher of the 36M and 17M) scores 0.957.
  • All 27 labels (our models only): typed F1 0.896. With CUSTOMER_ID and NATIONAL_ID added to the 14 types: 0.904 (Chitragupta, which has no label for them, 0.871).
  • CPU time is the median over 200 texts of the Ylemis benchmark below (median 96 characters), one at a time, 8 threads, all models on the same machine. On longer texts the gap grows.

Two public benchmarks. These helped us decide what to add to our training data (ID-shaped codes that are not PII, new ID formats), so they are not blind tests for these models; the comparison is shown for reference.

Model Ylemis India-PII: typed F1 IndiaPII-Bench: typed F1 IndiaPII-Bench: look-alike codes flagged
cbx-indic-pii-extractor-58M 0.986 0.946 13.3%
cbx-indic-pii-extractor-36M 0.981 0.958 19.8%
cbx-indic-pii-extractor-17M 0.982 0.944 12.3%
Chitragupta (with its rules) 0.941 0.900 47.0%
Chitragupta (model only) 0.931 0.890 47.7%
IndicBERT-PII 0.727 0.632 64.0%
GLiNER-PII large 0.867 0.934 56.2%
  • Ylemis India-PII (Pranshurs/ylemis-india-pii-benchmark): 10,000 texts; location, organisation and medical-ID annotations are not scored (we do not label them).
  • IndiaPII-Bench (maskflow-ai/indiapii-bench): 2,000 texts with annotated look-alike codes; "flagged" is the share of those codes a model marks as PII.

On our own held-out synthetic test set (same generators as the training data, so optimistic) the span F1 is 0.995.

Speed (one text at a time, end to end; NVIDIA TITAN RTX fp16, Intel i7-10700 CPU with 8 threads):

Short message (~340 characters) Document (2,000 characters)
GPU 12.6 ms 13.3 ms
CPU 37 ms 250 ms

How we evaluated

  • Same runs for every model. We ran every model ourselves, on the same texts, with the same scorer.
  • Types. Each model's labels were mapped to the 14 types every model in the table can name: PERSON, ADDRESS, PHONE, EMAIL, AADHAAR, PAN, TAX_ID (GSTIN), IFSC, UPI_ID, VOTER_ID, VEHICLE_REG, PASSPORT, DRIVERS_LICENSE, BANK_ACCOUNT. A predicted PIN code counts as an address. Annotated values of other types are left out, and predictions on them are not counted.
  • Settings. IndicBERT-PII at its card's threshold 0.5, GLiNER-PII at 0.3 with one fixed prompt list; texts longer than a model's limit (IndicBERT-PII 512 tokens, GLiNER 768 words) were read in consecutive windows. Adjacent pieces of the same type are joined for every model alike.
  • No overlap with training data. No IndicPIIDetX document appears in our training data, even with numbers and codes ignored; its names come from different lists (a few common names occur in both).
  • Teacher. The larger internal model is a fine-tuned mmBERT-base (307M parameters); it is not released.

Limitations

  • Names written in native script, especially less common names, are missed more often than by the larger internal model.
  • CUSTOMER_ID, NATIONAL_ID, PATIENT_ID, EMPLOYEE_ID and RATION_CARD values are found less reliably than names, phones and emails, and bank account numbers are sometimes labelled as other account types.
  • Phone numbers written in a 3-3-4 pattern (784-448-0140) can be read as Aadhaar numbers.
  • Values next to each other in JSON or CSV with no space between them can be returned as one span.
  • AMOUNT and DATE cover every amount and date, also prices and dates not tied to a person; benchmarks that label only personal ones count those as false alarms.
  • Trained on synthetic text only. Sensitive attributes such as health, religion or caste are not detected.
  • A detection aid, not a guarantee: keep human review where redaction must be complete.

Training data

About 74,000 synthetic documents in the 13 languages and three writing styles, made by us: templates written by large language models (gpt-5.4-mini, and Sarvam-30B for an earlier part), with every personal value filled in by code (valid checksums for Aadhaar, PAN, GSTIN and similar), plus documents full of codes that are not PII, and forms, tables, key-value records, CSV and JSON. No real personal data.

Licence

Apache-2.0. Base model mmBERT-small (MIT).

Downloads last month
42
Safetensors
Model size
57.6M params
Tensor type
F32
ยท
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support

Model tree for CobuildX-AI/cbx-indic-pii-extractor-58M

Quantized
(306)
this model

Collection including CobuildX-AI/cbx-indic-pii-extractor-58M