The tokenization tautology
Traditional address parsers split the input into 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., classify each 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. independently, then try to reassemble the piecesECE (Expected Calibration Error). A metric that measures how well a model's confidence scores align with its actual accuracy. Lower is better. Mailwoman's held-out ECE drops from 0.067 (raw) to 0.0035 (calibrated). into a coherent parseaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates.. This sequence contains a structural circularity: you cannot group 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. correctly without knowing their types, and you cannot type them correctly without knowing their groups. The traditional architecture resolves this with heuristics, exceptions, and solver post-processing. The exception pile grows without bound.
This article explains why the circularity exists, how traditional parsers work around it, and why Mailwoman's staged pipelinestaged pipelineMailwoman's runtime architecture: a sequence of pure-function stages (normalize → query-shape → locale-gate → kind-classifier → phrase-grouper → classifier → decoder) connected by typed handoffs. Each stage is published as its own npm package. inverts the approach.
How traditional parsers tokenize
The standard pipelinestaged pipelineMailwoman's runtime architecture: a sequence of pure-function stages (normalize → query-shape → locale-gate → kind-classifier → phrase-grouper → classifier → decoder) connected by typed handoffs. Each stage is published as its own npm package. (PeliasPeliasAn open-source geocoder, Mailwoman's spiritual predecessor., libpostallibpostalAn open-source C address parser used by Pelias. Mailwoman's rule-based v0 and neural classifier supersede it., Mailwoman v1):
Input string
↓
Tokenize (split on whitespace and punctuation)
↓
Classify each token independently (dictionary lookup, regex, pattern match)
↓
Reconcile overlapping and conflicting classifications (Cartesian solver)
↓
Output: ranked parse solutions
The problem is that Step 2 runs before Step 3 can feed back into it. The classifier sees independent 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.. It does not see boundaries.
Act 1 — Split first, ask questions later
Consider Saint Petersburg, FL. The tokenizertokenizerThe component that converts a raw address string into a sequence of numeric token IDs the model can process. Mailwoman's tokenizer is a SentencePiece unigram model trained specifically on postal addresses. produces:
[Saint] [Petersburg] [,] [FL]
The classifier now sees each 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. independently. "Saint" matches the street_prefix dictionary with high confidence — it IS a common street prefixstreet affixA modifier on a street name indicating type or direction — Street, Avenue, rue, Calle, N, East. Mailwoman tags these as street_prefix / street_suffix, recognized via a morphology FST.. "Petersburg" matches the WOFWOF (Who's On First). An open-source gazetteer of places maintained by Mapzen/whosonfirst. Mailwoman builds a custom SQLite database from WOF GeoJSON repos, extended with postcode data, importance scores, and coincident-role relations. localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy. 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. — over 30 populated places are named Petersburg. "FL" matches the US stateregionThe first-level administrative subdivision of a country — a US state, a French region, a province. The component between country and locality. abbreviation list.
Each individual classification is correct in isolation. The error is in the combination: "Saint Petersburg" is one localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy., not a street prefixstreet affixA modifier on a street name indicating type or direction — Street, Avenue, rue, Calle, N, East. Mailwoman tags these as street_prefix / street_suffix, recognized via a morphology FST. followed by a citylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy. name. But the classifier cannot know this because it classifies 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. before grouping them.
The solver receives:
{street_prefix: "Saint", confidence: 0.9}
{locality: "Petersburg", confidence: 0.8}
{region: "FL", confidence: 1.0}
The solver tries to build a valid address from these piecesECE (Expected Calibration Error). A metric that measures how well a model's confidence scores align with its actual accuracy. Lower is better. Mailwoman's held-out ECE drops from 0.067 (raw) to 0.0035 (calibrated).. It can produce:
street_prefix=Saint, locality=Petersburg, region=FL(wrong — "Saint" is not a directional)locality=Saint Petersburg, region=FL(correct — but requires merging two 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. the classifier already labeled differently)
Option 2 requires the solver to override the classifier's decisions — to say "you labeled 'Saint' as street_prefix, but I'm going to treat it as part of locality." This is what the solver actually does, via a penalty system that allows 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. to be reassigned. But the penalty system was tuned for the US address shapes the developers tested against. Every new address shape that requires a different reassignment pattern needs new penalty tuning.
Act 2 — Classification commits before phrase grouping resolves
The problem compounds with multi-word components:
P'tit St. Denis' Street Café, 75010 Paris
The tokenizertokenizerThe component that converts a raw address string into a sequence of numeric token IDs the model can process. Mailwoman's tokenizer is a SentencePiece unigram model trained specifically on postal addresses. splits on whitespace AND punctuation (commas, apostrophes):
[P] ['tit] [St] [.] [Denis] ['] [Street] [Café] [,] [75010] [Paris]
The classifier now sees eleven 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.. "St." matches street_prefix (abbreviation of "Saint" as a street prefixstreet affixA modifier on a street name indicating type or direction — Street, Avenue, rue, Calle, N, East. Mailwoman tags these as street_prefix / street_suffix, recognized via a morphology FST.). "StreetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels." matches street_suffix. "Paris" matches locality. "75010" matches 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..
The venuevenueA named, non-address place — a business, building, park, or stadium. Mailwoman's free-text point-of-interest component, added as a Tier 2 fine label. name P'tit St. Denis' Street Café has been shredded across seven 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., three of which look like address components. The solver must recognize that the entire prefix is a venuevenueA named, non-address place — a business, building, park, or stadium. Mailwoman's free-text point-of-interest component, added as a Tier 2 fine label., overcoming the classifier's high-confidence assignments of street_prefix to "St." and street_suffix to "StreetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels.."
This is the NY-NY Steakhouse problem generalized: any word that appears in both the venuevenueA named, non-address place — a business, building, park, or stadium. Mailwoman's free-text point-of-interest component, added as a Tier 2 fine label. lexicon and the address lexicon will be misclassified, and the misclassification will be high-confidence because the lexicon hit is real. The 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. "StreetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels." IS a street suffixstreet affixA modifier on a street name indicating type or direction — Street, Avenue, rue, Calle, N, East. Mailwoman tags these as street_prefix / street_suffix, recognized via a morphology FST. in most contexts. The 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. "NY" IS a regionregionThe first-level administrative subdivision of a country — a US state, a French region, a province. The component between country and locality. abbreviation. The parser cannot distinguish "venuevenueA named, non-address place — a business, building, park, or stadium. Mailwoman's free-text point-of-interest component, added as a Tier 2 fine label. uses address words" from "this is an address component" without knowing the overall structure of the input.
Act 3 — Confidence obscures the problem
Traditional parsers report high confidence on dict hits even when the overall parseaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates. is wrong. The street_prefix classifier is correct that "Saint" can be a street prefixstreet affixA modifier on a street name indicating type or direction — Street, Avenue, rue, Calle, N, East. Mailwoman tags these as street_prefix / street_suffix, recognized via a morphology FST.. The locality classifier is correct that "Petersburg" is a localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy. name. Both return confidence > 0.8.
The output:
{street_prefix: "Saint", confidence: 0.9}
{locality: "Petersburg", confidence: 0.85}
{region: "FL", confidence: 1.0}
To a consumer, this looks like a high-quality parseaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates.. Every component has high confidence. The address structure is valid (street prefixstreet affixA modifier on a street name indicating type or direction — Street, Avenue, rue, Calle, N, East. Mailwoman tags these as street_prefix / street_suffix, recognized via a morphology FST. + localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy. + regionregionThe first-level administrative subdivision of a country — a US state, a French region, a province. The component between country and locality.). The fact that "Saint" should be part of the localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy. is invisible in the confidence numbers — each individual classifier did its job correctly.
Aggregating per-component confidence hides combination errors — the parser is confident about each piece and wrong about the whole. The consumer trusts the high scores and routes the package to "StreetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels. address: Saint, CitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy.: Petersburg, FL," which the USPS will reject or misroute.
Why rules can't fix it
Every new address shape that combines 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. differently requires a new rule, a new penalty adjustment, or a new exception in the solver. The rule set grows without bound:
| Address shape | Rule needed | Why the rule is fragile |
|---|---|---|
Saint Petersburg, FL | Multi-word localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy. patterns | What about "St. Petersburg"? "Saint Peter"? "San Juan"? |
NY-NY Steakhouse, Houston TX | VenuevenueA named, non-address place — a business, building, park, or stadium. Mailwoman's free-text point-of-interest component, added as a Tier 2 fine label.-before-address boundary detection | The comma is the only signal — no comma, no boundary |
350 5th Ave, New York, NY 10118 | Number-as-house-number, not 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. | Positional assumption: first number is housenumber. Fails on 10118 350 5th Ave. |
47110 Sainte-Livrade-sur-Lot | 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.-first detection | Pattern match on 5-digit leading 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.. Fails on 90210 Beverly Hills (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. looks like ZIP, but context is different). |
West 26th Street, New York | Directional prefix detection | "West" is a directional, not a localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy.. But "West New York" is a town in NJ. |
Each rule works for the specific case it was written for. Each rule introduces false positives in cases that resemble the target but aren't. The rule set accretes, and the false-positive rate compounds. After enough rules, adding a new one fixes one address and breaks three others.
The Cartesian solver that reconciles these rules is what the operator's "Paris, Texas" talk calls "Sparkling Bogosort" — it enumerates valid permutations of all possible 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. assignments, filters the ones that violate hard constraints, and scores the rest. The name is accurate. For well-constrained inputs (a standard US address with a 5-digit ZIP that matches the stateregionThe first-level administrative subdivision of a country — a US state, a French region, a province. The component between country and locality.), the permutation space is small. For loosely-constrained inputs (an international address with missing components), the permutation space explodes.
Mailwoman's inversion
Mailwoman inverts the traditional order. Instead of:
tokenize → classify → reconcile
It runs:
phrase-group → classify-spans → reconcile
The phrase grouperphrase grouperStage 2.7 of the runtime pipeline: proposes coherent input units (street phrase, locality phrase, postcode, etc.) with structural kind hypotheses. Decouples boundary discovery from type classification so the classifier answers 'what type?' not 'where?' (StagestageOne of the dataflow stages in the runtime pipeline (normalize, locale gate, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone). 2.7) proposes coherent input unitsunitA subdivision of a building — apartment, suite, floor — that refines a street address. Mailwoman's unit component; a designator plus identifier forms a subpremise. before the classifier runs:
Input: "350 5th Ave, New York, NY 10118"
Spans: [350] [5th Ave] [New York] [NY] [10118]
The grouper uses structural cues — punctuation, capitalization, numeric patterns, 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. proximity — not dictionaries. It does not know what the 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. mean. It only knows where the boundaries are.
The neural classifierneural 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.' (StagestageOne of the dataflow stages in the runtime pipeline (normalize, locale gate, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone). 3) then types each proposed 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.. Because the classifier sees the full 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. sequence simultaneously (it's a transformerneural 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.', not a per-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. classifier), it conditions each 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.'s type on the surrounding 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.:
- "5th Ave" next to "350" → likely a streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels. with a house numberhouse numberThe numeric or alphanumeric identifier of a building on a street. Mailwoman's house_number component; its position relative to the street name flips between locales.
- "5th Ave" next to "Brooklyn" → likely a streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels. in a localitylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy.
- "NY" next to "10118" → likely a stateregionThe first-level administrative subdivision of a country — a US state, a French region, a province. The component between country and locality. abbreviation next to a ZIP code
- "NY" followed by "NY Steakhouse" → likely part of a venuevenueA named, non-address place — a business, building, park, or stadium. Mailwoman's free-text point-of-interest component, added as a Tier 2 fine label. name
The classifier can disagree with the grouper's boundaries (it can merge or split 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.) but it starts from a better prior than 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.-level independent classification.
The reconciler (StagestageOne of the dataflow stages in the runtime pipeline (normalize, locale gate, kind classify, phrase group, token classify, sequence correct, reconcile, resolve). Distinct from tier (model vocabulary) and phase (plan milestone). 5) then picks the joint interpretation that maximizes coherencecoherenceThe property of a parse whose resolved places form a consistent geographic hierarchy — the resolved locality really does sit inside the resolved region. — cross-component consistency on top of per-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. correctness. This is where the NY-NY Steakhouse problem gets solved: the reconciler sees that labeling the first "NY" as region and the second "NY" as region produces a joint parseaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates. with two regionsregionThe first-level administrative subdivision of a country — a US state, a French region, a province. The component between country and locality. for different places, which the resolverresolverThe component that converts parsed address components (locality, region, postcode) into coordinates by looking them up in the gazetteer. The resolver ranks candidates by name match, population, and proximity, and returns the best-matching place with its centroid or polygon.'s hierarchy cannot reconcile. The alternative interpretation — the "NY" 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. are part of a venue name — produces a joint-consistent parseaddress parsingThe process of decomposing a free-text postal address string into structured components — house number, street name, locality, region, postcode, and country — so a geocoder can resolve them to coordinates.. The reconciler switches.
What this doesn't solve
The staged pipelinestaged pipelineMailwoman's runtime architecture: a sequence of pure-function stages (normalize → query-shape → locale-gate → kind-classifier → phrase-grouper → classifier → decoder) connected by typed handoffs. Each stage is published as its own npm package. doesn't eliminate ambiguity. It moves the ambiguity to 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.' has the most information:
- The grouper can still propose wrong boundaries. But it's cheap to improve (rule-based, fast iteration) and wrong boundaries are upstream of classification rather than downstream of it.
- The classifier can still mis-type a 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.. But it has the full context of the input, rather than 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. in isolation.
- The reconciler can still pick a wrong joint interpretation. But it has resolverresolverThe component that converts parsed address components (locality, region, postcode) into coordinates by looking them up in the gazetteer. The resolver ranks candidates by name match, population, and proximity, and returns the best-matching place with its centroid or polygon. feedback — the world hierarchy is a constraint the traditional solver never had.
The traditional pipelinestaged pipelineMailwoman's runtime architecture: a sequence of pure-function stages (normalize → query-shape → locale-gate → kind-classifier → phrase-grouper → classifier → decoder) connected by typed handoffs. Each stage is published as its own npm package. has a structural ceiling: context-free decisions that context-sensitive decisions downstream cannot reverse. Mailwoman isn't perfect, but it moves the context-sensitive decisions upstream, where they belong.
See also
- The staged pipeline — the Mailwoman runtime in detail
- The knowledge ladder — the principle behind the decomposition
- How it used to work — the traditional Mailwoman v1 pipelinestaged pipelineMailwoman's runtime architecture: a sequence of pure-function stages (normalize → query-shape → locale-gate → kind-classifier → phrase-grouper → classifier → decoder) connected by typed handoffs. Each stage is published as its own npm package.
- How it works now — the current rule + neural hybrid