Token Classification
Transformers
ONNX
Safetensors
English
distilbert
pii
privacy
experimental
datafog
Instructions to use DataFog/pii-en-65m with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use DataFog/pii-en-65m with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("token-classification", model="DataFog/pii-en-65m")# pip install -U transformers accelerate # Load model directly from transformers import AutoTokenizer, AutoModelForTokenClassification tokenizer = AutoTokenizer.from_pretrained("DataFog/pii-en-65m") model = AutoModelForTokenClassification.from_pretrained("DataFog/pii-en-65m", device_map="auto") - Notebooks
- Google Colab
- Kaggle
Download reproduction/scripts/check_chunk_boundaries.py from DataFog/pii-en-65m: direct link, hf CLI and curl.
- Browser
- Download file 4.34 kB
-
https://huggingface.co/DataFog/pii-en-65m/resolve/5683ef559df6618f563375b632fdab7352ac1628/reproduction/scripts/check_chunk_boundaries.py
- Command line
-
hf download hf://DataFog/pii-en-65m@5683ef559df6618f563375b632fdab7352ac1628/reproduction/scripts/check_chunk_boundaries.py
-
curl -L -o check_chunk_boundaries.py https://huggingface.co/DataFog/pii-en-65m/resolve/5683ef559df6618f563375b632fdab7352ac1628/reproduction/scripts/check_chunk_boundaries.py
4.34 kB
| """Oracle BIO logits prove window mechanics independently of trained predictions.""" | |
| import json | |
| import sys | |
| from pathlib import Path | |
| import numpy as np | |
| from transformers import AutoTokenizer | |
| sys.path.insert(0,str(Path(__file__).resolve().parents[1])) | |
| from pii.core import encode_windows, LABELS, byte_positions, decode_window, result | |
| root=Path(__file__).resolve().parents[1] | |
| tokenizer=AutoTokenizer.from_pretrained(root/'artifacts/backbone',local_files_only=True) | |
| max_length,stride=512,42 | |
| cases=[] | |
| def make_case(name,prefix,entities,suffix): | |
| text=prefix; spans=[] | |
| for label,value in entities: | |
| start=len(text);text+=value;spans.append({'label':label,'start':start,'end':len(text)});text+=' ' | |
| text+=suffix | |
| bp=byte_positions(text) | |
| expected=[{'label':s['label'],'start':bp[s['start']],'end':bp[s['end']]} for s in spans] | |
| encoded=encode_windows(tokenizer,text,max_length,stride) | |
| raw=tokenizer(text,add_special_tokens=False,truncation=False,return_offsets_mapping=True,verbose=False) | |
| covered=[] | |
| for wi,ids in enumerate(encoded['input_ids']):covered.extend(ids[1 if wi==0 else stride+1:-1]) | |
| assert covered==raw['input_ids'], 'manual windows lost or duplicated tokens outside overlap' | |
| windows=[]; findings=[]; covers=[0]*len(spans); clips=0 | |
| for wi,offsets in enumerate(encoded['offset_mapping']): | |
| content=[(a,b) for a,b in offsets if a!=b] | |
| complete=[content[0][0]<=s['start'] and content[-1][1]>=s['end'] for s in spans] | |
| covers[:]=[v+int(c) for v,c in zip(covers,complete)] | |
| logits=np.full((len(offsets),15),-8,dtype=np.float32) | |
| for i,(a,b) in enumerate(offsets): | |
| label_id=0; peak=12. | |
| if a!=b: | |
| for si,s in enumerate(spans): | |
| if a<s['end'] and s['start']<b: | |
| assert s['start']<=a and b<=s['end'], 'fixture boundary unrepresentable' | |
| label_id=1+2*LABELS.index(s['label'])+int(a!=s['start']) | |
| peak=5. if complete[si] else 12. | |
| break | |
| logits[i,label_id]=peak | |
| decoded=decode_window(logits,offsets,text,max_length,wi>0,wi+1<len(encoded['input_ids'])) | |
| clips+=sum(f.get('_boundary_fragment',False) for f in decoded) | |
| findings.extend(decoded) | |
| windows.append({'input_ids':encoded['input_ids'][wi],'offsets':[[bp[a],bp[b]] for a,b in offsets], 'logits':logits.tolist()}) | |
| actual=result(text,findings) | |
| assert [{k:f[k] for k in ('label','start','end')} for f in actual['findings']]==expected,(name,actual,expected) | |
| assert all(covers), 'no complete window for entity' | |
| # Demonstrate the old greedy rule really loses exact spans on boundary cases. | |
| old=result(text,[{k:v for k,v in f.items() if k!='_boundary_fragment'} for f in findings]) | |
| old_wrong=[{k:f[k] for k in ('label','start','end')} for f in old['findings']]!=expected | |
| if name!='adjacent-repeated-unicode':assert clips and old_wrong | |
| cases.append({'name':name,'text':text,'expected':expected,'redacted':actual['redacted'],'windows':windows,'complete_window_counts':covers,'edge_predictions':clips,'old_greedy_would_fail':old_wrong,'full_token_coverage':True,'raw_tokens':len(raw['input_ids'])}) | |
| make_case('boundary-email','🙂 '+'hello '*505,[('email','jose.garcia@example.com')],'end '*30) | |
| make_case('max43-token-boundary-entity','🙂 '+'hello '*490,[('street_address',' '.join(['alpha']*43))],'end '*30) | |
| make_case('adjacent-repeated-unicode','🙂 ',[('first_name','José'),('last_name','García'),('first_name','José'),('last_name','García')],'done') | |
| fixture={'max_length':max_length,'stride':stride,'labels':LABELS,'cases':cases} | |
| (root/'rust/tests/boundary-oracle.json').write_text(json.dumps(fixture,separators=(',',':'))+'\n') | |
| report={'max_length':max_length,'stride':stride,'policy':'Artificial-edge fragments suppressed only when a strictly containing same-label prediction exists. Remaining candidates use original greedy score ordering.', 'proof':'Oracle true BIO labels; partial-window probabilities intentionally exceed complete-window probabilities.', 'cases':[{k:v for k,v in c.items() if k not in ('text','windows','redacted')} for c in cases]} | |
| (root/'artifacts/boundary-proof.json').write_text(json.dumps(report,indent=2)+'\n') | |
| print(json.dumps(report,indent=2)) | |