Instructions to use mki0809/geosearch-ner with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- GLiNER
How to use mki0809/geosearch-ner with GLiNER:
from gliner import GLiNER model = GLiNER.from_pretrained("mki0809/geosearch-ner") - Notebooks
- Google Colab
- Kaggle
| license: cc-by-4.0 | |
| language: [ru, en, tr, zh] | |
| library_name: gliner | |
| pipeline_tag: token-classification | |
| tags: [gliner, ner, toponym, geonames, multilingual] | |
| base_model: urchade/gliner_multi-v2.1 | |
| # geosearch-ner | |
| Multilingual toponym NER, fine-tuned from [`urchade/gliner_multi-v2.1`](https://huggingface.co/urchade/gliner_multi-v2.1). | |
| Labels: `CITY`, `REGION`, `STATE`, `COUNTRY`. | |
| **Span F1 0.763** (P 0.759 / R 0.767) on 47 held-out queries at threshold 0.4. | |
| This is the first stage of a multilingual toponym search pipeline: | |
| ``` | |
| text | |
| -> GLiNER (this model) | |
| -> char-n-gram BM25 retrieval | |
| -> CatBoost reranker | |
| -> ranked GeoNames places | |
| ``` | |
| ## Serving it: the word-splitter contract | |
| **This checkpoint requires a per-ideograph word splitter, and serving it without | |
| one loses every Chinese span with no error of any kind.** | |
| GLiNER classifies *token spans*, and its default whitespace splitter treats a | |
| run of Han characters as one token — so `莫斯科` inside `莫斯科新闻` is not a | |
| span the model can even express, let alone predict. This model was trained with | |
| one token per Han ideograph (`src/ner/tokenizer.py::CjkAwareSplitter`), which | |
| `gliner_config.json` cannot record: `words_splitter_type` names only GLiNER's | |
| built-in kinds. | |
| So the requirement is recorded in **`ner_meta.json`**, shipped beside the | |
| weights in this repo: | |
| ```json | |
| {"words_splitter": "src.ner.tokenizer.CjkAwareSplitter"} | |
| ``` | |
| The serving code reads that file and refuses to start when the configured | |
| splitter disagrees with it. If you load this model yourself, re-attach an | |
| equivalent splitter — measured effect of getting it wrong: 56/56 Chinese spans | |
| found versus 0. | |
| ## Decision threshold | |
| Served at **0.3**, below GLiNER's own 0.5, and | |
| that is a property of *serving* rather than of the model: retrieval is | |
| recall-hungry, because a city span never extracted can never be retrieved, | |
| while a spurious span only adds a candidate the reranker can demote. The value | |
| is derived from the sweep in `make ner-eval --sweep`, not asserted. | |
| ## Results | |
| ### Overall | |
| | bucket | precision | recall | F1 | gold | predicted | | |
| |---|---|---|---|---|---| | |
| | overall | 0.759 | 0.767 | 0.763 | 86 | 87 | | |
| ### By label | |
| | bucket | precision | recall | F1 | gold | predicted | | |
| |---|---|---|---|---|---| | |
| | CITY | 0.855 | 0.887 | 0.870 | 53 | 55 | | |
| | REGION | 0.800 | 0.267 | 0.400 | 15 | 5 | | |
| | STATE | 0.333 | 0.833 | 0.476 | 6 | 15 | | |
| | COUNTRY | 0.833 | 0.833 | 0.833 | 12 | 12 | | |
| ### By language | |
| | bucket | precision | recall | F1 | gold | predicted | | |
| |---|---|---|---|---|---| | |
| | ru | 0.818 | 0.818 | 0.818 | 22 | 22 | | |
| | en | 0.809 | 0.809 | 0.809 | 21 | 21 | | |
| | tr | 0.708 | 0.708 | 0.708 | 24 | 24 | | |
| | zh | 0.700 | 0.737 | 0.718 | 19 | 20 | | |
| ### Versus the zero-shot baseline | |
| Zero-shot `urchade/gliner_multi-v2.1`, measured with the **same** word splitter (otherwise the Chinese delta would be an artefact of segmentation rather than of fine-tuning): | |
| | bucket | precision | recall | F1 | gold | predicted | | |
| |---|---|---|---|---|---| | |
| | overall | 0.726 | 0.616 | 0.667 | 86 | 73 | | |
| Metrics are micro-averaged over **spans**, not macro over queries: a query | |
| naming three cities feeds three names into retrieval, so each one is a unit of | |
| work the pipeline either gets right or does not. | |
| ## Training | |
| Selected by **validation span F1**, not `eval_loss` — the checkpoint saved is | |
| whichever epoch scored best when called the way the engine calls it | |
| (`predict_entities` on raw text). | |
| | | | | |
| |---|---| | |
| | best epoch | 12 | | |
| | best val span F1 | 0.763 | | |
| | epochs | 15 | | |
| | batch size | 8 | | |
| | learning rate | 5e-06 | | |
| | others LR | 1e-05 | | |
| | weight decay | 0.01 | | |
| | warmup ratio | 0.1 | | |
| | seed | 42 | | |
| | trained at | 2026-08-07T08:49:14+00:00 | | |
| Trained on synthetic queries generated for this project — see | |
| [`mki0809/geosearch-queries`](https://huggingface.co/datasets/mki0809/geosearch-queries). | |
| ## Data licence and attribution | |
| Derived from [GeoNames](https://www.geonames.org/), licensed | |
| [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). | |
| **Modifications made to the source data:** | |
| - filtered to populated places only (`feature_class = 'P'`) | |
| - restricted to RU, US, TR, CN | |
| - restricted to name variants in ru, en, tr, zh | |
| - dropped feature codes PPLH, PPLQ, PPLW, PPLX | |
| - name variants grouped per place and joined into single document strings | |
| The fine-tune inherits any licence conditions of its base model; check | |
| [`urchade/gliner_multi-v2.1`](https://huggingface.co/urchade/gliner_multi-v2.1) | |
| before commercial use. | |