Token Classification
Transformers
Safetensors
lfm2
liquid
lfm2.5
bidirectional
masked-lm
encoder
pii
ner
privacy
multilingual
custom_code
Instructions to use LiquidAI/LFM2.5-Encoder-350M-PII-Detector with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use LiquidAI/LFM2.5-Encoder-350M-PII-Detector with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("token-classification", model="LiquidAI/LFM2.5-Encoder-350M-PII-Detector", trust_remote_code=True)# Load model directly from transformers import AutoTokenizer, AutoModelForTokenClassification tokenizer = AutoTokenizer.from_pretrained("LiquidAI/LFM2.5-Encoder-350M-PII-Detector", trust_remote_code=True) model = AutoModelForTokenClassification.from_pretrained("LiquidAI/LFM2.5-Encoder-350M-PII-Detector", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
Upload pii_hybrid_decode.py with huggingface_hub
Browse files- pii_hybrid_decode.py +19 -4
pii_hybrid_decode.py
CHANGED
|
@@ -70,6 +70,19 @@ _SNAP = {
|
|
| 70 |
}
|
| 71 |
_AUTH_TYPES = {t for t, _, _ in _AUTH}
|
| 72 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
def hybrid_spans(text: str, model_spans: list[dict]) -> list[dict]:
|
| 74 |
"""model_spans: [{'start','end','type'}...] from the token classifier. Returns the
|
| 75 |
hybrid-decoded spans (dicts with start/end/type/text)."""
|
|
@@ -81,6 +94,7 @@ def hybrid_spans(text: str, model_spans: list[dict]) -> list[dict]:
|
|
| 81 |
s, e = mm.start(), mm.end()
|
| 82 |
if any(claimed[s:e]): continue
|
| 83 |
if val and not val(mm.group(0)): continue
|
|
|
|
| 84 |
for i in range(s, e): claimed[i] = True
|
| 85 |
auth.append({"start": s, "end": e, "type": t, "text": mm.group(0)})
|
| 86 |
# 2. model spans for non-AUTH types; SNAP types expand to overlapping regex match
|
|
@@ -97,14 +111,15 @@ def hybrid_spans(text: str, model_spans: list[dict]) -> list[dict]:
|
|
| 97 |
if snap:
|
| 98 |
out.append({"start": snap.start(), "end": snap.end(), "type": t,
|
| 99 |
"text": text[snap.start():snap.end()]}); continue
|
| 100 |
-
|
| 101 |
-
|
| 102 |
out.extend(auth)
|
| 103 |
seen, uniq = set(), []
|
| 104 |
for sp in sorted(out, key=lambda s: (s["start"], s["end"])):
|
| 105 |
k = (sp["start"], sp["end"], sp["type"])
|
| 106 |
-
if k
|
| 107 |
-
|
|
|
|
| 108 |
return uniq
|
| 109 |
|
| 110 |
def model_spans(text: str, tok, model):
|
|
|
|
| 70 |
}
|
| 71 |
_AUTH_TYPES = {t for t, _, _ in _AUTH}
|
| 72 |
|
| 73 |
+
# boundary-snap: the token classifier drops leading/trailing sub-tokens of Latin-script
|
| 74 |
+
# words ('Ibuprofen'->'profen'). Extend a span across contiguous Latin word-chars to
|
| 75 |
+
# complete the partial word(s). Latin-only -> CJK/Arabic left untouched (no over-extend).
|
| 76 |
+
_LAT = re.compile(r"[0-9A-Za-z脌-脰脴-枚酶-每]")
|
| 77 |
+
def _snap_word(text, s, e):
|
| 78 |
+
n = len(text)
|
| 79 |
+
while s > 0 and _LAT.match(text[s - 1]) and _LAT.match(text[s]): s -= 1
|
| 80 |
+
while e < n and _LAT.match(text[e]) and _LAT.match(text[e - 1]): e += 1
|
| 81 |
+
return s, e
|
| 82 |
+
_SWIFT_CUE = re.compile(r"(?i)(swift|bic)")
|
| 83 |
+
def _swift_ok(text, s, val): # kill ALL-CAPS-word false BICs (PARTICULARS, CONFIDENTIAL)
|
| 84 |
+
return bool(re.search(r"\d", val)) or bool(_SWIFT_CUE.search(text[max(0, s - 12):s]))
|
| 85 |
+
|
| 86 |
def hybrid_spans(text: str, model_spans: list[dict]) -> list[dict]:
|
| 87 |
"""model_spans: [{'start','end','type'}...] from the token classifier. Returns the
|
| 88 |
hybrid-decoded spans (dicts with start/end/type/text)."""
|
|
|
|
| 94 |
s, e = mm.start(), mm.end()
|
| 95 |
if any(claimed[s:e]): continue
|
| 96 |
if val and not val(mm.group(0)): continue
|
| 97 |
+
if t == "financial.swift_bic" and not _swift_ok(text, s, mm.group(0)): continue
|
| 98 |
for i in range(s, e): claimed[i] = True
|
| 99 |
auth.append({"start": s, "end": e, "type": t, "text": mm.group(0)})
|
| 100 |
# 2. model spans for non-AUTH types; SNAP types expand to overlapping regex match
|
|
|
|
| 111 |
if snap:
|
| 112 |
out.append({"start": snap.start(), "end": snap.end(), "type": t,
|
| 113 |
"text": text[snap.start():snap.end()]}); continue
|
| 114 |
+
ss, ee = _snap_word(text, m["start"], m["end"]) # complete partial Latin words
|
| 115 |
+
out.append({"start": ss, "end": ee, "type": t, "text": text[ss:ee]})
|
| 116 |
out.extend(auth)
|
| 117 |
seen, uniq = set(), []
|
| 118 |
for sp in sorted(out, key=lambda s: (s["start"], s["end"])):
|
| 119 |
k = (sp["start"], sp["end"], sp["type"])
|
| 120 |
+
if k in seen: continue
|
| 121 |
+
if len(text[sp["start"]:sp["end"]].strip()) < 3: continue # drop fragments
|
| 122 |
+
seen.add(k); uniq.append(sp)
|
| 123 |
return uniq
|
| 124 |
|
| 125 |
def model_spans(text: str, tok, model):
|