vadimbelsky commited on
Commit
1c1e5be
·
verified ·
1 Parent(s): e98225e

v23: the ORGANIZATION-boundary build — every gate line passes on both seeds

Browse files
Files changed (2) hide show
  1. README.md +91 -74
  2. 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
- ## Verified results
33
-
34
- Build **v15**. Gate run 2026-09-03, scored through the production Rust inference
35
- engine, not a Python decode of the same ONNX. Corpus 147,753 training rows
36
- (128,584 generated + 19,169 augmented) and 5,300 held-out rows, 2 epochs. Every
37
- figure below was measured, none estimated; the gate was re-run on the stored
38
- results before this upload and every check passed.
39
-
40
- | | **v15 (this build)** | previous build on this repo (v13) |
41
- |---|---|---|
42
- | **regex-owned identifiers wrongly touched** (hivetrace ru) | **0.0%** (0/770) | 0.0% (0/770) |
43
- | **known-defect suite** | **21/21** (int8 and fp32) | 21/21 |
44
- | hivetrace ru PII (synthetic, expert-annotated) — F1 / P / R | **0.8656 / 0.8000 / 0.9429** | 0.8036 / 0.6888 / 0.9643 |
45
- | Wojood ar NER — F1 / P / R | **0.5870 / 0.6537 / 0.5327** | 0.4945 / 0.6143 / 0.4137 |
46
- | IAHLT ar — F1 | **0.5444** | 0.4697 |
47
- | TAB en incl. quasi-identifiers — F1 | 0.7009 | 0.7428 |
48
- | production-engine filtered F1, frozen eval file `cf21c01b1759` (n = 3,300) | **0.9348** | 0.8563 |
49
- | … PERSON only / production-masked (PERSON + ADDRESS) | **0.9772 / 0.9864** | 0.8094 / — |
50
- | production-engine filtered F1, register eval `c72af982d47c` (n = 3,300) | **0.8786** | 0.7884 |
51
- | … LOCATION / ADDRESS | **0.927 / 0.942** | 0.568 / 0.793 |
52
- | int8 vs fp32 F1 cost (95% CI) | +0.0086 [+0.0058, +0.0112] | +0.0020 |
53
- | latency p95 @ 512 tok (CPU) | 191.7 ms (measured under load avg 10–14; an upper bound) | 190.8 ms |
54
- | name-origin bias delta | **0.0000** — every origin scores 1.000 | 0.0469 |
55
-
56
- The two "frozen eval" rows are the only cross-build deltas in this table that
57
- compare like with like: the same 3,300 rows, the same scorer, both builds. The
58
- register file stresses LOCATION and ADDRESS in syntactic roles the original file
59
- under-represents; v15 lifted LOCATION there from 0.568 to 0.927.
60
-
61
- **What changed in v15.** Name-origin bias closed to zero (v14's frame work had
62
- left it at 0.0469) by drawing names from the Mimesis pools for every origin
63
- (anglo, arabic, slavic, south_asian, east_asian, african, hispanic). Address
64
- abbreviations (Apt/Ste/Whg, а/я, ص.ب, postcodes, PO boxes) and LOCATION in
65
- syntactic roles are now covered. TAB en moved slightly against this build
66
- (0.7428 → 0.7009); it is reported, not gated.
67
-
68
- Head-to-head vs `onnx-community/multilang-pii-ner-ONNX` (PERSON only, WikiANN
69
- test[:500]): es +0.65 · it +0.62 · fr +0.63 · en +0.61 · ar +0.59 · de +0.56 ·
70
- ru +0.12, with the 95% CI lower bound above the −0.02 floor in every language.
71
- Disclosure: in that comparison ours is scored as fp32 torch and the incumbent as
72
- int8 ONNX; the +0.0086 quantization cost above does not change the conclusion.
73
-
74
- Nothing from this repo has shipped to devices yet: the repo is private and the
75
- agent's model catalog does not point at it. "Previous build" means the previous
76
- commit here, not a fleet.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
  ## Read this before using it
79
 
80
- **Arabic is the weakest language, and the gap is recall.** Arabic F1 sits at
81
- 0.54–0.59 on the external benchmarks against 0.87 for Russian and 0.70 for
82
- English, concentrated in LOCATION (Wojood LOCATION recall 0.334). Do not read
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
- benchmarks above (Wojood, IAHLT, TAB), not the synthetic figures — and note that
88
- hivetrace is itself a synthetic benchmark, expert-annotated but not real text.
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 in the production-filtered number by design, not by
94
- defect.** The production filter keeps a date span only next to an explicit
95
- birth-context cue (`born on`, `DOB:`, …); this eval corpus carries none, so
96
- every date the model correctly finds is filtered back out before it reaches
97
- the number above. It is not gated and should not be read as a date-detection
98
- failure — see `precision.rs`'s date-context rule in the agent's source.
 
 
 
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
- only a hand-rolled encode path that skips special tokens needs to add them back.
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
- Environment: transformers 4.57.1, tokenizers 0.22.1, torch 2.9.0a0, faker 37.12.0.
139
 
140
- ## Licence
 
 
 
 
141
 
142
- Apache-2.0.
 
 
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:af1250c3c3dd69151528d28efeb1222088af4ad8b5dd3513cc4a2451b350d99c
3
- size 278234412
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c996fc5ad1118e0571263ee637f541b3d0981bec648e57211ad4676a3dc1ab47
3
+ size 278234411