Downloads · 30 days
17
18% of all-time downloads
keshavsharma/indian-pii-ner-distilbert-crf
indian-pii-ner-distilbert-crf is a token classification model from keshavsharma. Use it when you need labels on individual words, such as names. The card lists the license as mit.
A token-classification model that detects names and addresses in free text, for PII redaction/masking use cases. Built on distilbert-base-multilingual-cased with a CRF layer on top for more consistent entity boundaries.
Downloads · 30 days
17
18% of all-time downloads
All-time downloads
94
Public
Parameters
135M
1.1 GB on disk
Likes
1
Public
Click a slice to open those files.
.bin539 MB · 50%
From the Hugging Face model README
A token-classification model that detects names and addresses in free text, for PII redaction/masking use cases. Built on distilbert-base-multilingual-cased with a CRF layer on top for more consistent entity boundaries.
| Label | Meaning |
|---|---|
O | Not PII |
B-NAME / I-NAME | Start / continuation of a person's name |
B-ADDRESS / I-ADDRESS | Start / continuation of an address |
Trained on a combination of:
with a 30% lowercase-augmented duplicate of each to make the model robust to casing.
Entity-level precision / recall / F1 on a held-out 10% split (see eval_report.txt in this repo for the full numbers):
| Entity | Precision | Recall | F1 |
|---|---|---|---|
| NAME | fill in from eval_report.txt | ||
| ADDRESS | fill in from eval_report.txt | ||
| micro avg |
This model uses a custom architecture (DistilBERT encoder + CRF decoding layer), so it can't be loaded with a plain AutoModelForTokenClassification. Use the snippet below instead.
pip install torch transformers pytorch-crf huggingface_hub
import torch
import torch.nn as nn
from huggingface_hub import snapshot_download
from torchcrf import CRF
from transformers import AutoConfig, AutoModel, AutoTokenizer
import json
REPO_ID = "your-username/pii-ner-distilbert-crf" # <-- change this
local_dir = snapshot_download(REPO_ID)
class DistilBertCRF(nn.Module):
def __init__(self, config, num_labels):
super().__init__()
self.bert = AutoModel.from_config(config)
self.dropout = nn.Dropout(0.1)
self.classifier = nn.Linear(self.bert.config.hidden_size, num_labels)
self.crf = CRF(num_labels, batch_first=True)
def forward(self, input_ids, attention_mask):
outputs = self.bert(input_ids=input_ids, attention_mask=attention_mask)
emissions = self.classifier(self.dropout(outputs.last_hidden_state))
return self.crf.decode(emissions, mask=attention_mask.bool())
with open(f"{local_dir}/label_map.json") as f:
id2label = {int(k): v for k, v in json.load(f)["id2label"].items()}
config = AutoConfig.from_pretrained(local_dir)
tokenizer = AutoTokenizer.from_pretrained(local_dir)
model = DistilBertCRF(config, num_labels=len(id2label))
model.load_state_dict(torch.load(f"{local_dir}/pytorch_model.bin", map_location="cpu"))
model.eval()
def predict(text):
enc = tokenizer(text, return_tensors="pt", return_offsets_mapping=True, truncation=True, max_length=512)
offsets = enc.pop("offset_mapping")[0].tolist()
with torch.no_grad():
pred_ids = model(input_ids=enc["input_ids"], attention_mask=enc["attention_mask"])[0]
return [(text[s:e], id2label[label_id]) for (s, e), label_id in zip(offsets, pred_ids) if s != e]
text = "Hi, my name is Rajesh Kumar and I live at 42 MG Road, Bangalore."
for token, label in predict(text):
if label != "O":
print(f"{token!r:25s} -> {label}")
NAME and ADDRESS — no coverage of emails, phone numbers, IDs, etc.MIT