Symphonym v8 — Universal Phonetic Embeddings for Cross-Script Toponym Matching

DOI

Symphonym maps toponyms (place names) from 36 writing systems into a unified 128-dimensional phonetic embedding space, enabling direct cross-script similarity comparison without runtime phonetic conversion or language identification.

"London" / "Лондон" / "伦敦" / "لندن" → [0.12, -0.34, …] (all nearby)

v8 supersedes v7, which remains available unchanged. v8 is a retrain from scratch, not a fine-tune: the vocabulary, the script coverage and the IPA lineage all differ, so v8 weights and v7 vocabularies are not interchangeable (see Migrating from v7).

Intended Use

  • Cross-script toponym matching in geographic databases and gazetteers
  • Phonetic search — retrieve results for a place name entered in any script
  • Historical record linkage — match pre-standardisation spelling variants
  • Multilingual named entity linking in NLP pipelines
  • Digital humanities — reconciling place references across archival sources

The model operates on phonetic similarity, not semantic or orthographic similarity. It is designed as a candidate retrieval component within a larger reconciliation pipeline, where candidates are subsequently filtered by geographic proximity and other constraints.

🛑 A similarity score measures the NAME and nothing else. It carries no geographic term and cannot separate two places that share a name: Newcastle in Australia scores exactly as well as the one in England. Anything auto-accepting a match on this score needs a second, non-name signal.

Quick Start

from inference import SymphonymModel

model = SymphonymModel()   # loads weights from this directory

sim = model.similarity("London", "en", "Лондон", "ru")
print(f"London / Лондон: {sim:.3f}")

embeddings = model.batch_embed([
    ("London",   "en"),
    ("Лондон",   "ru"),
    ("伦敦",     "zh"),
    ("لندن",     "ar"),
    ("ლონდონი",  "ka"),
])

With HuggingFace huggingface_hub

from huggingface_hub import snapshot_download

model_dir = snapshot_download("docuracy/symphonym-v8")

from inference import SymphonymModel
model = SymphonymModel(model_dir=model_dir)

Model Architecture

Symphonym uses a Teacher–Student knowledge distillation framework.

Teacher (PhoneticEncoder) — training only

  • Input: IPA transcriptions via Epitran (+ 102 extensions), Phonikud, CharsiuG2P
  • Representation: PanPhon192 — 24-dim articulatory feature vectors, 8-bin positional pooling → 192-dim fixed-length input
  • Architecture: BiLSTM → Self-Attention → Attention Pooling → 128-dim projection

Student (UniversalEncoder) — deployed model

  • Input: raw Unicode characters + script ID + language ID + length bucket
  • Vocabulary: 114,845 characters, 37 script ids (36 named scripts plus an OTHER catch-all), 2,438 language codes
  • Architecture: Character/Script/Language/Length embeddings → Input projection → BiLSTM → Self-Attention (residual) → Attention Pooling → 128-dim projection → L2 normalisation
  • Parameters: ~8.3M

The length bucket embedding (16 buckets, 8-dim) conditions every character representation on sequence length, mitigating spurious matches between short toponyms and long compound strings.

Three-Phase Training Curriculum

Phase Objective Epochs Notes
1 Teacher: triplet margin loss on PanPhon192 features 50 val_loss 0.0056
2 Student–Teacher distillation: α·MSE + (1−α)·cosine 50 α=0.5, Student–Teacher cosine 0.942
3 Hard negative fine-tuning (triplet, margin=0.3) 30 val_loss 0.02122

Do not select a checkpoint by training loss. Four v8 candidates were trained; the one with the best validation loss at all three phases was the worst of the four on every downstream measure. Phase loss orders candidates within a fixed batch size and does not compare across recipes. Selection criteria and the four candidate scorecards are in models/PROVENANCE.md.

Evaluation

MEHDIE Hebrew–Arabic Historical Benchmark (Sagi et al., 2025)

Independent evaluation on medieval Hebrew and Arabic geographical sources — not in training data. Unweighted mean across the five testsets (137 queries), which is the benchmark's own convention; the string baselines were re-run in the same pass rather than quoted, and reproduce their published values to within 0.1.

Method R@1 R@5 R@10 MRR
PanPhon192 (ablation) 41.1% 48.2% 52.3% 45.0%
Levenshtein + AnyAscii 81.5% 97.6% 99.4% 88.5%
Jaro-Winkler + AnyAscii 78.5% 96.3% 97.8% 86.3%
Symphonym v7 85.2% 97.0% 97.6% 90.8%
Symphonym v8 89.3% 97.0% 98.2% 92.8%

R@1 +4.1 points over v7, MRR +2.0. Note the shape: R@5 is unchanged and R@10 moves +0.6, while R@1 and MRR move substantially. v8 is not finding answers v7 could not find — it is ranking the right answer first more often.

Levenshtein still wins R@10 (99.4 vs 98.2), as it did for v7.

This is a small benchmark. +4.1 points on R@1 is about five or six queries changing rank. The comparison is paired (same queries, both models), which is stronger than independent samples, but it will not carry more weight than that.

Cross-Script Pair Validation (11,723 pairs, 179 script combinations)

Systematically sampled from training data (up to 10 pairs per script-pair bin); this tests embedding retrieval quality over the full 73.5M-toponym index, not generalisation to unseen sources. 11,535 pairs scored; 188 had an endpoint absent from the current index.

Metric v7 v8
Pass rate (≥0.75 cosine) 90.7% 90.8%
Cyrillic–Latin (n=1,306) 0.923 0.937
Arabic–Latin (n=786) 0.898 0.916
Devanagari–Telugu 0.976 0.975
Hiragana–Katakana 0.981 0.993

Strongest pairs at scale: Gujarati–Latin 0.957 (n=228), Georgian–Latin 0.947 (n=210), Armenian–Latin 0.943 (n=236), Devanagari–Latin 0.943 (n=681).

v8 Changes

1. The Chinese training signal was contaminated in v7 — this is a correction

v7's card attributed low CJK–Hiragana similarity to "a genuine phonological mismatch, not a model deficiency". That explanation was wrong. v7's IPA for Chinese Han toponyms was produced under a defective language tag, so the model learned Chinese characters with Japanese readings attached. The similarity structure that resulted looked like a phonological fact and was an artefact of the G2P dispatch.

v8 fixes the tag and retrains. CJK–Hiragana mean similarity falls from 0.437 to 0.320 — and that fall is the correct direction: Chinese Han and Japanese Hiragana genuinely do not sound alike, and v7's higher score was the contamination.

2. Script coverage nearly doubled

20 script ids → 37 (36 named + OTHER); 113,280 → 114,845 characters; 1,944 → 2,438 language codes.

The vocabularies are not additive. v8 is not v7 plus new entries: ids are reassigned, and a v8 checkpoint paired with a v7 vocabulary does not raise an error — it produces plausible garbage. See Migrating from v7.

3. Ranking improved; reach did not

Measured over one fixed 1,053,229-name haystack with 4,843 queries, v7 against v8:

v7 v8
recall@1 0.0593 0.0690
recall@10 0.2893 0.3176
recall@200 0.4792 0.4908
MRR 0.1383 0.1557

Every metric improved, but recall@200 rose only +0.0116 while MRR rose +0.0173. Three independent measurements — this one, MEHDIE, and order-sensitivity bands — agree that v8 orders better rather than reaching further.

Limitations

  • Phonetic similarity only: no geographic coordinates, semantics or entity types. Phonetically similar but geographically unrelated names (Austria/Australia: 0.883) score highly.
  • 🛑 Discrimination fell slightly. Separating true pairs from false ones, AUC 0.9324 (v7) → 0.9270 (v8). v8 retrieves and ranks better while separating marginally worse. If your use is thresholding rather than ranking, measure before assuming v8 is an upgrade for it.
  • More than half the correct answers are beyond rank 200. At recall@200 = 0.4908 on the corpus above, no amount of downstream re-ranking can reach them; that is a property of the embedding space, not of the scoring layer.
  • Training bias: sources over-represent populated places with official names in high-resource languages. Under-represented scripts and mundane places are weaker.
  • Tonal languages: PanPhon encodes segmental articulatory features but not tone.
  • IPA coverage is partial: 54% of training-namespace toponyms received an IPA transcription; the rest contribute through character-level distillation only.

Migrating from v7

🛑 Weights and vocabularies must be replaced together. Pairing v8 weights with v7 vocabularies raises no error and silently produces meaningless embeddings.

char_vocab.json    16a4d41e868fecc16e1b701363e1bda8   114,845 chars
lang_vocab.json    17ce7fe69a836ddd32c9577cdd60f389     2,438 langs
script_vocab.json  18fd9410e1c543429ec8327a29ee3225        37 script ids

Embeddings are not comparable across versions. A v7 query vector scored against a v8 index returns confident nonsense rather than an error. Re-embed the corpus, and if a client generates query vectors, upgrade the client in the same change.

Training Data

Trained on 73.5 million unique toponyms from:

Source License
GeoNames CC BY 4.0
Wikidata CC0
Getty TGN ODC-By 1.0

Repository Contents

model.safetensors               Student (UniversalEncoder) weights
config.json                     Architecture hyperparameters + measured index stats
inference.py                    Self-contained inference module
requirements.txt                Dependencies
PROVENANCE.md                   Checkpoint lineage, candidate selection, vocab md5s
symphonym-v8.onnx               ONNX export (batch 1) for browser/edge inference
symphonym-v8.onnx.provenance.json
vocab/
  char_vocab.json               114,845-character vocabulary
  lang_vocab.json               2,438 language codes
  script_vocab.json             37 script ids
evaluation/                     MEHDIE, cross-script and candidate-arm reports
training_stats/                 IPA coverage, phase{1,2,3} metrics
epitran_extensions/             102 custom CSV G2P files

config.json carries total_toponyms, embedding_coverage, as_of and measured_from, each measured from the live index at publish time rather than remembered — an earlier release shipped a hard-coded 66,924,548 at 100% coverage against a live index of 72,703,777.

Citation

@misc{symphonym2026,
    author        = {Gadd, Stephen},
    title         = {Symphonym: Universal Phonetic Embeddings for Cross-Script Name Matching},
    year          = {2026},
    eprint        = {2601.06932},
    archivePrefix = {arXiv},
    primaryClass  = {cs.CL},
    url           = {https://arxiv.org/abs/2601.06932},
    doi           = {10.48550/arXiv.2601.06932}
}

@dataset{symphonym_v8_zenodo,
    author  = {Gadd, Stephen},
    title   = {Symphonym v8 — Universal Phonetic Embeddings for Cross-Script Toponym Matching},
    year    = {2026},
    doi     = {10.5281/zenodo.22767194},
    url     = {https://doi.org/10.5281/zenodo.22767194}
}

The arXiv preprint describes v7, including the two claims corrected above (the Chinese contamination, and the weight carried by letter order). A revision is in preparation. Cite the Zenodo DOI for v8 specifically.

Downloads last month
-
Safetensors
Model size
8.41M params
Tensor type
F32
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Paper for docuracy/symphonym-v8