ganmoor-ai-labs/piiguard-qwen3-1.7b
PIIGuard — Indian-language PII extraction (Qwen3-1.7B)
PIIGuard extracts personally identifiable information from English, Hindi (Devanagari), and Hinglish (code-mixed) enterprise text and returns structured JSON — built for on-prem redaction pipelines where data cannot leave the building (support transcripts, logs, KYC notes, LLM-gateway scrubbing, DPDP compliance).
It handles the India-specific surface forms Western PII tools miss: Aadhaar numbers (including Devanagari digits ९८७६…), PAN codes, UPI/VPA handles, IFSC codes, Indian address formats — while deliberately ignoring lookalike traps (12-digit UTR references, order/ticket/invoice numbers, OTPs, GSTINs, employee IDs, company names).
The Q4KM GGUF is 1.2GB and runs on CPU-only hardware down to a Raspberry Pi via llama.cpp/Ollama.
Entity types
PERSON PHONE EMAIL AADHAAR PAN ADDRESS DOB BANK_ACCOUNT IFSC CARD UPI_ID
Explicitly not flagged (by design): GSTIN (public business identifier), UTR/transaction refs, OTPs, order/ticket/invoice/tracking numbers, employee IDs, company names, standalone city names, non-birth dates.
Results
Held-out test set: 1,320 examples from unseen templates and unseen PII values (train/test value pools are hash-disjoint), 200 of them PII-free.
Per language F1: en 98.4 / hi 98.8 / hi-en 99.2. Full per-entity × per-language tables in `eval_report.md`.
Q4KM quantization spot-check (100-example slice): recall 98.4%, precision 95.9%.
Input/output contract
- system:
Extract all personally identifiable information (PII) from the user's text. Respond with only a JSON object: {"entities": [{"type": ..., "text": ...}]}. Valid types: PERSON, PHONE, EMAIL, AADHAAR, PAN, ADDRESS, DOB, BANK_ACCOUNT, IFSC, CARD, UPI_ID. If there is no PII, return {"entities": []}. - user: raw text
- assistant:
{"entities": [{"type": "PHONE", "text": "९८२२०४५६७१"}, ...]}—textis the verbatim substring; locate spans by string search downstream. Use temperature 0.
Quickstart (Ollama — easiest)
ollama run santosh07401/piiguard "mera number ९८२२०४५६७१ hai aur naam Ravi Gupta"
# {"entities":[{"type":"PHONE","text":"९८२२०४५६७१"},{"type":"PERSON","text":"Ravi Gupta"}]}The system prompt is baked in — paste text, get JSON. Model page: ollama.com/santosh07401/piiguard
Quickstart (llama.cpp)
llama-server -m piiguard-q4_k_m.gguf --port 8090 -c 4096
curl -s localhost:8090/v1/chat/completions -d '{
"messages": [
{"role": "system", "content": "<system prompt above>"},
{"role": "user", "content": "sir mera naam Anjali Deshmukh hai, mera number ९८२२०४५६७१ hai"}
],
"temperature": 0, "max_tokens": 512
}'Files
Training
LoRA (r=16, α=32) on Qwen/Qwen3-1.7B with TRL SFTTrainer, assistant-only loss, thinking disabled; 3 epochs + 1 targeted fix epoch on a single NVIDIA GB10 (DGX Spark). Data: ~26k fully synthetic examples — carrier templates written by gemma-3-27b-it (local), filled with programmatically generated, checksum-valid fake PII (Verhoeff-valid Aadhaar, Luhn-valid cards, region-correct PIN codes; Roman + Devanagari name pairs). Labels are exact by construction; no real personal data was used at any point.
Limitations
- Trained on synthetic text in 3 language tracks; other Indic languages (Bengali, Tamil, Telugu…) are not yet covered.
- Entity
textis returned verbatim but span location is left to the caller. - 16-digit bank accounts vs. cards are disambiguated by context; genuinely context-free digit runs can still be typed wrong.
- Not a compliance guarantee: use as a component in a reviewed pipeline, with human escalation where stakes require it.
- PASSPORT / VOTERID / DRIVINGLICENSE are not yet entity types (roadmap).
