The model that never saw an intersection
We spent a night trying to make our neural address parser less overconfident. The diagnostics showed a different problem: the training data contained no examples of whole categories of address.
This post describes how we optimized the wrong metric and which diagnostics pointed at the right one.
The hypothesis: it's overconfident
Across the v0.6.x training cycle, one pattern kept surfacing: when the model was wrong, it was confidently wrong. On a held-out test set, 86% of its incorrect predictions were made at ≥0.9 confidence, and most of those at a flat 1.00. We reasoned that a model that hedged appropriately would stop overriding good answers with bad high-confidence ones.
The standard tool for that is label smoothing. Instead of training toward a one-hot target (1.0 for the right tag, 0 for the rest), you train toward a softer target (0.9 for the right tag, with the rest spread across the others). This caps how peaked the model's outputs can get. We ran a single-variable experiment (the v0.6.0 recipe plus label_smoothing=0.1, with no other variable changed) and measured.
Label smoothing did what it is designed to do. Overconfidence-on-wrong dropped 86% → 67%, and the mass at 1.00 confidence disappeared, with confidence capped around 0.95. Postcode recall rose slightly.
The metric we ship on, harness pass rate, did not improve. It went from 14.6% to 13.8%. Two tags (house numbers and streets) regressed.
The evidence
A better-calibrated model that performs no better at the task needed explaining. Instead of tuning the smoothing value again, we asked a more direct question: of everything the harness gets wrong, what kind of error is it?
We categorized every failure, and the results changed the direction of the work:
- 55% of the gap was missing labels. The model emitted no tag at all where one belonged, rather than a wrong value or a fuzzy boundary.
- The most-missed tags were
street(×197) andhouse_number(×100). - One cluster stood out: intersections: addresses like
Broadway & W 42nd St. They're 17% of our harness, and the model scored 0% on them.
Calibration softens the confidence of labels the model does emit. It cannot produce a label the model never emits. That is why the harness pass rate stayed flat.
The probe
We ran a single probe on a canonical intersection. For every token in Broadway & W 42nd St, we read off the probability the model assigned to the intersection_a / intersection_b tags.
The maximum, across every token, was ~0.0001.
An uncertain model still puts some probability on the right tag. A maximum of ~0.0001 means the model has no representation of intersections. The labels existed in its output vocabulary, but it had never learned to use them.
We checked the corpus pipeline. It has synthesizers for streets, venues without a street, PO boxes, and house+venue combinations, and no synthesizer that generates intersections. The real-world adapters don't emit them in that form either. The training signal for intersections was approximately zero, so the model never learned the category. A different loss function, calibration method, or larger model cannot recover a category that is absent from the data.
A different coverage gap, a different fix
The one improvement from calibration (a small postcode gain) pointed at a second coverage gap, this one caused by tokenization.
The subword tokenizer splits alphanumeric postcodes (SW1A 1AA, M5V 2T6) into fragments like ["S","##W","##1","##A", "1","##AA"]. A regex recognizes the seven-character shape easily, but the model sees only disconnected pieces. As a result, GB/CA/NL postcodes scored 0%.
This fix needed no training. A deterministic regex repair runs after the model decodes. It detects a postcode-shaped substring and aligns the label span to it. On the postcode harness that single pass fixed 135 cases and regressed zero, taking GB/CA/DE/PT to 100%. The repair is eight lines of pattern matching plus a "longest-match-wins" rule that keeps a US ZIP+4 from matching as a Dutch postcode.
What we learned
We are keeping these lessons:
- Pick a metric that the change you're testing cannot inflate on its own. Per-tag F1 looked fine while the product was stuck. Harness pass rate asks whether the whole address comes out right, and it showed the real state.
- A confidently wrong model and a model missing training data need different fixes. We assumed the first case, and the data showed the second. Calibration addresses overconfidence, and coverage addresses missing data.
- Structural validity is a separate signal. A checker that flags incoherent parses, such as a house number without a street or an orphaned unit, caught a mid-training regression that the headline accuracy number hid.
- A model cannot learn a category that is absent from its training data. The problem that looked most expensive had the cheapest cause: a missing synthesizer.
The fix for intersections is a couple thousand synthetic X & Y St examples, labeled and added to the corpus as a small targeted supplement, plus a retrain. That run is training as we publish this.
We'll report the model's intersection results after that run finishes.
