Skip to main content

v4.15.0 — v1.9.3a3 anchor-absorption

v4.15.0 ships v1.9.3a3-anchor-absorption (step 80000), the fix for the #723 postcodepostcodeThe country-specific postal code (US ZIP, French code postal, etc.). Mailwoman handles postcode parsing entirely by rule classifier — a regex problem, not an ML one.-anchor-pollution bug, on top of v4.14.0's v1.9.2-multilocale-au.

The bug (#723)

The postcode anchoranchor inferenceA technique where structured knowledge (postcode locations, gazetteer place names) is injected into the model as soft input features — not as deterministic overrides. The model still decides the final labels, but the anchor signal biases it toward correct admin tags. is a soft channel: when a tokentokenOne word or subword in the tokenized input. For the neural classifier, tokens come from SentencePiece (subword units); for the rule classifiers, tokens are whitespace- and punctuation-separated words. looks like a postcodepostcodeThe country-specific postal code (US ZIP, French code postal, etc.). Mailwoman handles postcode parsing entirely by rule classifier — a regex problem, not an ML one., an auxiliary featurefeatureAn input signal a model conditions on. Beyond the raw tokens, Mailwoman feeds soft features — gazetteer-membership channels and the postcode anchor — that inform predictions without overriding them. paints the country posteriorcountry posteriorA country → probability map (derived from postcodes or the coarse-placer) that re-ranks resolver candidates as a soft prior, never a hard filter. (the localelocaleThe combination of language and country an address comes from. en-US and fr-FR are the locales Mailwoman ships weights for. signal) onto that spanspanA contiguous range of characters or tokens in the input string, tagged with an address component type (street, locality, postcode, etc.). Parsed addresses are represented as collections of spans, possibly nested in a tree.. It fired on a leading 5-digit tokentokenOne word or subword in the tokenized input. For the neural classifier, tokens come from SentencePiece (subword units); for the rule classifiers, tokens are whitespace- and punctuation-separated words. even when that tokentokenOne word or subword in the tokenized input. For the neural classifier, tokens come from SentencePiece (subword units); for the rule classifiers, tokens are whitespace- and punctuation-separated words. was a US house number that happens to be a real ZIP — 12345 Main St got 12345 labeled postcode. A decode-time override that relabeled it back to house_number was killed (project-723-leading-house-repair-killed): the discipline is the modelneural classifierThe machine learning model at the core of Mailwoman's parser — a transformer encoder (~30M parameters) trained from scratch to do BIO token classification over addresses. It learns the 'grammar' of address formats; the gazetteer supplies the 'atlas.' learns the disambiguation, not a deterministic patch on top of it.

Root cause — a train/inference WHERE-mismatch

Deeper than "the modelneural classifierThe machine learning model at the core of Mailwoman's parser — a transformer encoder (~30M parameters) trained from scratch to do BIO token classification over addresses. It learns the 'grammar' of address formats; the gazetteer supplies the 'atlas.' needs more examples." TrainingtrainingThe process of adjusting a model's parameters so its predictions match labeled examples, by repeatedly measuring error and nudging the weights to reduce it. Distinct from inference, when the trained model is run on new input. painted the anchor on the gold B/I-postcodepostcodeThe country-specific postal code (US ZIP, French code postal, etc.). Mailwoman handles postcode parsing entirely by rule classifier — a regex problem, not an ML one. spansspanA contiguous range of characters or tokens in the input string, tagged with an address component type (street, locality, postcode, etc.). Parsed addresses are represented as collections of spans, possibly nested in a tree. (tokenizer.realign_anchor_to_pieces). InferenceinferenceRunning the trained model on new input to get predictions, as opposed to training, which produces the model. In Mailwoman that means a small transformer encoder reads an address string and classifies every token — house number, street, locality, region, postcode, and the rest. A Who's On First gazetteer can feed soft location hints into the pass, but the model makes the final call on every label. Where a generative model writes text token by token, Mailwoman's output is a retrieval-augmented token classification: one label per input piece. paints on postcodepostcodeThe country-specific postal code (US ZIP, French code postal, etc.). Mailwoman handles postcode parsing entirely by rule classifier — a regex problem, not an ML one.-shaped spansspanA contiguous range of characters or tokens in the input string, tagged with an address component type (street, locality, postcode, etc.). Parsed addresses are represented as collections of spans, possibly nested in a tree. (neural/postcode-anchor.ts collectMatches). So at trainingtrainingThe process of adjusting a model's parameters so its predictions match labeled examples, by repeatedly measuring error and nudging the weights to reduce it. Distinct from inference, when the trained model is run on new input. time the anchor never fired on a house-number-that-looks-like-a-ZIP — the modelneural classifierThe machine learning model at the core of Mailwoman's parser — a transformer encoder (~30M parameters) trained from scratch to do BIO token classification over addresses. It learns the 'grammar' of address formats; the gazetteer supplies the 'atlas.' was never shown the case it fails on. It could not have learned to override a present anchor from context, because it never saw a present anchor it was supposed to override.

The fix — two coordinated levers

  1. anchor_paint_mode: shaped — a load-time transform in encode_row (realign_anchor_to_pieces_shaped) that paints the anchor at trainingtrainingThe process of adjusting a model's parameters so its predictions match labeled examples, by repeatedly measuring error and nudging the weights to reduce it. Distinct from inference, when the trained model is run on new input. time on the same collect_matches shaped spansspanA contiguous range of characters or tokens in the input string, tagged with an address component type (street, locality, postcode, etc.). Parsed addresses are represented as collections of spans, possibly nested in a tree. inferenceinferenceRunning the trained model on new input to get predictions, as opposed to training, which produces the model. In Mailwoman that means a small transformer encoder reads an address string and classifies every token — house number, street, locality, region, postcode, and the rest. A Who's On First gazetteer can feed soft location hints into the pass, but the model makes the final call on every label. Where a generative model writes text token by token, Mailwoman's output is a retrieval-augmented token classification: one label per input piece. uses. No corpuscorpusThe BIO-labeled training data used to train Mailwoman's neural classifier. Assembled from real sources (OpenAddresses, National Address Database) and synthetic shards (boundary stress, order variants, negative space). Managed by @mailwoman/corpus. rebuild.
  2. The synth-anchor-absorption counter-shardshardA partial output file of the corpus build, written in Parquet format. The training pipeline streams shards row by row. (source-weightparameterA single learned number inside a model — one weight or bias. Mailwoman's encoder has roughly 30 million of them; training is the search for good values. 6.0) carrying the localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy. discriminator: leading-5-digit + trailing postcodepostcodeThe country-specific postal code (US ZIP, French code postal, etc.). Mailwoman handles postcode parsing entirely by rule classifier — a regex problem, not an ML one.house_number; + localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy. (no trailing) → house_number; + no localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy. + stateregionThe first-level administrative subdivision of a country — a US state, a French region, a province. The component between country and locality.postcode (the VT E911E911 (Enhanced 911). County- and state-level emergency-dispatch address-point databases with full component breakdown. A lineage source for the situs and address-point layers used in geocoding. format). The A2 shardshardA partial output file of the corpus build, written in Parquet format. The training pipeline streams shards row by row. lacked the localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy. discriminator and cost house# by flipping leading-5-digit→postcodepostcodeThe country-specific postal code (US ZIP, French code postal, etc.). Mailwoman handles postcode parsing entirely by rule classifier — a regex problem, not an ML one. on localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy.-bearing no-trailing rows; hn-regression-diff.ts diagnosed it as 132/132, and A3 added the discriminator. Converged via the A0→A3 probe ladder.

Result — the #723 fix lands at zero coordinate cost

SLICE-H (leading-5-digit house#, anchor ON): 20 → 100. The bug is fixed.

The coordinate is the metric we ship for, graded non-circularly through oa-resolver-eval (2000 real OpenAddressesOpenAddresses (OA). A global open aggregation of address points collected from many official sources. A primary source of component-supervised training data outside proprietary registries. US points, real government lat/lon vs resolved admin centroid, full ship-config — anchor + gazetteergazetteerA geographical index that maps place names and postcodes to real-world coordinates. Mailwoman uses a custom-built Who's On First (WOF) SQLite database as its gazetteer — the 'atlas' half of the grammar/atlas architecture. + suppress):

metricv192 (v4.14.0)v1.9.3a3 (v4.15.0)Δ
localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy.-match97.7%97.8%+0.1
regionregionThe first-level administrative subdivision of a country — a US state, a French region, a province. The component between country and locality.-match99.9%99.9%flat
coord p503.4 km3.4 kmflat
coord p9010.7 km10.7 kmflat
coord p99275.3 km259.1 km−16 (better)

Per-stateregionThe first-level administrative subdivision of a country — a US state, a French region, a province. The component between country and locality., VT — the E911E911 (Enhanced 911). County- and state-level emergency-dispatch address-point databases with full component breakdown. A lineage source for the situs and address-point layers used in geocoding. leading-postcodepostcodeThe country-specific postal code (US ZIP, French code postal, etc.). Mailwoman handles postcode parsing entirely by rule classifier — a regex problem, not an ML one. format where the entire postcodepostcodeThe country-specific postal code (US ZIP, French code postal, etc.). Mailwoman handles postcode parsing entirely by rule classifier — a regex problem, not an ML one. labelcomponent tagOne of the 33 labels in Mailwoman's address schema — street, locality, region, postcode, house_number, unit, po_box, country, venue, intersection, and others. Each parsed span carries exactly one component tag. trade lives — is flat at the coordinate (localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy. 92.6 = 92.6); SD 96.5 → 97.5. Even where the modelneural classifierThe machine learning model at the core of Mailwoman's parser — a transformer encoder (~30M parameters) trained from scratch to do BIO token classification over addresses. It learns the 'grammar' of address formats; the gazetteer supplies the 'atlas.' now mislabelscomponent tagOne of the 33 labels in Mailwoman's address schema — street, locality, region, postcode, house_number, unit, po_box, country, venue, intersection, and others. Each parsed span carries exactly one component tag. the leading-5-digit-postcodepostcodeThe country-specific postal code (US ZIP, French code postal, etc.). Mailwoman handles postcode parsing entirely by rule classifier — a regex problem, not an ML one. tokentokenOne word or subword in the tokenized input. For the neural classifier, tokens come from SentencePiece (subword units); for the rule classifiers, tokens are whitespace- and punctuation-separated words., the address still resolves to the right place from the other tokenstokenOne word or subword in the tokenized input. For the neural classifier, tokens come from SentencePiece (subword units); for the rule classifiers, tokens are whitespace- and punctuation-separated words., so the lossloss functionA number measuring how wrong the model's predictions are on a batch of examples. Training minimizes it. Mailwoman's loss combines per-token negative log-likelihood with the CRF sequence loss. never reaches the coordinate.

The cost — a coordinate-invisible label trade

The fix trades labelcomponent tagOne of the 33 labels in Mailwoman's address schema — street, locality, region, postcode, house_number, unit, po_box, country, venue, intersection, and others. Each parsed span carries exactly one component tag.-F1 on two postcodepostcodeThe country-specific postal code (US ZIP, French code postal, etc.). Mailwoman handles postcode parsing entirely by rule classifier — a regex problem, not an ML one. tags:

  • us.postcode −2.0 → 95.0 — the rare leading-5-digit-postcodepostcodeThe country-specific postal code (US ZIP, French code postal, etc.). Mailwoman handles postcode parsing entirely by rule classifier — a regex problem, not an ML one. (VT E911E911 (Enhanced 911). County- and state-level emergency-dispatch address-point databases with full component breakdown. A lineage source for the situs and address-point layers used in geocoding.) case
  • fr.postcode −0.2 → 99.3evalevalRunning the model against a held-out golden dataset and computing per-component F1, exact-match, calibration, and resolved-coordinate error. noise

Both are coordinate-invisible (the table above; VT flat).

Gate — a stated revision, not silent drift

The pre-registered v4.4.0-boundary gate floors us.postcode at 97.0 and fr.postcode at 99.5 — bars the #723 fix inherently trades. Quoting the canonical bars verbatim:

v4.4.0-boundary floors: us.postcode 97.0, fr.postcode 99.5, us.locality 62.2, us.street 74.0, us.region 80.1, de.native_locality 83.8, arena.perturb 71.0, …

The revision (scripts/eval/gates/v4.15.0-boundary.json) lowers only those two postcodepostcodeThe country-specific postal code (US ZIP, French code postal, etc.). Mailwoman handles postcode parsing entirely by rule classifier — a regex problem, not an ML one. floors — us.postcode 97.0 → 95.0, fr.postcode 99.5 → 99.3 — with the coordinate justification above. Every other floor is unchanged and passes with margin (us.locality 76.7/62.2, us.street 82.3/74, us.region 88.6/80.1, de.native_locality 89.3/83.8, arena.perturb 78/71). The mask-regression gate (#718) passes: no tag regresses >2pp under the conventions maskconventions maskA decode-time constraint layer keyed by the model's own address-system detection (the exported locale head): tags that are ungrammatical in the detected system are removed from the Viterbi vocabulary, and the system's postcode shape arms a snap-only repair pass. The first slice forbids USPS street-affix decomposition for French. Same knowledge-outside-the-weights property as the gazetteer anchor — add a codex conventions row, no retrain.. int8 ↔ fp32fp32 / fp1632-bit and 16-bit floating-point formats. Mailwoman trains in bf16 (a 16-bit variant) and exports the ONNX model in int8 for size. delta 0.

This is the feedback-verify-before-verdict / #566 / #723 discipline applied: the labelcomponent tagOne of the 33 labels in Mailwoman's address schema — street, locality, region, postcode, house_number, unit, po_box, country, venue, intersection, and others. Each parsed span carries exactly one component tag. gate fails on a postcodepostcodeThe country-specific postal code (US ZIP, French code postal, etc.). Mailwoman handles postcode parsing entirely by rule classifier — a regex problem, not an ML one. floor; the assembled coordinatecoord metricThe primary evaluation metric: distance from the resolved coordinate to the true address point. Measured at percentiles (p50, p90) and as 'within X meters.' Prevents the label-F1 trap where a model scores higher on token labels but geocodes worse. — what we ship for — holds. Grade the coordinate, never labelcomponent tagOne of the 33 labels in Mailwoman's address schema — street, locality, region, postcode, house_number, unit, po_box, country, venue, intersection, and others. Each parsed span carries exactly one component tag.-F1. The revision is operator-approved.

Artifacts

  • Modelneural classifierThe machine learning model at the core of Mailwoman's parser — a transformer encoder (~30M parameters) trained from scratch to do BIO token classification over addresses. It learns the 'grammar' of address formats; the gazetteer supplies the 'atlas.': model-v193a3-step-80000-int8.onnx, int8 md5 4dec4f460a934949580d8e7b43adae7e
  • Coordinate evalevalRunning the model against a held-out golden dataset and computing per-component F1, exact-match, calibration, and resolved-coordinate error.: /tmp/oa-v192.txt vs /tmp/oa-v193a3.txt
  • Gate: scripts/eval/gates/v4.15.0-boundary.json, verdict /tmp/gate-v415/verdict.json
  • TrainingtrainingThe process of adjusting a model's parameters so its predictions match labeled examples, by repeatedly measuring error and nudging the weights to reduce it. Distinct from inference, when the trained model is run on new input.: branch feat/v193-anchor-absorption; recipe corpus-python/src/mailwoman_train/configs/v1.9.3a3-anchor-absorption.yaml