Why Zero False Positives Still Wasn’t Enough: Lessons from 100+ NER Experiments

What 100+ NER experiments taught us about eliminating false positives on a fixed GitHub technical-term evaluation set without regressing existing performance: hard negatives, focused loss, distillation, residual repair, and class-specific logit adjustment.

Why Zero False Positives Still Wasn’t Enough: Lessons from 100+ NER Experiments

Hello, we are the Qualiteg Research Team.

We develop PII-FI, a tool for detecting and masking personally identifiable information and other sensitive data. PII-FI uses named entity recognition (NER) as part of its processing pipeline, and this article focuses on continual learning for NER. At Qualiteg, we work with a powerful NER model trained on a large corpus assembled over several years. For convenience, we refer to it throughout this article as our ‘strong NER model.’ While scanning several GitHub repositories, we found false positives in which short technical terms inside product names and identifiers were classified as organization names or internal terminology.

Adding a few exclusions would have fixed that one screen. In an unfamiliar repository, however, the errors would simply have moved to different technical terms, issue numbers, version numbers, or identifier fragments.

With that context, we addressed these false positives by improving the strong NER model itself through continual learning instead of adding dictionary exceptions. We created more than 100 NER models before selecting the production version. Several intermediate candidates reduced the target false positives to zero, but we rejected every candidate that weakened recall on existing documents or degraded even one other entity type. Our aim is to share what these experiments taught us about evolving a trained model when small false positives continue to surface one after another.

The winning pattern was not one large training run. We mined hard negatives from the current model itself, protected diversity on unseen expressions separately from the weak positive boundaries the existing model was most likely to lose, repaired only the final residual errors with minimal training, and then applied the smallest necessary class-specific adjustment to the logits. That sequence gave us the point where false positives disappeared without sacrificing existing performance—and in fact improved it.

This article does not disclose model names, customer data, or internal training documents. Instead, it presents the reusable training design, equations, failure analysis, and acceptance gates behind the result.

1. Define “pass” mathematically before training

Before improving the model, we fixed the acceptance criteria. A higher aggregate F1 score was not enough. Let \(c\) denote an entity type, \(\mathrm{base}\) the current model, and \(\mathrm{new}\) a candidate. At minimum, a candidate had to satisfy the following conditions in product evaluation:

$$\begin{aligned} \mathrm{TP}_{\mathrm{new}}(c) &\ge \mathrm{TP}_{\mathrm{base}}(c) \\ \mathrm{FP}_{\mathrm{new}}(c) &\le \mathrm{FP}_{\mathrm{base}}(c) \\ \mathrm{FN}_{\mathrm{new}}(c) &\le \mathrm{FN}_{\mathrm{base}}(c) \qquad \forall c \end{aligned}$$

Losing a correct detection in even one entity type meant rejection. We rejected candidates that produced zero false positives on the target technical terms but missed one person name in another domain. It is important not to relax this rule later. The more effort we invest in a candidate, the stronger the temptation to say, “It has improved this much, so we should ship it.” That temptation becomes especially strong after 10 or 20 training runs. Even when one metric has slipped slightly, a dramatic improvement elsewhere makes it very easy to give in to the voice saying, “Can’t we just ship this one?”

We also measured precision, recall, and F1:

$$\begin{aligned} P &= \frac{\mathrm{TP}}{\mathrm{TP}+\mathrm{FP}} \\ R &= \frac{\mathrm{TP}}{\mathrm{TP}+\mathrm{FN}} \\ F_1 &= \frac{2PR}{P+R} \end{aligned}$$

These aggregate metrics are summaries, not acceptance criteria. The decision came from TP, FP, and FN by entity type, together with the exact spans that appeared or disappeared.

Figure 1. The improvement loop from false-positive discovery to production acceptance

Workflow from discovering false positives through mining hard negatives, protecting positive boundaries, localized training and evaluation, class-specific adjustment, and production acceptance
Each failure delta becomes the next small experiment, progressively narrowing the scope of change.

2. Split the data into four roles—and call any evaluation you inspect validation

We began with synthetic technical documents containing no real PII. The important point was not the number of examples but how we separated them.

PartitionPurposeOperating rule
TrainingGradient updates and hard-negative miningUsed directly for training
DevelopmentIntermediate candidate comparisonUsed to select checkpoints, learning rates, and loss weights
Additional validationAdditional checks on unseen expressionsText is not returned to training, but results influence candidate selection
Fixed regressionBroad non-regression checksKept unopened initially; after opening, reused for regression and no longer called a final test

We separated more than whole documents. Vocabulary, identifier components, prefixes and suffixes, and sentence templates were also split. If the same word merely appears in a different template, a model that memorized the word can still pass. Conversely, changing the word while reusing the same identifier components or sentence pattern can exaggerate novelty.

We fixed the item count and hash of every set. If an evaluation changes every time it is regenerated, differences between candidates cannot be attributed to the training change.

The naming matters. Once we inspected the additional-validation results and changed training conditions, that set was no longer an independent final test; it was validation data. The fixed-regression set remained unopened until the first promising candidate, but we then reused it to check multiple candidates for regressions. We therefore do not claim that an untouched final test guaranteed performance. Any set inspected during more than 100 adaptive experiments is described honestly as development or regression data.

3. Mine the model’s own false positives, not hand-picked words

Our first attempt labeled the misclassified technical terms as negatives. Suppressing those words merely moved the false positives to issue numbers, version numbers, or nearby short fragments in the same sentence.

We then ran the current model over synthetic technical documents reserved for the training side. Among positions that generation rules or annotations verified were not named entities, we mined the exact spans that the model nevertheless classified as entities. We did not mine from the development, additional-validation, or fixed-regression text. A hard negative was not a term that a person thought looked difficult; it was a position where the current decision boundary had demonstrably failed.

Let \(z_e\) be the logit for the target entity type, \(z_O\) the logit for the non-entity label O, and \(m\) the required margin. At a negative position, a pairwise margin loss can be written as:

$$\mathcal{L}_{\mathrm{neg}}=\max\!\left(0,\,m+z_e-z_O\right)$$

No gradient is applied when \(z_O\) is already sufficiently higher. Only the misclassified boundary is pushed back by the required margin. This limits the scope of change far more effectively than training the entire document as O.

4. Labeling an entire GitHub document as O damages real entities

One of our largest failures came from training every unannotated token in technical documents as O. Training-set scores rose, and false positives on unseen technical terms fell sharply. In real documents from another domain, however, recall dropped on genuine internal entity names that the model should have detected.

The cause was token imbalance. A document contains a small number of correction targets and a large number of background tokens. Feeding all background tokens into cross-entropy as O pushes even unrelated positions toward O. This project was not class-incremental NER in the strict sense of adding new entity classes sequentially. It nevertheless shares a known structural risk with continual NER: treating unannotated entities as O can destroy existing recognition ability.

If \(M\) is the set of tokens being corrected, the focused cross-entropy loss is:

$$\mathcal{L}_{\mathrm{focused}} =-\frac{1}{|M|}\sum_{t\in M}\log p_{\theta}(y_t\mid x)$$

Tokens outside \(M\) are not treated as ordinary O labels. They are either excluded from the loss or protected by using the old model as a teacher.

Comparing the main families of failed experiments

Direction triedWhat improvedWhy we rejected itLesson carried forward
Negative labels only for selected technical termsLower confidence on the target termsFalse positives moved to numbers and adjacent fragmentsMine every false-positive span produced by the model itself
Train the full technical document as OFewer false positives on unseen technical termsLower recall on existing entitiesApply loss only at correction positions
Update only classifier rows for target typesRestricted the output directionCould not separate contextual differencesA small encoder update was still necessary
LoRA with pseudo-labelingZero false positives on the target setLower precision and recall in another domainUse the teacher model to preserve logits outside corrected positions
Focused loss with distillationBalanced the target set and unseen termsLost a few weak positive spans from the existing productAdd visible windows around weak boundaries to the positive side
Localized residual repairZero target false positives and recovered existing true positivesPrecision in another domain remained slightly below the gateApply the smallest adjustment only to error-heavy entity types

5. Protect two kinds of positives instead of simply making positives stronger

Training only on negatives weakens real entities. Repeating a large volume of positives did not solve the problem either. Giving positive cross-entropy too much weight pulled unseen common words toward entity classes and reduced precision.

We ultimately needed two positive sets with different roles.

Positive setWhat it protectsFailure when omitted
Diverse synthetic positivesGeneralization to unseen expressionsPositive examples in the fixed evaluation are missed
Visible windows around weak boundariesSpans the current product detects only narrowlyA small number of existing true positives are lost

Diverse synthetic positives alone did not protect weak boundaries in the existing product. Weak boundaries alone encouraged memorization of their context without generalizing to unseen expressions. Only when both groups competed with negatives in the same batch did we obtain candidates that preserved unseen positives and existing true positives together.

Figure 2. Balancing negative correction with three kinds of protection

Training design balancing hard-negative correction, positive protection, distillation from the old model, and retention of existing labeled data
While correcting negatives, we separately protect positives, output outside correction positions, and capability on existing domains.

6. Use the old model as a fixed reference, not as ground truth

To protect existing capability, we used the current model as a teacher. Let \(U\) be the set of tokens outside the correction targets, and let \(z_t^T\) and \(z_t^S\) be the teacher and student logits. A context-stabilization loss can be written as:

$$\mathcal{L}_{\mathrm{KD}} =\frac{1}{|U|}\sum_{t\in U}\left\lVert z_t^S-z_t^T\right\rVert_2^2$$

A KL divergence over temperature-scaled softmax outputs is another option. The important point is that freezing existing classifier rows does not by itself protect existing entity types. If LoRA changes the encoder, the representations entering the frozen classifier also change, and so do the final logits. Freezing the classifier and distilling the outputs are separate safeguards.

Conceptually, our full objective combined four terms:

$$\mathcal{L} =\mathcal{L}_{\mathrm{neg}} +\alpha\mathcal{L}_{\mathrm{pos}} +\beta\mathcal{L}_{\mathrm{KD}} +\gamma\mathcal{L}_{\mathrm{preserve}}$$

\(\mathcal{L}_{\mathrm{neg}}\) corrects false-positive boundaries. \(\mathcal{L}_{\mathrm{pos}}\) is supervised loss on diverse synthetic positives and weak positive boundaries. \(\mathcal{L}_{\mathrm{KD}}\) preserves output outside correction positions. \(\mathcal{L}_{\mathrm{preserve}}\) is supervised loss on a fixed labeled retention set from existing domains. Larger coefficients are not automatically safer. Excessive positive protection reintroduces false positives, while excessive negative correction reduces recall. We monitored the scale of each loss as a diagnostic, but ultimately rejected settings that improved only one objective by inspecting FP, FN, and span-level deltas on the development data.

7. Repair only the residual errors instead of retraining at the same scale

After broad training reduced false positives to a handful, we did not repeat another run of the same size. We identified the remaining false-positive spans and the correct spans lost by the new candidate, then performed small repairs using only the context windows visible to the tokenizer. The text returned to repair training was limited to training data and development regression windows regenerated from approved training sources. We did not feed text discovered in the fixed-regression set directly back into training.

Net improvement was not sufficient. We separated gains and losses by entity type. Even when a type had the same net number of true positives, the model might have lost one correct span and gained a different one. Once the lost span was identified, that single positive window could be trained alongside the residual negative.

We compared learning rates, training steps, and positive-loss weights in small increments. We also tracked the confidence and length of each remaining false-positive span. If an error shrank from two characters to one and its margin over O moved toward zero, we treated that as evidence of movement in the right direction and considered another small update. If confidence increased while the count stayed unchanged, we stopped that line of experiments.

Near the final candidate, a difference of only a few training steps or a small change in positive weight separated preservation of unseen positives from the last zero-false-positive result. The lesson from more than 100 experiments was not how to run a massive hyperparameter search. It was how to turn each failure delta into the next small experiment.

8. Move from inexpensive gates to expensive ones

Running full product evaluation for every candidate wastes time. We used a funnel in which only candidates that passed one gate moved to the next.

  1. Target false-positive set
  2. Additional unseen positives separated from training
  3. Basic technical negatives
  4. Large-scale false-positive stress test
  5. Fixed regression data, reused after its first opening
  6. TP, FP, and FN by entity type on product documents
  7. Precision and recall in another domain
  8. The actual runtime contract
  9. The full production acceptance criteria

A candidate with even one error on the target set did not enter expensive evaluation. A candidate below the unseen-positive gate did not enter the large negative stress test. Product evaluation stored not only aggregate F1 but also span-level changes by entity type. Every gate listed here was used for development or regression while selecting the final candidate. Because a failure could influence the next training data or loss design, these gates must be distinguished from an independent final test.

Figure 3. The evaluation funnel for narrowing candidates to production acceptance

Evaluation funnel progressing from target false positives through additional validation, negative stress tests, fixed regression, all product entity types, another domain, the runtime contract, and production criteria
Only candidates that pass inexpensive gates proceed to expensive product evaluation. Every gate is a development or regression gate used for candidate selection.

9. Finish with the smallest class-specific score adjustment—not one global threshold

After training, a promising candidate passed the target technical false-positive and product evaluations, but precision in another domain remained slightly below the acceptance line. Raising the O logit globally improved precision, but it also removed genuine unseen entities across all types.

Instead, we added a negative bias only to the two entity types where false positives were concentrated, slightly lowering the corresponding logits:

$$z'_c=z_c+b_c,\qquad b_c<0$$

This was not an exclusion list for particular words; it applied to every candidate span in the same entity type. Nor was it probability calibration in the usual sense of aligning predicted probability with empirical accuracy. It was a class-specific logit adjustment. We compared bias sizes on the cross-domain development evaluation and selected the smallest value that crossed the acceptance line. We then reran the unseen positives, fixed regression, every product entity type, and the full production criteria. We rejected stronger adjustments because they removed additional genuine entities even when precision increased.

10. Final results and the decisive factors

The production candidate passed the fixed GitHub technical-term evaluation, additional unseen-positive validation, basic negatives, the large false-positive stress test, and fixed regression data. In product evaluation, it gained correct detections, reduced false positives by roughly 400 on the same fixed product-regression data, and reduced false negatives relative to the old model. No entity type—including the major types—regressed in TP, FP, or FN. “Zero false positives” here is an observed result on fixed evaluation sets, not a claim that no false positive can occur in every unseen repository.

MeasureCompared with the old model
False positives on the fixed GitHub technical-term setReduced to zero
True positives in product evaluationIncreased
False positives in product evaluationReduced by roughly 400
False negatives in product evaluationReduced
Non-regression by entity typePassed for every type
Unseen positives and fixed regressionPassed

The success cannot be reduced to LoRA, distillation, or margin loss alone. It came from this combination:

  1. Turn the model’s own false positives into hard negatives.
  2. Do not casually label every unannotated token as O.
  3. Protect diversity on unseen expressions separately from weak existing positive boundaries.
  4. Use the old model as a teacher to stabilize outputs outside correction positions.
  5. Repair only residual errors with minimal additional training.
  6. Apply the smallest adjustment only to entity types where residual errors are concentrated.
  7. Accept a candidate by TP, FP, and FN for every type—not by aggregate F1 alone.

The major improvements to our strong NER system came through more than 100 training runs, and the successful result was formally adopted as a new model artifact.

Summary

  • Have you separated training, development, additional-validation, and fixed-regression roles?
  • Have you separated identifier components and sentence patterns—not only vocabulary?
  • Have you mined the model’s actual false-positive spans?
  • Are you avoiding the assumption that every unannotated token is O?
  • Are you protecting both positive diversity and weak positive boundaries?
  • Are you relying on more than a frozen classifier to protect existing entity types?
  • Are you recording gains and losses for every candidate at span level?
  • Do only candidates that pass inexpensive gates enter expensive product evaluation?
  • Are you preventing aggregate F1 from hiding type-level regressions?
  • Is any class-specific score adjustment limited to the smallest value that crosses the acceptance line?

In continual NER, measuring which capabilities must not move is harder than raising the score on the target data. More than 100 failures should not end as a column of candidate names. Convert the movement of false positives, confidence changes, lost spans, and type-level gains and regressions into the next small experiment. Through this experience, we came to understand that only after designing the process at that level does model improvement become reproducible.

In our earlier article, “Extending a Model’s Domains “Without Breaking It” — Design Notes on XLM-RoBERTa Continual Learning,” we explained how to restrict the part of a model changed by additional training. This article is the sequel: what to do with the data and evaluation when even that restricted training cannot pass in one attempt.

See you next time.


Sources and further reading

Read more