Skip to content

Commit 8dfccc2

Browse files
Sid Mohanclaude
andcommitted
Fix incorrect entity/label counts in all docs: 81→44 types, 163→89 BIO labels
The label_schema.py defines 44 entity types (6+6+12+20 across 4 tiers), producing 89 BIO labels (1 O + 44 B- + 44 I-). All documentation incorrectly referenced 81 entity types and 163 BIO labels. Updated counts, transition matrix sizes (89x89 = 7,921 params), and derived calculations across 6 documentation files. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent c311ed8 commit 8dfccc2

6 files changed

Lines changed: 58 additions & 59 deletions

pii-ner-v1/docs/full_training_presentation.html

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ <h3>DataFog PII-NER v1</h3>
124124
<h1>Full Training Pipeline</h1>
125125
<p class="subtitle">From smoke test to 360K examples: data engineering, debugging, and design decisions</p>
126126
<div style="margin-top: 44px; color: #475569; font-size: 1.05em;">
127-
DeBERTa v3-xsmall &middot; CharCNN &middot; Gating Fusion &middot; CRF &nbsp;&middot;&nbsp; 71.1M parameters &nbsp;&middot;&nbsp; 163 BIO tags &nbsp;&middot;&nbsp; 3 datasets
127+
DeBERTa v3-xsmall &middot; CharCNN &middot; Gating Fusion &middot; CRF &nbsp;&middot;&nbsp; 71.1M parameters &nbsp;&middot;&nbsp; 89 BIO tags &nbsp;&middot;&nbsp; 3 datasets
128128
</div>
129129
<div style="margin-top: 20px;">
130130
<span class="tag tag-green">Smoke test: PASSED (F1=0.992)</span>
@@ -145,7 +145,7 @@ <h3 style="color: #60a5fa;"><span class="step-num">1</span> Data Heterogeneity</
145145
</div>
146146
<div class="flow-step">
147147
<h3 style="color: #60a5fa;"><span class="step-num">2</span> Label Normalization</h3>
148-
<p style="font-size: 1em;">100+ source label names must map to 81 canonical entity types. "FIRSTNAME", "FIRST_NAME", "first_name", "PER" &rarr; all become PERSON.</p>
148+
<p style="font-size: 1em;">100+ source label names must map to 44 canonical entity types. "FIRSTNAME", "FIRST_NAME", "first_name", "PER" &rarr; all become PERSON.</p>
149149
</div>
150150
<div class="flow-step">
151151
<h3 style="color: #60a5fa;"><span class="step-num">3</span> Optimizer Lifecycle</h3>
@@ -287,7 +287,7 @@ <h3 style="color: #60a5fa;"><span class="step-num">4</span> Feed to Existing Pip
287287
<!-- ===== Slide 6: Label Normalization ===== -->
288288
<div class="slide" id="s6">
289289
<div>
290-
<h2>Label Normalization: 100+ Names &rarr; 81 Types</h2>
290+
<h2>Label Normalization: 100+ Names &rarr; 44 Types</h2>
291291
<p style="margin-bottom: 20px;">Each dataset uses its own label vocabulary. We maintain per-dataset mapping dictionaries to a canonical set.</p>
292292
<div class="two-col">
293293
<div>
@@ -326,7 +326,7 @@ <h3>Why Not Automatic Fuzzy Matching?</h3>
326326
<div class="slide" id="s7">
327327
<div>
328328
<h2>The 4-Tier PII Taxonomy</h2>
329-
<p style="margin-bottom: 20px;">81 entity types organized by sensitivity. Each tier has a different recall target &mdash; miss tolerance decreases with severity.</p>
329+
<p style="margin-bottom: 20px;">44 entity types organized by sensitivity. Each tier has a different recall target &mdash; miss tolerance decreases with severity.</p>
330330
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
331331
<div class="flow-step" style="border-left: 4px solid #ef4444;">
332332
<h3 style="color: #ef4444;">Tier 1: Critical &mdash; Target &ge; 0.98 Recall</h3>
@@ -458,7 +458,7 @@ <h3 style="color: #22c55e;">After (Fixed)</h3>
458458
<div class="slide" id="s10">
459459
<div>
460460
<h2>Mixed Precision + CRF: A Delicate Balance</h2>
461-
<p style="margin-bottom: 20px;">The CRF head computes log-sum-exp over 163&times;163 transition matrices. This is numerically unstable in half precision.</p>
461+
<p style="margin-bottom: 20px;">The CRF head computes log-sum-exp over 89&times;89 transition matrices. This is numerically unstable in half precision.</p>
462462
<div class="two-col">
463463
<div>
464464
<h3>The Three-Layer Defense</h3>
@@ -489,7 +489,7 @@ <h3>Precision Comparison</h3>
489489
<tr><td><span class="yellow">FP16</span></td><td>5 bits</td><td>10 bits</td><td>~6.5&times;10<sup>4</sup></td><td>Yes</td></tr>
490490
</table>
491491
<div class="callout callout-yellow" style="margin-top: 16px;">
492-
<span class="yellow">FP16 CRF failure mode:</span> Transition scores for 163 labels easily exceed FP16 range (65504) in the log-sum-exp computation, producing <code>inf</code> &rarr; <code>NaN</code> loss.
492+
<span class="yellow">FP16 CRF failure mode:</span> Transition scores for 89 labels can exceed FP16 range (65504) in the log-sum-exp computation, producing <code>inf</code> &rarr; <code>NaN</code> loss.
493493
</div>
494494
<h3 style="margin-top: 20px;">GPU Auto-Detection</h3>
495495
<div class="code-block" style="font-size: 0.82em; margin: 6px 0;">gpu_mem = torch.cuda.get_device_properties(0).total_memory
@@ -527,7 +527,7 @@ <h3>Hyperparameters</h3>
527527
<div>
528528
<h3>Why These Choices</h3>
529529
<ul>
530-
<li><strong>50x LR ratio:</strong> Validated in smoke test &mdash; DeBERTa representations must be preserved while CRF transitions (163&times;163 matrix) need aggressive learning</li>
530+
<li><strong>50x LR ratio:</strong> Validated in smoke test &mdash; DeBERTa representations must be preserved while CRF transitions (89&times;89 matrix) need aggressive learning</li>
531531
<li><strong>10 epochs:</strong> With 360K examples and effective batch 32, each epoch is ~11,250 steps. 10 epochs = ~112K steps &mdash; well within the typical fine-tuning budget</li>
532532
<li><strong>256 seq len:</strong> Covers 95%+ of PII-containing text. Longer sequences have diminishing returns and quadratic attention cost</li>
533533
<li><strong>Linear schedule:</strong> Standard for NER fine-tuning. Cosine provides marginal benefit for <20 epoch runs</li>
@@ -694,7 +694,7 @@ <h2>Architecture Decision Record</h2>
694694
<tr>
695695
<td>Training data</td>
696696
<td>3-dataset mix</td>
697-
<td>AI4Privacy: volume. Nemotron: synthetic diversity. Gretel: financial PII coverage. Combined: broadest coverage for 81 entity types.</td>
697+
<td>AI4Privacy: volume. Nemotron: synthetic diversity. Gretel: financial PII coverage. Combined: broadest coverage for 44 entity types.</td>
698698
<td>Single dataset (gaps in coverage), 5+ datasets (diminishing returns, more label noise)</td>
699699
</tr>
700700
</table>
@@ -754,7 +754,7 @@ <h2>FAQ: Optimizer &amp; Training</h2>
754754
</div>
755755
<div class="callout callout-blue">
756756
<strong style="color: #60a5fa;">Q: Why 50x LR ratio specifically?</strong><br>
757-
Derived empirically from the smoke test. Standard NER fine-tuning uses 2e-5&ndash;5e-5 for the backbone. The CRF head starts from random initialization with a 163&times;163 transition matrix &mdash; it needs aggressive learning. 1e-3 is a standard "train from scratch" LR. The 50x ratio ensures the backbone is gently tuned while the head learns fast.
757+
Derived empirically from the smoke test. Standard NER fine-tuning uses 2e-5&ndash;5e-5 for the backbone. The CRF head starts from random initialization with an 89&times;89 transition matrix &mdash; it needs aggressive learning. 1e-3 is a standard "train from scratch" LR. The 50x ratio ensures the backbone is gently tuned while the head learns fast.
758758
</div>
759759
</div>
760760
<div>
@@ -800,8 +800,8 @@ <h2>FAQ: Model Architecture</h2>
800800
Constrained decoding (e.g., masking invalid transitions at inference) prevents illegal sequences but doesn't learn transition preferences. The CRF learns that <code>B-PERSON &rarr; I-PERSON</code> is much more likely than <code>B-PERSON &rarr; I-EMAIL</code>. This soft constraint improves boundary detection even when emission scores are ambiguous.
801801
</div>
802802
<div class="callout callout-blue" style="margin-bottom: 16px;">
803-
<strong style="color: #60a5fa;">Q: 163 BIO tags seems like a lot. Is the CRF transition matrix too large?</strong><br>
804-
The transition matrix is 163&times;163 = 26,569 parameters. This is tiny compared to the model's 71M total. Many transitions are effectively zero (B-SSN &rarr; I-PERSON never happens), so the matrix is very sparse in practice. The Viterbi decode is O(T&times;K&sup2;) where T=seq_len, K=163 &mdash; still fast at ~2ms per sequence.
803+
<strong style="color: #60a5fa;">Q: Is the CRF transition matrix large enough to matter?</strong><br>
804+
The transition matrix is 89&times;89 = 7,921 parameters. This is tiny compared to the model's 71M total. Many transitions are effectively zero (B-SSN &rarr; I-PERSON never happens), so the matrix is very sparse in practice. The Viterbi decode is O(T&times;K&sup2;) where T=seq_len, K=89 &mdash; still fast at ~2ms per sequence.
805805
</div>
806806
<div class="callout callout-blue">
807807
<strong style="color: #60a5fa;">Q: What's the <code>prediction_step</code> override doing?</strong><br>
@@ -823,7 +823,7 @@ <h2>Competitive Landscape</h2>
823823
<td><strong style="color: #60a5fa;">DataFog PII-NER v1</strong></td>
824824
<td>DeBERTa + CharCNN + CRF</td>
825825
<td>~2ms/seq (ONNX target)</td>
826-
<td>81</td>
826+
<td>44</td>
827827
<td>Full fine-tuning</td>
828828
<td>Open source, ONNX-exportable, tier-based recall targets</td>
829829
</tr>
@@ -861,7 +861,7 @@ <h2>Competitive Landscape</h2>
861861
</tr>
862862
</table>
863863
<div class="callout callout-green" style="margin-top: 16px;">
864-
<strong>Our differentiator:</strong> Widest open-source PII entity coverage (81 types), CRF-enforced boundary consistency, CharCNN for format pattern detection, and ONNX-exportable for sub-5ms inference. The combination of breadth + speed + open source doesn't exist yet.
864+
<strong>Our differentiator:</strong> Widest open-source PII entity coverage (44 types), CRF-enforced boundary consistency, CharCNN for format pattern detection, and ONNX-exportable for sub-5ms inference. The combination of breadth + speed + open source doesn't exist yet.
865865
</div>
866866
</div>
867867
</div>

pii-ner-v1/docs/model_comparison.html

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ <h3>Sections</h3>
170170
<div class="section-title" id="tldr">0. The 30-Second Version</div>
171171

172172
<div class="a">
173-
<p><span class="model-tag tag-spacy">SpaCy</span> is fast and cheap but can only find about 7 out of 81 PII types. It needs regex bolted on for everything else. Good baseline, not a real PII solution on its own.</p>
173+
<p><span class="model-tag tag-spacy">SpaCy</span> is fast and cheap but can only find about 7 out of 44 PII types. It needs regex bolted on for everything else. Good baseline, not a real PII solution on its own.</p>
174174
<p><span class="model-tag tag-gliner">GLiNER 2</span> is flexible and can find any entity type you describe, without retraining. But it's 9x larger, 5-8x slower, degrades past 30 entity types, and has no character-level features for structured PII like SSNs.</p>
175175
<p><span class="model-tag tag-datafog">DataFog v1</span> is purpose-built for PII. Smaller, faster, with character-level pattern detection and CRF sequence constraints. But it's unproven &mdash; the smoke test passed, but full training hasn't happened yet.</p>
176176
<div class="callout">
@@ -204,7 +204,7 @@ <h3>Sections</h3>
204204
<div class="q">What is DataFog PII-NER v1?</div>
205205
<div class="a">
206206
<p>Our model. DeBERTa-v3-xsmall (70.7M params, 384-dim) combined with a Character CNN (catches patterns like XXX-XX-XXXX), a gating fusion layer (dynamically blends character and context signals per token), and a CRF head (enforces valid BIO tag sequences).</p>
207-
<p>It's designed from the ground up for one task: finding PII in English text. Fixed taxonomy of 81 entity types, 4 severity tiers, trained on 360K PII-specific examples. Target: ONNX INT8, ~25 MB, runs on CPU at ~10ms per 128 tokens.</p>
207+
<p>It's designed from the ground up for one task: finding PII in English text. Fixed taxonomy of 44 entity types, 4 severity tiers, trained on 360K PII-specific examples. Target: ONNX INT8, ~25 MB, runs on CPU at ~10ms per 128 tokens.</p>
208208
<div class="callout warn">
209209
<strong>Important caveat:</strong> The smoke test passed (F1 0.992 on 100 memorized examples). Full training on 360K examples hasn't happened yet. The performance targets below are projections based on architecture design and literature, not measured results. SpaCy and GLiNER numbers are real. Ours are a bet.
210210
</div>
@@ -244,7 +244,7 @@ <h3>Sections</h3>
244244
<td><strong>PII types (native)</strong></td>
245245
<td class="orange">~7 (PERSON, ORG, GPE, LOC, DATE, TIME, MONEY)</td>
246246
<td class="green">Any (zero-shot, user-specified)</td>
247-
<td class="green">81 fixed types across 4 tiers</td>
247+
<td class="green">44 fixed types across 4 tiers</td>
248248
</tr>
249249
<tr>
250250
<td><strong>Structured PII<br>(SSN, CC, email)</strong></td>
@@ -524,7 +524,7 @@ <h3>Sections</h3>
524524
<tr style="background: #172554;">
525525
<td><span class="model-tag tag-datafog">DataFog v1 (planned)</span></td>
526526
<td class="dim"><strong>TBD</strong></td>
527-
<td>~359K combined: AI4Privacy (209K) + Nemotron (100K) + Gretel (50K), 81 types</td>
527+
<td>~359K combined: AI4Privacy (209K) + Nemotron (100K) + Gretel (50K), 44 types</td>
528528
<td>Held-out splits + cross-dataset eval</td>
529529
<td class="green"><strong>Both</strong> &mdash; in-dist + cross-dataset planned</td>
530530
</tr>
@@ -552,7 +552,7 @@ <h3>Sections</h3>
552552
<div class="q">What about that "custom transformer" that got 0.95 F1 in the arxiv benchmark?</div>
553553
<div class="a">
554554
<p>That number is real but misleading for our purposes. It was measured on <strong>CoNLL-2003</strong>, which has only 4 entity types: PERSON, LOCATION, ORGANIZATION, and MISC. The training data is ~14K sentences of Reuters newswire &mdash; real text, not synthetic.</p>
555-
<p>Detecting 4 entity types in clean newswire text is a fundamentally easier task than detecting 46-81 PII types in diverse synthetic or real-world documents. A 0.95 on CoNLL-2003 doesn't tell us anything about how that model would perform on SSNs, credit cards, or medical record numbers &mdash; because those entities don't exist in CoNLL-2003.</p>
555+
<p>Detecting 4 entity types in clean newswire text is a fundamentally easier task than detecting 44-55 PII types in diverse synthetic or real-world documents. A 0.95 on CoNLL-2003 doesn't tell us anything about how that model would perform on SSNs, credit cards, or medical record numbers &mdash; because those entities don't exist in CoNLL-2003.</p>
556556
<p>The useful takeaway: fine-tuned transformers <em>with CRF</em> consistently outperform other approaches on NER. The CRF in that same benchmark scored 0.93, nearly matching the custom transformer. Our architecture includes both.</p>
557557
</div>
558558

@@ -592,7 +592,7 @@ <h3>Sections</h3>
592592
<td><strong>Combined</strong></td>
593593
<td><strong>~359K</strong></td>
594594
<td><strong>3 different generators</strong></td>
595-
<td><strong>~81 (after normalization)</strong></td>
595+
<td><strong>~44 (after normalization)</strong></td>
596596
<td><strong>Hundreds of domains</strong></td>
597597
</tr>
598598
</table>
@@ -696,7 +696,7 @@ <h4 style="color: #3b82f6;">DataFog v1 (target)</h4>
696696
<p>Three things:</p>
697697
<p><strong>1. See character patterns.</strong> GLiNER and SpaCy operate on subword tokens. They never see the raw characters. Our CharCNN looks at the actual characters of each token and can learn that "three digits, dash, two digits, dash, four digits" is an SSN pattern. GLiNER sees <code>["_555", "-12", "-34", "56"]</code> and has to learn the pattern indirectly from subword token co-occurrence. This is why NVIDIA's GLiNER-PII gets only 0.64 F1 on AI4Privacy despite being 460M parameters.</p>
698698
<p><strong>2. Enforce sequence validity.</strong> Our CRF head physically cannot output <code>I-EMAIL</code> after <code>B-PERSON</code>. The transition matrix makes that transition score negative infinity. GLiNER uses span matching with greedy decoding &mdash; it can produce contradictory extractions (overlapping spans with different types) and has no mechanism to enforce sequential BIO constraints.</p>
699-
<p><strong>3. Run without entity type tokens in the input.</strong> GLiNER prepends entity type names as tokens, which means: (a) the effective sequence gets longer, (b) with 50+ types, you're adding 50+ tokens to every input, and (c) performance degrades beyond ~30 types because the entity type tokens compete with the actual text for attention. Our model always predicts over all 81 types simultaneously with zero additional input overhead.</p>
699+
<p><strong>3. Run without entity type tokens in the input.</strong> GLiNER prepends entity type names as tokens, which means: (a) the effective sequence gets longer, (b) with 50+ types, you're adding 50+ tokens to every input, and (c) performance degrades beyond ~30 types because the entity type tokens compete with the actual text for attention. Our model always predicts over all 44 types simultaneously with zero additional input overhead.</p>
700700
</div>
701701

702702
<div class="q">What can GLiNER do that our model fundamentally can't?</div>

0 commit comments

Comments
 (0)