Views
No views yet
{"entities": [{"type": "PERSON", "value": "Ayşe Yılmaz"}, {"type": "EMAIL", "value": "ayse@firma.com"}]}PERSON, EMAIL, PHONE, NATIONAL_ID, IBAN, CREDIT_CARD, ADDRESS, DATE_OF_BIRTH, IP_ADDRESS.1from transformers import AutoModelForCausalLM, AutoTokenizer
2
3model_id = "promptrails/piimask-qwen2.5-0.5b"
4tok = AutoTokenizer.from_pretrained(model_id)
5model = AutoModelForCausalLM.from_pretrained(model_id)
6
7SYSTEM = (
8 "You are a PII detection engine. Extract every piece of personally identifiable "
9 "information from the user's text.\n\n"
10 'Respond with ONLY a JSON object of the form:\n'
11 '{"entities": [{"type": "<TYPE>", "value": "<exact substring from the text>"}]}\n\n'
12 "Valid types: PERSON, EMAIL, PHONE, NATIONAL_ID, IBAN, CREDIT_CARD, ADDRESS, "
13 "DATE_OF_BIRTH, IP_ADDRESS.\n"
14 "Each value must be copied verbatim from the text. If the text contains no PII, "
15 'respond with {"entities": []}.'
16)
17
18text = "Ben Ayşe Yılmaz, kartım 4111 1111 1111 1111, mailim ayse@firma.com"
19prompt = tok.apply_chat_template(
20 [{"role": "system", "content": SYSTEM}, {"role": "user", "content": text}],
21 add_generation_prompt=True, tokenize=False,
22)
23ids = tok(prompt, return_tensors="pt").to(model.device)
24out = model.generate(**ids, max_new_tokens=256, do_sample=False)
25print(tok.decode(out[0][ids["input_ids"].shape[1]:], skip_special_tokens=True))Barbaros Hayrettin Paşa Sokak No:158 Daire:37 Konak/Adana). This small, order-invariant
regex grammar catches them by structure and recovers recall to ~1.0 — add its matches to
the model output. It fires only on Turkish structure, so English is untouched:1import re
2
3_TR_IL = ("Adana Adıyaman Afyonkarahisar Afyon Ağrı Amasya Ankara Antalya Artvin Aydın "
4 "Balıkesir Bilecik Bingöl Bitlis Bolu Burdur Bursa Çanakkale Çankırı Çorum Denizli "
5 "Diyarbakır Edirne Elazığ Erzincan Erzurum Eskişehir Gaziantep Antep Giresun Gümüşhane "
6 "Hakkari Hatay Isparta Mersin İçel İstanbul İzmir Kars Kastamonu Kayseri Kırklareli "
7 "Kırşehir Kocaeli Konya Kütahya Malatya Manisa Kahramanmaraş Maraş Mardin Muğla Muş "
8 "Nevşehir Niğde Ordu Rize Sakarya Samsun Siirt Sinop Sivas Tekirdağ Tokat Trabzon "
9 "Tunceli Şanlıurfa Urfa Uşak Van Yozgat Zonguldak Aksaray Bayburt Karaman Kırıkkale "
10 "Batman Şırnak Bartın Ardahan Iğdır Yalova Karabük Kilis Osmaniye Düzce").split()
11_L = "A-Za-zçğıöşüÇĞİÖŞÜ"
12_IL = re.compile(rf"(?<![{_L}])(?:" + "|".join(sorted(map(re.escape, _TR_IL), key=len, reverse=True)) + rf")(?![{_L}])")
13_ANCHOR = re.compile(r"\b(mahallesi|mahalle|mah|mh|caddesi|cadde|cad|cd|sokağı|sokak|sok|sk|bulvarı|bulvar|blv|meydanı|meydan)\b\.?", re.I)
14_NUM = re.compile(r"\b(no|numara|kat|daire|blok|apt|d)\b\.?\s*[:.]?\s*\d|\bno[:.]?\s*\d|\d+/\d+", re.I)
15
16def _start(text, a):
17 i = a
18 while i > 0 and text[i - 1] == " ": i -= 1
19 s = i
20 while i > 0:
21 j = i
22 while j > 0 and (text[j - 1].isalnum() or text[j - 1] in "çğıöşüÇĞİÖŞÜ"): j -= 1
23 w = text[j:i]
24 if w and (w[0].isupper() or w[0].isdigit()):
25 s = j; i = j
26 while i > 0 and text[i - 1] == " ": i -= 1
27 if i > 0 and text[i - 1] in ".,:;": break
28 else:
29 break
30 return s
31
32def turkish_addresses(text):
33 spans = []
34 for m in _IL.finditer(text):
35 w0 = max(0, m.start() - 140)
36 anchors = list(_ANCHOR.finditer(text[w0:m.start()]))
37 if not anchors: continue
38 st = _start(text[w0:m.start()], anchors[0].start()) + w0
39 if not _NUM.search(text[st:m.end()]) and "mah" not in anchors[0].group().lower(): continue
40 spans.append([st, m.end()])
41 spans.sort(key=lambda s: (s[0], -(s[1] - s[0])))
42 out = []
43 for s in spans:
44 if out and s[0] < out[-1][1]:
45 out[-1][1] = max(out[-1][1], s[1])
46 else:
47 out.append(s)
48 return [text[a:b] for a, b in out]
49
50# merge: add addresses the model missed
51# seen = {e["value"] for e in entities}
52# entities += [{"type": "ADDRESS", "value": v} for v in turkish_addresses(text) if v not in seen]Isotonic/deberta-v3-base_finetuned_ai4privacy_v2).| System | micro-F1 |
|---|---|
| piimask (0.5B) | 0.81 |
| ai4privacy DeBERTa | 0.65 |
| Microsoft Presidio | 0.51 |
| Eval set | piimask | Presidio | DeBERTa |
|---|---|---|---|
| Realistic Turkish PII (held-out test) | 0.89 | 0.42 | 0.47 |
| piimb aggregate (en) | 0.65 | 0.55 | — |
| gretel finance docs (en, out-of-distribution) | 0.49 | 0.37 | 0.37 |