smoh commited on
Commit
07ab3c2
·
verified ·
1 Parent(s): 7cadd41

Expand model card with measured performance, usage and native qualification

Browse files
README.md CHANGED
@@ -13,40 +13,264 @@ tags:
13
  - experimental
14
  - datafog
15
  ---
16
- # DataFog PII v0.1.0
17
 
18
- Experimental English PII token classifier trained by DataFog. This is the September 25, 2026 DistilBERT model used by datafog-mcp PR #32. GLiNER is a separate CI comparison and is neither the backbone nor an artifact in this release.
19
 
20
- ## Checkpoint and training
21
 
22
- Release checkpoint: **epoch 2**, selected by lowest validation token cross entropy under the predeclared two-epoch recipe. Epoch 1: 0.003570595682300194; epoch 2: 0.0018816228932983772. The safetensors bytes equal `training/epoch-2/model/model.safetensors`; the frozen ONNX, tokenizer, calibration and native macOS bundle are copied unchanged. No final-test tuning or retraining occurred for release.
23
 
24
- Backbone: `distilbert/distilbert-base-cased` at `6ea81172465e8b0ad3fddeed32b986cdcdcffcf0`. NVIDIA Nemotron-PII at `b70ffaf5ff39e079776134c5bf4381f00a9fd1ed`. Seed 42, float32 MPS, batch 16, learning rate 2e-5, weight decay 0.01, two epochs (8,102 seconds). Prepared partitions: train 89,307; validation 514; calibration-fit 501; calibration-select 532; test 90,018 documents. Grouping by UID, exact text and normalized annotated templates prevents evidenced family overlap; semantic annotation noise remains. See `provenance/docs/data.md` and `provenance/artifacts/data-manifest.json` for filtering, immutable source hashes and split provenance.
25
 
26
- ## Labels and inference contract
 
 
 
 
 
 
 
 
 
 
 
27
 
28
- Seven labels in this exact order: first_name, last_name, email, phone_number, street_address, customer_id, account_number. Class 0 is O; each label has B then I, for 15 classes. Both mappings are preserved in root `config.json` and `label-mappings.json`. Root configuration is the Transformers checkpoint configuration; `bundle/config.json` is the frozen native inference configuration. Do not interchange them.
29
 
30
- The released JSONL runtime tokenizes the entire input, constructs 512-token windows (510 content tokens), and overlaps 42 content tokens. Minimum predicted-class token probability forms the raw span score. Boundary-fragment suppression precedes greedy overlap resolution. Half-open offsets count UTF-8 **bytes**, not Python characters. Pooled isotonic calibration uses clipped linear interpolation; the experimental threshold is 0.6486486486486487 (the exact value in `bundle/calibration.json` is authoritative). Calibration fit and threshold selection use separate partitions. Preserve tokenizer, config and calibration together with weights. The runtime returns `{findings, contains_pii, redacted}`; invalid requests and unsupported full-window entities return explicit errors.
31
 
32
- Transformers can load the root safetensors using `AutoModelForTokenClassification.from_pretrained(path, local_files_only=True)`. A generic token-classification pipeline does **not** reproduce native windowing, calibrated confidence, thresholding, offsets or redaction. Use the bundle for MCP qualification; `reproduction/pii/core.py` preserves the Python reference.
 
 
 
 
 
 
 
 
 
 
 
33
 
34
- ## Evaluation
35
 
36
- Frozen held-out synthetic evaluation: exact typed-span precision 0.9901, recall 0.9763, F1 0.9832; TP 242,853, FP 2,424, FN 5,886. UTF-8 byte redaction F1 0.9876. Hand-authored support fixtures recovered 14/18 entities (F1 0.8485). These are agreement with synthetic annotations, not verified accuracy on private customer tickets. Full metrics, calibration reliability, limitations and original audit are in `provenance/REPORT.md` and `provenance/artifacts/evaluation/`.
37
 
38
- ## MCP integration and platform distribution
39
 
40
- PR #32 at `c2c45ec62728d85b7472131a3df9ccb67afd4a02` launches `<bundle>/datafog-pii <bundle>` locally. It maps first_name/last_name to PERSON and street_address to STREET_ADDRESS; it ignores other model categories in favor of Core detectors. No downloads occur during MCP inference. Configure `[model].bundle_directory` after installing the bundle. See `DOWNLOAD.md`.
41
 
42
- Only **macOS ARM64** has a qualified executable in this release. The preserved binary uses the bundled ONNX Runtime 1.30.0 dylib, API 27, CPU execution. Require macOS 14.0+ for the packaged dylib (Mach-O LC_BUILD_VERSION minos 14.0). The model and tokenizer are platform-neutral; the native executable/library are not. `PLATFORMS.md` documents candidate builds for macOS Intel, Linux x86_64/AArch64 and Windows x86_64, including Windows adapter requirements. None is advertised as qualified before a native build and actual MCP fixture test. Do not copy the ARM dylib to another platform.
 
 
43
 
44
- The adapter previously timed out on a 100 MB input at a 120-second model deadline. This release does not establish the 100 MB complete-model-scan target, production throughput or real-world privacy guarantees. Finite window overlap cannot cover arbitrary entity lengths; 16 training spans had boundaries the tokenizer cannot express exactly. A clean result only concerns seven supported labels and accepted predictions.
45
 
46
- ## License and attribution
 
 
 
 
 
47
 
48
- DataFog additions, trained weights and runtime source: Apache License 2.0; `LICENSE`. DistilBERT backbone and tokenizer: Apache-2.0, retained backbone card. Training dataset: CC-BY-4.0, attribution in `NOTICE` and preserved dataset card. ONNX Runtime: MIT with complete upstream ThirdPartyNotices. Rust dependencies retain their own licenses in `licenses/cargo/`; `licenses/cargo-inventory.json` records all 98 locked packages across targets, a conservative superset rather than a claim that all are linked. See `LICENSING.md` for redistribution details.
 
 
 
 
 
49
 
50
- ## Reproduction and versioning
 
 
 
 
51
 
52
- `release.json` identifies the version, selection, revisions and weights. `manifest.json` contains SHA-256 and size for every distributed file except itself. `archives/datafog-pii-v0.1.0-macos-arm64.tar.gz` preserves executable mode and includes original bundle checksums. Pin a Hub commit SHA rather than `main` for downloads; tag `v0.1.0` is a convenient release alias after publication. `reproduction/` includes the original scripts and uv.lock; run those from a restored workspace layout following `provenance/README.md`. Original training optimizer states, raw datasets, obsolete caches and diagnostic probe weights are intentionally omitted. Exact inference artifacts are reproducible by checksum; bitwise repeatability of MPS training is not promised.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  - experimental
14
  - datafog
15
  ---
16
+ # DataFog PII
17
 
18
+ DataFog PII is a **65.2M-parameter English token classifier** for locating and redacting seven types of personally identifiable information. It returns typed spans, calibrated span confidence, UTF-8 byte offsets and redacted text through a local JSONL runtime. Inference uses the CPU and requires no network access after installation.
19
 
20
+ The frozen **model v0.1.0** achieves **98.32% exact typed-span F1** on 90,018 held-out synthetic documents. **Runtime v0.2.0** has qualified native bundles for five platform environments. The checkpoint and acceptance threshold remain **experimental**; performance on reviewed customer tickets has not been established.
21
 
22
+ [Model](#model) · [Files](#files) · [Usage](#usage) · [Input-and-output contract](#input-and-output-contract) · [Results](#results) · [Training](#training-and-reproduction) · [Limitations](#limitations) · [License](#license)
23
 
24
+ ## Model
25
 
26
+ | Property | Value |
27
+ |---|---|
28
+ | Backbone | [distilbert/distilbert-base-cased](https://huggingface.co/distilbert/distilbert-base-cased), pinned revision `6ea81172465e8b0ad3fddeed32b986cdcdcffcf0` |
29
+ | Parameters | 65,202,447, counted from the released safetensors tensor shapes |
30
+ | Precision | Float32 weights and ONNX inference |
31
+ | Task | BIO token classification: 15 classes, including `O` |
32
+ | Supported labels | `first_name`, `last_name`, `email`, `phone_number`, `street_address`, `customer_id`, `account_number` |
33
+ | Long-input processing | 512-token windows: 510 content tokens with 42-token overlap |
34
+ | Confidence | Pooled isotonic calibration of exact typed-span correctness |
35
+ | Experimental threshold | `0.6486486486486487`; authoritative value in `bundle/calibration.json` |
36
+ | Native execution | Rust + ONNX Runtime 1.30.0/API27, CPU |
37
+ | Model / runtime versions | Model `0.1.0`; native runtime `0.2.0`; versioned separately |
38
 
39
+ The root safetensors are a standard Transformers token-classification checkpoint. The native runtime additionally supplies complete-input windowing, boundary-fragment suppression, overlap resolution, calibration, thresholding and byte-based redaction. A generic Transformers pipeline does not reproduce that full contract. GLiNER is a separate CI comparison and is not this model's backbone or a release artifact.
40
 
41
+ ## Files
42
 
43
+ | File or directory | Purpose |
44
+ |---|---|
45
+ | `model.safetensors`, `config.json` | Frozen selected checkpoint and Transformers label mappings |
46
+ | `tokenizer.json`, `tokenizer_config.json`, `label-mappings.json` | Tokenizer and explicit class/label contract |
47
+ | `bundle/model.onnx`, `bundle/config.json`, `bundle/calibration.json` | Original native-model assets; native configuration differs from root Transformers configuration |
48
+ | `runtimes/v0.2.0/*.tar.gz` | Five platform-specific native bundles, including model assets, executable, matching ORT libraries and notices |
49
+ | `runtimes/v0.2.0/release-index.json` | Exact archive hashes, source/runtime provenance and native qualification reports |
50
+ | `runtimes/v0.2.0/tools/` | Hash-verifying bundle installer and archive helpers |
51
+ | `provenance/artifacts/evaluation/`, `provenance/REPORT.md` | Frozen detection, redaction, calibration and support-fixture results |
52
+ | `card-evidence/` | Original regex comparison summaries and recorded execution environment |
53
+ | `reproduction/`, `provenance/` | Training/evaluation code, locks, partition/source provenance and original audit |
54
+ | `LICENSE`, `NOTICE`, `licenses/`, `LICENSING.md` | Model/source licensing, dataset attribution and upstream dependency notices |
55
 
56
+ Pin model revision `02cc6ca86a11dcb2b328861687770a22a7ea1b76` and runtime revision `7cadd41e21f59b2a149e31378d073cf6ca057531` for reproducible downloads. Original tags `v0.1.0` and `runtime-v0.2.0` remain preserved. The original `bundle/` executable is macOS ARM64-only; use the versioned runtime archives for other platforms. The root manifest covers the original model files and current card evidence; the versioned runtime has its own manifest.
57
 
58
+ ## Usage
59
 
60
+ ### Install a native bundle
61
 
62
+ Use Python 3.12+ for the download/installer. Native inference itself requires neither Python nor a GPU. While this repository is private, authenticate using `hf auth login` with an account authorized for DataFog.
63
 
64
+ ```sh
65
+ python -m pip install huggingface_hub==1.27.0
66
+ ```
67
 
68
+ Select `TARGET` from the platform table below. This example downloads only that archive and its installer, verifies the expected archive and file hashes, and installs to a new directory.
69
 
70
+ ```python
71
+ import json
72
+ import subprocess
73
+ import sys
74
+ from pathlib import Path
75
+ from huggingface_hub import hf_hub_download
76
 
77
+ REPO = "DataFog/datafog-pii"
78
+ REVISION = "7cadd41e21f59b2a149e31378d073cf6ca057531"
79
+ PREFIX = "runtimes/v0.2.0"
80
+ TARGET = "macos-arm64" # Select your tested platform below.
81
+ downloads = Path("datafog-download").resolve()
82
+ bundle = Path("datafog-bundle").resolve() # Must not already exist.
83
 
84
+ def download(name):
85
+ return Path(hf_hub_download(
86
+ REPO, filename=f"{PREFIX}/{name}", revision=REVISION,
87
+ local_dir=downloads, force_download=True,
88
+ ))
89
 
90
+ index = json.loads(download("release-index.json").read_text(encoding="utf-8"))
91
+ record = next(item for item in index["platforms"] if item["platform"] == TARGET)
92
+ archive = download(record["archive"]["filename"])
93
+ download("tools/common.py")
94
+ installer = download("tools/install_bundle.py")
95
+ subprocess.run([
96
+ sys.executable, str(installer), "--archive", str(archive),
97
+ "--sha256", record["archive"]["sha256"], "--destination", str(bundle),
98
+ ], check=True)
99
+ ```
100
+
101
+ ### Run inference
102
+
103
+ Send one JSON object per line to `<bundle>/datafog-pii <bundle>` (`datafog-pii.exe` on Windows). The process can serve multiple requests without reloading the model.
104
+
105
+ ```python
106
+ import json
107
+ import os
108
+ import subprocess
109
+ from pathlib import Path
110
+
111
+ bundle = Path("datafog-bundle").resolve()
112
+ executable = bundle / ("datafog-pii.exe" if os.name == "nt" else "datafog-pii")
113
+ response = subprocess.run(
114
+ [str(executable), str(bundle)],
115
+ input=json.dumps({"text": "Contact John Smith at john@example.com."}) + "\n",
116
+ text=True, encoding="utf-8", capture_output=True, check=True,
117
+ )
118
+ result = json.loads(response.stdout)
119
+ print(result["redacted"])
120
+ # Contact [REDACTED] [REDACTED] at [REDACTED].
121
+ print([(f["label"], f["start"], f["end"]) for f in result["findings"]])
122
+ # [('first_name', 8, 12), ('last_name', 13, 18), ('email', 22, 38)]
123
+ ```
124
+
125
+ The example output was checked against the released runtime. See the [versioned download guide](runtimes/v0.2.0/DOWNLOAD.md) for installation details.
126
+
127
+ ### MCP integration
128
+
129
+ The model supplies `PERSON` (first/last-name spans) and `STREET_ADDRESS` to the proposed DataFog MCP integration. Other model categories are ignored by that adapter in favor of Core detectors. Configure the installed absolute path in the MCP policy:
130
+
131
+ ```toml
132
+ [model]
133
+ bundle_directory = "/absolute/path/to/datafog-bundle"
134
+ ```
135
+
136
+ Windows TOML paths can use literal strings, for example `bundle_directory = 'C:\DataFog\bundle'`. [MCP PR #33](https://github.com/DataFog/datafog-mcp/pull/33), stacked on #32, supplies Windows `.exe` selection. Qualified production/test compatibility revision: `01d7f74e693a7892ab1ed504d0d8f8c1e7ce51d3`. Qualification below covers that fixture contract; the final packaged-MCP/model release gate remains part of the coordinated release work.
137
+
138
+ ## Input-and-output contract
139
+
140
+ | Field | Meaning |
141
+ |---|---|
142
+ | Input `text` | Entire input string, processed across overlapping windows |
143
+ | Output `findings` | Accepted spans with `label`, `start`, `end`, `raw_score`, `confidence` |
144
+ | `start`, `end` | Half-open UTF-8 **byte** offsets into the original input; not character indices |
145
+ | `raw_score` | Minimum predicted-class token probability within a span |
146
+ | `confidence` | Pooled calibrated exact-span score, using clipped linear interpolation |
147
+ | `contains_pii` | At least one accepted finding among the seven supported labels |
148
+ | `redacted` | Original text with accepted spans replaced by `[REDACTED]` |
149
+
150
+ Class 0 is `O`; each supported label has `B` then `I` in the label order above. The complete mappings are in `label-mappings.json`. Keep weights, tokenizer, native configuration and calibration together. Malformed requests and unsupported full-window entities return explicit errors. Finite overlap does not cover arbitrarily long entities.
151
+
152
+ ## Results
153
+
154
+ ### Held-out detection
155
+
156
+ Evaluation uses **90,018 synthetic Nemotron-PII documents** and **248,739 gold entities**, with the checkpoint and threshold frozen before final-test evaluation. A true positive requires both the **exact label and exact span boundaries**. Precision, recall and F1 are percentages; overall scores are micro-averaged across the seven labels.
157
+
158
+ | Label | Gold entities | Precision | Recall | F1 |
159
+ |---|---:|---:|---:|---:|
160
+ | Overall | 248,739 | 99.01 | 97.63 | **98.32** |
161
+ | first_name | 74,498 | 99.40 | 98.30 | **98.85** |
162
+ | last_name | 52,864 | 99.31 | 97.96 | **98.63** |
163
+ | email | 49,246 | 98.57 | 97.74 | **98.15** |
164
+ | phone_number | 22,033 | 98.29 | 95.28 | **96.76** |
165
+ | street_address | 15,382 | 98.47 | 96.66 | **97.56** |
166
+ | customer_id | 19,085 | 99.27 | 98.00 | **98.63** |
167
+ | account_number | 15,631 | 98.76 | 96.83 | **97.78** |
168
+
169
+ Overall: **242,853 TP / 2,424 FP / 5,886 FN**. UTF-8 byte-union redaction precision is **99.66%**, recall **97.89%**, F1 **98.76%**; that coverage metric ignores labels and weights multibyte characters by byte length. There were 63,160 missed gold bytes and 10,061 unnecessary redaction bytes.
170
+
171
+ Among 16,233 target-clean documents, 81 had an accepted finding. Among 73,785 target-positive documents, 469 had no accepted finding; **3,947** had at least one missed entity, including partial misses. A document-level detection therefore does not imply complete PII coverage. These scores measure agreement with synthetic annotations, not reviewed private-ticket accuracy. [Full test metrics](provenance/artifacts/evaluation/test.json).
172
+
173
+ ### Regex comparison
174
+
175
+ Both systems were evaluated on the **same 90,018 documents**, restricted to **email and phone**, the two labels supported by the recorded DataFog 4.8.1 regex baseline within this model's label contract. Combined support is 71,279 gold entities. Other labels are excluded from both sides. Scores are percentages; the better F1 in each row is bold.
176
+
177
+ | Scope | Model precision | Model recall | Model F1 | Regex precision | Regex recall | Regex F1 |
178
+ |---|---:|---:|---:|---:|---:|---:|
179
+ | Email + phone | 98.48 | 96.98 | **97.73** | 71.71 | 93.54 | 81.18 |
180
+ | email | 98.57 | 97.74 | 98.15 | 99.38 | 98.99 | **99.19** |
181
+ | phone_number | 98.29 | 95.28 | **96.76** | 40.81 | 81.36 | 54.35 |
182
+
183
+ The regex baseline performs better on email F1; the model performs better on phone F1 and the combined scoped score. This is a comparison with the recorded `datafog==4.8.1` release, not a benchmark of today's Core detector or a GLiNER comparison. [Regex metrics](card-evidence/regex-metrics.json), [model scoped metrics](card-evidence/model-email-phone-metrics.json), [baseline version/API provenance](card-evidence/regex-provenance.json).
184
+
185
+ ### Support fixtures
186
+
187
+ Seven hand-authored synthetic documents contain 18 gold entities and cover Unicode signatures, pasted fields, multiline addresses, repeated entities, clean numeric text and long inputs.
188
+
189
+ | Evaluation | Precision | Recall | F1 | TP / FP / FN |
190
+ |---|---:|---:|---:|---|
191
+ | Exact typed spans | 93.33% | 77.78% | 84.85% | 14 / 1 / 4 |
192
+ | UTF-8 redaction byte coverage | 100.00% | 64.32% | 78.29% | 128 / 0 / 71 bytes |
193
+
194
+ These cases expose failures despite the high aggregate held-out score: email recovered **2/4** entities, phone **0/1**, and street address **0/1**. Their small support does not estimate general label accuracy. [Full fixture metrics](provenance/artifacts/evaluation/support-fixtures.json).
195
+
196
+ ### Calibration
197
+
198
+ Calibration fit used 501 separate documents with 1,220 predicted spans (1,172 exact matches); threshold selection used another 532 documents. The threshold maximizes exact typed-span F1 on calibration-select, with ties favoring recall and then the higher threshold. Test reliability uses all 250,771 predicted spans **before thresholding**, including rejected predictions. Lower Brier and expected calibration error (ECE) are better.
199
+
200
+ | Score | Brier | ECE |
201
+ |---|---:|---:|
202
+ | Raw token-derived span score | 0.014821 | 0.011747 |
203
+ | Calibrated exact-span score | 0.013717 | 0.007427 |
204
+
205
+ Reliability correctness means exact label and boundaries. Missed entities are absent from the prediction population, so recall must be read separately. ECE uses ten equal-width bins and depends on binning. Only 48 incorrect spans supported calibration fitting; sparse confidence regions remain uncertain. This is a population-specific experimental score, not a universal probability or a production threshold.
206
+
207
+ ### Runtime performance
208
+
209
+ These are the **original v0.1.0 macOS ARM64 measurements**, using native Rust + ONNX Runtime CPU with **one inference thread**. The recorded environment is macOS 26.6.2 ARM64; the chip model was not recorded. They are single observations, not p50/p95 benchmarks, and do not measure runtime v0.2.0 on all five platforms.
210
+
211
+ | Input UTF-8 bytes | Warm end-to-end latency (ms) |
212
+ |---|---:|
213
+ | 115 | 6.005 |
214
+ | 752 | 20.579 |
215
+ | 2,010 | 81.282 |
216
+ | 4,502 | 172.720 |
217
+
218
+ | Resource measurement | Observed value |
219
+ |---|---:|
220
+ | Cold process launch through first empty-input response | 170.013 ms |
221
+ | Maximum RSS sampled after responses | 473.4 MiB |
222
+ | Original unpacked bundle size | 286.2 MiB |
223
+
224
+ The four request lengths were selected at observed min/median/p90/max validation byte lengths, **not latency percentiles**. Warm timings include end-to-end request processing. The RSS sample is not an allocator high-water mark. Cross-platform percentile latency, sustained throughput and large-file performance remain unmeasured. [Benchmark artifact](provenance/artifacts/native-benchmark.json), [recorded environment](card-evidence/environment-check.json).
225
+
226
+ ### Native platform qualification
227
+
228
+ Runtime v0.2.0 preserves the model/configuration/calibration bytes while adding platform library selection and explicit telemetry disabling. Each target passed native unit/boundary tests, installation on a separate fresh runner, seven parity/golden cases and **33 actual MCP runtime/CSV tests with zero failures, errors or skips**.
229
+
230
+ | Archive target | Actual tested environment | MCP tests | Max native/Python logit difference |
231
+ |---|---|---:|---:|
232
+ | `linux-x86_64` | Linux-6.8.0-1064-azure-x86_64-with-glibc2.35 | 33 passed | 0.0 |
233
+ | `linux-aarch64` | Linux-6.17.0-1022-azure-aarch64-with-glibc2.39 | 33 passed | 0.0 |
234
+ | `macos-arm64` | macOS-14.8.9-arm64-arm-64bit | 33 passed | 0.0 |
235
+ | `macos-x86_64` | macOS-15.7.9-x86_64-i386-64bit | 33 passed | 0.0 |
236
+ | `windows-x86_64` | Windows-2022Server-10.0.20348-SP0 | 33 passed | 0.0 |
237
+
238
+ All five matched the Python reference logits bit-for-bit on the seven fixtures and matched the original Mac golden boundaries/labels/redaction. This establishes fixture consistency, not general detection quality or speed. [Release index and reports](runtimes/v0.2.0/release-index.json), [successful native qualification](https://github.com/DataFog/DataFog-models/actions/runs/37249503334).
239
+
240
+ macOS packaging minimums are ARM64 14 and Intel 15; only the named environments were tested. Intel macOS uses a source build of ONNX Runtime commit `f2c39fe2f838cf35ce7da92824f5a5e3ee6e88a7`, since upstream has no 1.30.0 Intel binary. Windows requires Microsoft's x64 Visual C++ Redistributable (2019-compatible v14). Ubuntu ARM64 evidence does not qualify Raspberry Pi OS, and Server 2022 evidence does not qualify every desktop Windows edition. Other OS versions/distributions, Windows ARM64 and GPU execution remain unqualified.
241
+
242
+ ## Training and reproduction
243
+
244
+ Trained on [NVIDIA Nemotron-PII](https://huggingface.co/datasets/nvidia/Nemotron-PII), pinned revision `b70ffaf5ff39e079776134c5bf4381f00a9fd1ed`. Documents are grouped by UID, exact text and normalized annotated templates to reduce family leakage. Structural filtering and split checks do not eliminate semantic annotation noise or every possible near-duplicate.
245
+
246
+ | Partition | Documents |
247
+ |---|---:|
248
+ | Training | 89,307 |
249
+ | Validation | 514 |
250
+ | Calibration fit | 501 |
251
+ | Calibration select | 532 |
252
+ | Held-out test | 90,018 |
253
+
254
+ Training used seed 42, float32 MPS, batch size 16, learning rate 2e-5, weight decay 0.01 and a predeclared two-epoch recipe. Total elapsed time was 8,102 seconds. **Epoch 2** was selected by minimum validation token cross entropy:
255
+
256
+ | Epoch | Validation token loss | Release checkpoint |
257
+ |---|---:|---|
258
+ | 1 | 0.003570595682300194 | No |
259
+ | 2 | 0.0018816228932983772 | Yes |
260
+
261
+ No final-test tuning or release-time retraining occurred. [Training history](provenance/artifacts/training/complete.json), [data provenance](provenance/docs/data.md), [partition/source manifest](provenance/artifacts/data-manifest.json). Inference/evaluation code and dependency locks are in `reproduction/`; restore the documented workspace layout following [provenance/README.md](provenance/README.md). Exact inference assets are reproducible by checksum; bitwise MPS training repeatability is not promised. Raw datasets, optimizer states and obsolete diagnostic checkpoints are omitted from this release.
262
+
263
+ ## Limitations
264
+
265
+ - Evaluation is synthetic; reviewed English customer-support and other real-world domains remain unvalidated. Labels can be semantically wrong or incomplete despite structural filtering.
266
+ - Only seven labels are supported. `contains_pii: false` is not assurance that a document contains no sensitive information or missed supported entities.
267
+ - The small support-fixture evaluation includes misses in email, phone and street address; aggregate test scores should not hide those failures.
268
+ - Tokenization cannot express every character boundary: 16 unrepresentable training-source entity boundaries were recorded. Window overlap cannot cover arbitrary entity lengths.
269
+ - The MCP adapter previously timed out on a 100 MB input at a 120-second model deadline. This release does not establish a 100 MB complete-model-scan target or a production throughput guarantee.
270
+ - Native qualification covers the reported environments and finite fixture contract. Model inference performance and the final packaged-MCP/model release acceptance are separate checks.
271
+
272
+ ## License
273
+
274
+ DataFog source and trained weights: **Apache-2.0**. DistilBERT backbone/tokenizer: Apache-2.0. Training data: **CC-BY-4.0**; attribution to NVIDIA and Amy Steier, Andre Manoel, Alexa Haushalter and Maarten Van Segbroeck, *Nemotron-PII* (2025), is preserved in `NOTICE` and the dataset card. ONNX Runtime: MIT with complete third-party notices. The Rust inventory preserves licenses for all 98 locked packages, a conservative target superset. The MCP snapshot retains MIT licensing.
275
+
276
+ See [LICENSE](LICENSE), [NOTICE](NOTICE), [LICENSING.md](LICENSING.md) and `licenses/` for redistribution details. Source and release tooling: [DataFog/DataFog-models](https://github.com/DataFog/DataFog-models).
card-evidence/environment-check.json ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "platform": "macOS-26.6.2-arm64-arm-64bit",
3
+ "python": "3.12.13",
4
+ "versions": {
5
+ "torch": "2.14.0",
6
+ "transformers": "5.17.0",
7
+ "datasets": "5.0.1",
8
+ "accelerate": "1.15.0",
9
+ "safetensors": "0.8.0",
10
+ "scikit-learn": "1.9.1",
11
+ "onnx": "1.23.0",
12
+ "onnxscript": "0.7.2",
13
+ "onnxruntime": "1.30.0"
14
+ },
15
+ "mps_built": true,
16
+ "mps_available": true,
17
+ "onnxruntime_providers": [
18
+ "CoreMLExecutionProvider",
19
+ "AzureExecutionProvider",
20
+ "CPUExecutionProvider"
21
+ ],
22
+ "mps_smoke_check": {
23
+ "finite_loss": true,
24
+ "finite_gradients": true,
25
+ "weights_changed": true
26
+ }
27
+ }
card-evidence/model-email-phone-metrics.json ADDED
@@ -0,0 +1,267 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "threshold": 0.6486486486486487,
3
+ "overall": {
4
+ "tp": 69126,
5
+ "fp": 1064,
6
+ "fn": 2153,
7
+ "predicted": 70190,
8
+ "support": 71279,
9
+ "precision": 0.9848411454623166,
10
+ "recall": 0.9697947502069333,
11
+ "f1": 0.9772600357675533
12
+ },
13
+ "per_label": {
14
+ "first_name": {
15
+ "tp": 0,
16
+ "fp": 0,
17
+ "fn": 0,
18
+ "predicted": 0,
19
+ "support": 0,
20
+ "precision": 0.0,
21
+ "recall": 0.0,
22
+ "f1": 0.0
23
+ },
24
+ "last_name": {
25
+ "tp": 0,
26
+ "fp": 0,
27
+ "fn": 0,
28
+ "predicted": 0,
29
+ "support": 0,
30
+ "precision": 0.0,
31
+ "recall": 0.0,
32
+ "f1": 0.0
33
+ },
34
+ "email": {
35
+ "tp": 48134,
36
+ "fp": 698,
37
+ "fn": 1112,
38
+ "predicted": 48832,
39
+ "support": 49246,
40
+ "precision": 0.9857060943643512,
41
+ "recall": 0.9774194858465662,
42
+ "f1": 0.9815453006790513
43
+ },
44
+ "phone_number": {
45
+ "tp": 20992,
46
+ "fp": 366,
47
+ "fn": 1041,
48
+ "predicted": 21358,
49
+ "support": 22033,
50
+ "precision": 0.9828635640041202,
51
+ "recall": 0.9527526891480961,
52
+ "f1": 0.9675739208591644
53
+ },
54
+ "street_address": {
55
+ "tp": 0,
56
+ "fp": 0,
57
+ "fn": 0,
58
+ "predicted": 0,
59
+ "support": 0,
60
+ "precision": 0.0,
61
+ "recall": 0.0,
62
+ "f1": 0.0
63
+ },
64
+ "customer_id": {
65
+ "tp": 0,
66
+ "fp": 0,
67
+ "fn": 0,
68
+ "predicted": 0,
69
+ "support": 0,
70
+ "precision": 0.0,
71
+ "recall": 0.0,
72
+ "f1": 0.0
73
+ },
74
+ "account_number": {
75
+ "tp": 0,
76
+ "fp": 0,
77
+ "fn": 0,
78
+ "predicted": 0,
79
+ "support": 0,
80
+ "precision": 0.0,
81
+ "recall": 0.0,
82
+ "f1": 0.0
83
+ }
84
+ },
85
+ "redaction_byte_coverage": {
86
+ "tp": 1533224,
87
+ "fp": 5298,
88
+ "fn": 32976,
89
+ "predicted": 1538522,
90
+ "support": 1566200,
91
+ "precision": 0.9965564353320915,
92
+ "recall": 0.9789452177244286,
93
+ "f1": 0.9876723262179351
94
+ },
95
+ "documents": {
96
+ "total": 90018,
97
+ "positive_support": 43658,
98
+ "clean_support": 46360,
99
+ "false_positive": 119,
100
+ "false_negative": 424,
101
+ "with_any_missed_entity": 1717,
102
+ "with_any_spurious_entity": 784
103
+ },
104
+ "raw_reliability": {
105
+ "support": 72438,
106
+ "brier": 0.021829755183972725,
107
+ "ece": 0.018571813107282845,
108
+ "binning": "10 equal-width bins; left-inclusive, right-exclusive except final bin includes 1",
109
+ "bins": [
110
+ {
111
+ "lower": 0.0,
112
+ "upper": 0.1,
113
+ "support": 0,
114
+ "mean_score": null,
115
+ "accuracy": null
116
+ },
117
+ {
118
+ "lower": 0.1,
119
+ "upper": 0.2,
120
+ "support": 0,
121
+ "mean_score": null,
122
+ "accuracy": null
123
+ },
124
+ {
125
+ "lower": 0.2,
126
+ "upper": 0.3,
127
+ "support": 0,
128
+ "mean_score": null,
129
+ "accuracy": null
130
+ },
131
+ {
132
+ "lower": 0.3,
133
+ "upper": 0.4,
134
+ "support": 3,
135
+ "mean_score": 0.3863314212266449,
136
+ "accuracy": 0.0
137
+ },
138
+ {
139
+ "lower": 0.4,
140
+ "upper": 0.5,
141
+ "support": 95,
142
+ "mean_score": 0.4782374044011286,
143
+ "accuracy": 0.14736842105263157
144
+ },
145
+ {
146
+ "lower": 0.5,
147
+ "upper": 0.6,
148
+ "support": 1049,
149
+ "mean_score": 0.5422019699381606,
150
+ "accuracy": 0.21067683508102955
151
+ },
152
+ {
153
+ "lower": 0.6,
154
+ "upper": 0.7,
155
+ "support": 618,
156
+ "mean_score": 0.6473821870437928,
157
+ "accuracy": 0.39158576051779936
158
+ },
159
+ {
160
+ "lower": 0.7,
161
+ "upper": 0.8,
162
+ "support": 641,
163
+ "mean_score": 0.751043175894807,
164
+ "accuracy": 0.5647425897035881
165
+ },
166
+ {
167
+ "lower": 0.8,
168
+ "upper": 0.9,
169
+ "support": 890,
170
+ "mean_score": 0.8564894739866963,
171
+ "accuracy": 0.7
172
+ },
173
+ {
174
+ "lower": 0.9,
175
+ "upper": 1.0,
176
+ "support": 69142,
177
+ "mean_score": 0.9973556895161934,
178
+ "accuracy": 0.989427554887044
179
+ }
180
+ ]
181
+ },
182
+ "confidence_reliability": {
183
+ "support": 72438,
184
+ "brier": 0.019584471057273317,
185
+ "ece": 0.007393019371353872,
186
+ "binning": "10 equal-width bins; left-inclusive, right-exclusive except final bin includes 1",
187
+ "bins": [
188
+ {
189
+ "lower": 0.0,
190
+ "upper": 0.1,
191
+ "support": 455,
192
+ "mean_score": 0.0007816997886464416,
193
+ "accuracy": 0.15164835164835164
194
+ },
195
+ {
196
+ "lower": 0.1,
197
+ "upper": 0.2,
198
+ "support": 9,
199
+ "mean_score": 0.1469554986102011,
200
+ "accuracy": 0.1111111111111111
201
+ },
202
+ {
203
+ "lower": 0.2,
204
+ "upper": 0.3,
205
+ "support": 317,
206
+ "mean_score": 0.22929724681409186,
207
+ "accuracy": 0.2302839116719243
208
+ },
209
+ {
210
+ "lower": 0.3,
211
+ "upper": 0.4,
212
+ "support": 111,
213
+ "mean_score": 0.32947968452441123,
214
+ "accuracy": 0.26126126126126126
215
+ },
216
+ {
217
+ "lower": 0.4,
218
+ "upper": 0.5,
219
+ "support": 31,
220
+ "mean_score": 0.45223669021161506,
221
+ "accuracy": 0.25806451612903225
222
+ },
223
+ {
224
+ "lower": 0.5,
225
+ "upper": 0.6,
226
+ "support": 1311,
227
+ "mean_score": 0.5399227828147602,
228
+ "accuracy": 0.4248665141113654
229
+ },
230
+ {
231
+ "lower": 0.6,
232
+ "upper": 0.7,
233
+ "support": 2174,
234
+ "mean_score": 0.6484848726369532,
235
+ "accuracy": 0.7465501379944802
236
+ },
237
+ {
238
+ "lower": 0.7,
239
+ "upper": 0.8,
240
+ "support": 3,
241
+ "mean_score": 0.7473658018005699,
242
+ "accuracy": 1.0
243
+ },
244
+ {
245
+ "lower": 0.8,
246
+ "upper": 0.9,
247
+ "support": 5,
248
+ "mean_score": 0.8363934597936151,
249
+ "accuracy": 0.8
250
+ },
251
+ {
252
+ "lower": 0.9,
253
+ "upper": 1.0,
254
+ "support": 68022,
255
+ "mean_score": 0.9937033861243243,
256
+ "accuracy": 0.9924142189291699
257
+ }
258
+ ]
259
+ },
260
+ "reliability_population": "All predicted spans before threshold, correctness = exact typed span. Missed gold entities excluded; see recall.",
261
+ "confidence_semantics": "Pooled calibrated exact-span score",
262
+ "coverage_semantics": "Union of UTF-8 bytes; ignores labels. Zero-denominator P/R/F1 reported as 0.",
263
+ "comparison_scope": [
264
+ "email",
265
+ "phone_number"
266
+ ]
267
+ }
card-evidence/regex-metrics.json ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "threshold": null,
3
+ "overall": {
4
+ "tp": 66676,
5
+ "fp": 26307,
6
+ "fn": 4603,
7
+ "predicted": 92983,
8
+ "support": 71279,
9
+ "precision": 0.7170773152081563,
10
+ "recall": 0.9354227752914603,
11
+ "f1": 0.8118250112624953
12
+ },
13
+ "per_label": {
14
+ "first_name": {
15
+ "tp": 0,
16
+ "fp": 0,
17
+ "fn": 0,
18
+ "predicted": 0,
19
+ "support": 0,
20
+ "precision": 0.0,
21
+ "recall": 0.0,
22
+ "f1": 0.0
23
+ },
24
+ "last_name": {
25
+ "tp": 0,
26
+ "fp": 0,
27
+ "fn": 0,
28
+ "predicted": 0,
29
+ "support": 0,
30
+ "precision": 0.0,
31
+ "recall": 0.0,
32
+ "f1": 0.0
33
+ },
34
+ "email": {
35
+ "tp": 48751,
36
+ "fp": 306,
37
+ "fn": 495,
38
+ "predicted": 49057,
39
+ "support": 49246,
40
+ "precision": 0.9937623580732617,
41
+ "recall": 0.9899484222068797,
42
+ "f1": 0.9918517237520726
43
+ },
44
+ "phone_number": {
45
+ "tp": 17925,
46
+ "fp": 26001,
47
+ "fn": 4108,
48
+ "predicted": 43926,
49
+ "support": 22033,
50
+ "precision": 0.4080726676683513,
51
+ "recall": 0.8135523986747152,
52
+ "f1": 0.5435194590578996
53
+ },
54
+ "street_address": {
55
+ "tp": 0,
56
+ "fp": 0,
57
+ "fn": 0,
58
+ "predicted": 0,
59
+ "support": 0,
60
+ "precision": 0.0,
61
+ "recall": 0.0,
62
+ "f1": 0.0
63
+ },
64
+ "customer_id": {
65
+ "tp": 0,
66
+ "fp": 0,
67
+ "fn": 0,
68
+ "predicted": 0,
69
+ "support": 0,
70
+ "precision": 0.0,
71
+ "recall": 0.0,
72
+ "f1": 0.0
73
+ },
74
+ "account_number": {
75
+ "tp": 0,
76
+ "fp": 0,
77
+ "fn": 0,
78
+ "predicted": 0,
79
+ "support": 0,
80
+ "precision": 0.0,
81
+ "recall": 0.0,
82
+ "f1": 0.0
83
+ }
84
+ },
85
+ "redaction_byte_coverage": {
86
+ "tp": 1513432,
87
+ "fp": 282773,
88
+ "fn": 52768,
89
+ "predicted": 1796205,
90
+ "support": 1566200,
91
+ "precision": 0.8425719781428066,
92
+ "recall": 0.9663082620354999,
93
+ "f1": 0.9002080356173632
94
+ },
95
+ "documents": {
96
+ "total": 90018,
97
+ "positive_support": 43658,
98
+ "clean_support": 46360,
99
+ "false_positive": 11261,
100
+ "false_negative": 344,
101
+ "with_any_missed_entity": 3806,
102
+ "with_any_spurious_entity": 20923
103
+ },
104
+ "coverage_semantics": "Union of UTF-8 bytes; ignores labels. Zero-denominator P/R/F1 reported as 0.",
105
+ "score_semantics": "Regex match indicator, no probability calibration evaluated",
106
+ "comparison_scope": [
107
+ "email",
108
+ "phone_number"
109
+ ],
110
+ "unsupported_labels": [
111
+ "first_name",
112
+ "last_name",
113
+ "street_address",
114
+ "customer_id",
115
+ "account_number"
116
+ ]
117
+ }
card-evidence/regex-provenance.json ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "package": "datafog",
3
+ "version": "4.8.1",
4
+ "official_repository": "https://github.com/DataFog/datafog-python",
5
+ "release": "https://pypi.org/project/datafog/4.8.1/",
6
+ "input_sha256": "f7737b588c95c0910a58374a9bee655cb5d4f080da795ed0d74a676da6d9d128",
7
+ "documents": 90018,
8
+ "elapsed_seconds": 16.65799858304672,
9
+ "API": "datafog.scan(text, engine='regex', entity_types=['EMAIL','PHONE'])",
10
+ "offset_conversion": "Python character offsets converted to original UTF-8 byte offsets",
11
+ "dependencies": {
12
+ "pydantic_core": "2.46.5",
13
+ "pydantic": "2.13.5",
14
+ "pydantic-settings": "2.15.0",
15
+ "datafog": "4.8.1",
16
+ "annotated-types": "0.8.0",
17
+ "typing-inspection": "0.4.4",
18
+ "python-dotenv": "1.2.3",
19
+ "typing_extensions": "4.16.0"
20
+ }
21
+ }
manifest.json CHANGED
@@ -23,8 +23,8 @@
23
  "bytes": 2526
24
  },
25
  "README.md": {
26
- "sha256": "4569cdcf29a50391127f5889a6b67296583bb7a9c4f149662af0ccee49bcd421",
27
- "bytes": 6451
28
  },
29
  "archives/datafog-pii-v0.1.0-macos-arm64.tar.gz": {
30
  "sha256": "2dd5251c1b984011852a46a3f4f30b176decf82f543a691c5a288e0a9f93b7a1",
@@ -1957,6 +1957,22 @@
1957
  "validation.json": {
1958
  "sha256": "98ef0cd2b71c95a57a5bb054abe41f6165cc8cdeab05145bc984b445001013da",
1959
  "bytes": 464
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1960
  }
1961
  }
1962
  }
 
23
  "bytes": 2526
24
  },
25
  "README.md": {
26
+ "sha256": "80205bcdc3939661cb9151fcdf71c5599635083b09ef62426adf78542e7da46c",
27
+ "bytes": 19311
28
  },
29
  "archives/datafog-pii-v0.1.0-macos-arm64.tar.gz": {
30
  "sha256": "2dd5251c1b984011852a46a3f4f30b176decf82f543a691c5a288e0a9f93b7a1",
 
1957
  "validation.json": {
1958
  "sha256": "98ef0cd2b71c95a57a5bb054abe41f6165cc8cdeab05145bc984b445001013da",
1959
  "bytes": 464
1960
+ },
1961
+ "card-evidence/environment-check.json": {
1962
+ "sha256": "7ce07dba86189bebbd023c42e407ba05a9872dbf92c36a28e5788cfcb3cb6177",
1963
+ "bytes": 612
1964
+ },
1965
+ "card-evidence/regex-provenance.json": {
1966
+ "sha256": "c0e0a3eabbfb8aaed91c4386d2eaca4d44c2fb553b4d18b79ac3f194063b27a3",
1967
+ "bytes": 765
1968
+ },
1969
+ "card-evidence/regex-metrics.json": {
1970
+ "sha256": "ff38e4cee40469881d484d217ce6a60ab6c56349819ec7fd28ab4c26715ce243",
1971
+ "bytes": 2464
1972
+ },
1973
+ "card-evidence/model-email-phone-metrics.json": {
1974
+ "sha256": "98d75034af1a0de4eee52f7b6d98c703a0de22901ced0e7b38c99454e4f4b3c2",
1975
+ "bytes": 6122
1976
  }
1977
  }
1978
  }