v23: the ORGANIZATION-boundary build — every gate line passes on both seeds
Browse files- README.md +91 -74
- onnx/model_quantized.onnx +2 -2
README.md
CHANGED
|
@@ -29,73 +29,97 @@ classifier only produces a second, weaker opinion that then competes with the
|
|
| 29 |
layer that was right. This model is built to leave them alone — and measurably
|
| 30 |
does (see the touch rate below).
|
| 31 |
|
| 32 |
-
##
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
|
| 41 |
-
|-
|
| 42 |
-
|
|
| 43 |
-
|
|
| 44 |
-
|
|
| 45 |
-
|
|
| 46 |
-
|
|
| 47 |
-
|
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
|
| 78 |
## Read this before using it
|
| 79 |
|
| 80 |
-
**Arabic is the weakest language, and the gap is recall.**
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
the aggregate numbers as uniform accuracy across languages.
|
| 84 |
|
| 85 |
**Trained only on synthetic data.** Entity pools are finite, so scores on
|
| 86 |
-
generated text overstate real-world ability. Judge it on the external
|
| 87 |
-
|
| 88 |
-
|
| 89 |
|
| 90 |
**Arabizi is a reconstruction.** No corpus contains it; its conventions
|
| 91 |
(3=ain, 7=haa, 2=hamza) are our model of how people type, not observed data.
|
| 92 |
|
| 93 |
-
**DATE_TIME scores 0.000
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
|
|
|
|
|
|
|
|
|
| 99 |
|
| 100 |
## Use
|
| 101 |
|
|
@@ -121,22 +145,15 @@ logits = sess.run(None, {i.name: enc[i.name] for i in sess.get_inputs()})[0]
|
|
| 121 |
**Include `<s>`/`</s>` in the tokenizer call for full-fidelity results** — this
|
| 122 |
is what closed most of the gap between the Python-decode number and the
|
| 123 |
production-engine number above. `AutoTokenizer.__call__` does this by default;
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
Pair it with a regex/checksum layer for the structured identifiers it
|
| 127 |
-
deliberately ignores.
|
| 128 |
-
|
| 129 |
-
## Training data
|
| 130 |
-
|
| 131 |
-
Fully synthetic, no real person's data. A meaningful share of rows contain no
|
| 132 |
-
entity at all; those entity-free negatives are what keeps the touch rate at
|
| 133 |
-
zero. The published corpus `ScienceSoft/scnsoft-pii-synthetic-corpus` is the
|
| 134 |
-
v13 build's corpus; v15 trained on a later generator (address shapes, LOCATION
|
| 135 |
-
roles, Arabic address vocabulary, Mimesis name pools) whose export has not been
|
| 136 |
-
published yet.
|
| 137 |
|
| 138 |
-
|
| 139 |
|
| 140 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
|
| 142 |
-
|
|
|
|
|
|
| 29 |
layer that was right. This model is built to leave them alone — and measurably
|
| 30 |
does (see the touch rate below).
|
| 31 |
|
| 32 |
+
## This build: v23
|
| 33 |
+
|
| 34 |
+
Gate run 2026-09-09, scored through the production Rust inference engine, not a
|
| 35 |
+
Python decode of the same ONNX. **Every one of the eight gate lines passed, on
|
| 36 |
+
both training seeds** — the first build since v15 to do so.
|
| 37 |
+
|
| 38 |
+
| gate line | v23 seed 1234 | v23 seed 4321 | bar |
|
| 39 |
+
|---|---|---|---|
|
| 40 |
+
| observed defects | 21/21 | 21/21 | all must pass |
|
| 41 |
+
| regex-owned identifiers wrongly touched | **0.1 %** | **0.0 %** | ≤ 15 % |
|
| 42 |
+
| recall on real Russian PII (hivetrace) | 0.9429 | 0.9357 | ≥ 0.90 |
|
| 43 |
+
| int8 vs fp32 F1 cost | −0.0003 | +0.0043 | ≤ 0.02 |
|
| 44 |
+
| latency p95 @ 512 tok, CPU | 192.6 ms | 188.9 ms | ≤ 200 |
|
| 45 |
+
| name-origin bias delta | **0.0000** | **0.0000** | ≤ 0.05 |
|
| 46 |
+
| vs incumbent, worst language (ru) | +0.1538 [+0.1012, +0.2082] | +0.1308 [+0.0809, +0.1870] | CI lower ≥ −0.02 |
|
| 47 |
+
| held-out register frames, engine-filtered | **0.8870** | 0.8860 | ≥ 0.8786 |
|
| 48 |
+
|
| 49 |
+
Production-engine numbers on the frozen file (`n = 3,300`): filtered F1 **0.9353**
|
| 50 |
+
(P 0.9899 / R 0.8864), production-masked (PERSON + ADDRESS) **0.9795**. Per
|
| 51 |
+
entity: ADDRESS 1.000 · ORGANIZATION 0.9988 · LOCATION 0.9984 · PERSON 0.9657.
|
| 52 |
+
On the held-out register frames, raw F1 0.9438 and filtered 0.8870.
|
| 53 |
+
|
| 54 |
+
Real-text benchmarks, unchanged in method from previous builds and **reported,
|
| 55 |
+
not gated**: hivetrace ru 0.8713 · TAB en incl. quasi-identifiers 0.7111 ·
|
| 56 |
+
IAHLT ar 0.5490 · Wojood ar 0.5384.
|
| 57 |
+
|
| 58 |
+
**What changed from v15.** One lever: `ORG_ARTICLE_FRAMES` — training frames
|
| 59 |
+
whose definite article or head noun sits immediately before the organisation
|
| 60 |
+
slot and **outside** the span. It targets the largest single ORGANIZATION error
|
| 61 |
+
class this project has measured: on two unsteered real-text sets the previous
|
| 62 |
+
build lost 9–10 % of GermEval's gold organisations and 16 % of TAB's to a span
|
| 63 |
+
that started one word early.
|
| 64 |
+
|
| 65 |
+
## Where it stands against other PII models
|
| 66 |
+
|
| 67 |
+
Scored on [piibench](https://huggingface.co/datasets/ScienceSoft/piibench), our
|
| 68 |
+
own open benchmark — real text carried in the wire shapes outbound traffic
|
| 69 |
+
actually uses (JSON bodies, XML, SQL, CSV, key=value, logs, OCR noise), scored
|
| 70 |
+
character-level and label-agnostically.
|
| 71 |
+
|
| 72 |
+
**This artifact scores 0.772 against the strongest independent entrant's 0.753**
|
| 73 |
+
(NVIDIA's GLiNER PII), a paired difference of **+0.0192, 95 % CI
|
| 74 |
+
[+0.0100, +0.0269]** — separable.
|
| 75 |
+
|
| 76 |
+
Two things belong in the same breath as that number.
|
| 77 |
+
|
| 78 |
+
**The claim is about this artifact, not the recipe.** The same corpus trained on
|
| 79 |
+
a second seed scores 0.759, and *that* difference from GLiNER is +0.0057
|
| 80 |
+
[−0.0031, +0.0143] — **not** separable. A shipped model is one specific set of
|
| 81 |
+
weights, so the board position is real; but do not read it as "this recipe beats
|
| 82 |
+
GLiNER every time", because measured across two seeds it does not.
|
| 83 |
+
|
| 84 |
+
**We win on recall and lose on precision.** 0.908 against their 0.785 on recall;
|
| 85 |
+
0.685 against their 0.743 on precision. For a data-loss-prevention product that
|
| 86 |
+
is the right side of the trade — a miss is personal data leaving the machine,
|
| 87 |
+
a false positive is a word masked that needn't have been — but it is a trade,
|
| 88 |
+
and the aggregate hides it.
|
| 89 |
+
|
| 90 |
+
**Latency, measured the same way for every entrant**: 20.3 ms median per request
|
| 91 |
+
against GLiNER's 140.6 ms, batch size 1, p95 33.5 ms against 199.7 ms. Both on
|
| 92 |
+
the same shared workstation, ours under a *higher* load average (20.7 vs 11.1),
|
| 93 |
+
so that gap is if anything understated. Absolute milliseconds from a machine
|
| 94 |
+
under load are pessimistic; the ordering is the informative part.
|
| 95 |
+
|
| 96 |
+
**Touch rate 0.020 against GLiNER's 0.304.** This model fires on the identifiers
|
| 97 |
+
a regex already owns roughly fifteen times less often. That is the property the
|
| 98 |
+
label set was designed for, and it is not on the ranking axis.
|
| 99 |
|
| 100 |
## Read this before using it
|
| 101 |
|
| 102 |
+
**Arabic is the weakest language, and the gap is recall.** 0.54–0.55 on the
|
| 103 |
+
Arabic benchmarks against 0.87 for Russian. Do not read the aggregate as uniform
|
| 104 |
+
accuracy across languages.
|
|
|
|
| 105 |
|
| 106 |
**Trained only on synthetic data.** Entity pools are finite, so scores on
|
| 107 |
+
generated text overstate real-world ability. Judge it on the external benchmarks
|
| 108 |
+
and on piibench, not on the synthetic figures — and note that hivetrace is
|
| 109 |
+
itself synthetic, expert-annotated but not real text.
|
| 110 |
|
| 111 |
**Arabizi is a reconstruction.** No corpus contains it; its conventions
|
| 112 |
(3=ain, 7=haa, 2=hamza) are our model of how people type, not observed data.
|
| 113 |
|
| 114 |
+
**DATE_TIME scores 0.000 on the frozen file by design, not by defect.** The
|
| 115 |
+
production filter keeps a date span only next to an explicit birth-context cue
|
| 116 |
+
(`born on`, `DOB:`, …), and that corpus carries none, so every date the model
|
| 117 |
+
correctly finds is filtered back out before it reaches the number. On the
|
| 118 |
+
held-out register frames, where such cues do occur, filtered DATE_TIME is 0.4933
|
| 119 |
+
at precision 0.9418.
|
| 120 |
+
|
| 121 |
+
**Nothing from this repo has shipped to devices.** The repo is private and the
|
| 122 |
+
agent's model catalog does not point at it.
|
| 123 |
|
| 124 |
## Use
|
| 125 |
|
|
|
|
| 145 |
**Include `<s>`/`</s>` in the tokenizer call for full-fidelity results** — this
|
| 146 |
is what closed most of the gap between the Python-decode number and the
|
| 147 |
production-engine number above. `AutoTokenizer.__call__` does this by default;
|
| 148 |
+
a manual `encode(..., add_special_tokens=False)` does not.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
|
| 150 |
+
## Provenance
|
| 151 |
|
| 152 |
+
Corpus generated by this project's own generator; no production traffic and no
|
| 153 |
+
scraped text is ever used for training. The generator switch set for this build,
|
| 154 |
+
its seeds and the library versions are recorded with the run, and the
|
| 155 |
+
organisation-name pool (`REAL_ORGS`) is **off** — it was measured in an earlier
|
| 156 |
+
arm and cost more than it bought.
|
| 157 |
|
| 158 |
+
Training stack: `transformers==4.57.1`, `tokenizers==0.22.1`, `torch==2.9.0a0`,
|
| 159 |
+
`faker==37.12.0`. Two epochs.
|
onnx/model_quantized.onnx
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c996fc5ad1118e0571263ee637f541b3d0981bec648e57211ad4676a3dc1ab47
|
| 3 |
+
size 278234411
|