Uzbek NER: exact spans for ORG / NAME / GEO (Latin and Cyrillic)

Named entity recognition for Uzbek text, built for brand monitoring. The model finds organizations, person names and locations and returns exact character offsets into the original string.

It works on both Uzbek scripts — Latin (Toshkentda) and Cyrillic (Тошкентда) — and on mixed text, where the two are combined in one message. No transliteration is needed on your side.

Classes ORG (organizations, brands, media), NAME (people), GEO (places)
Scripts Uzbek Latin, Uzbek Cyrillic, mixed
Architecture biaffine span decoder on top of mmBERT-base (308M parameters)
Input length any — long texts are split into overlapping 512-token windows
License Apache-2.0

Quick start

pip install torch transformers safetensors huggingface_hub
import sys
from huggingface_hub import snapshot_download

path = snapshot_download("Slenser0/uzbek-ner-mmbert-span")
sys.path.insert(0, path)
from uzner import UzbekNER

ner = UzbekNER.from_pretrained(path)          # picks CUDA, then Apple MPS, then CPU

ner.predict("Toshkentda Oqtepa Lavash filiali ochildi, Kun.uz xabar berdi.")
[{'label': 'GEO', 'start': 0,  'end': 10, 'text': 'Toshkentda',    'score': 0.997},
 {'label': 'ORG', 'start': 11, 'end': 24, 'text': 'Oqtepa Lavash', 'score': 0.9985},
 {'label': 'ORG', 'start': 42, 'end': 48, 'text': 'Kun.uz',        'score': 0.9994}]

Pass a list of strings to process a batch: ner.predict([text1, text2, ...]) returns one list of entities per text. From the command line: python uzner.py "Toshkentda Oqtepa Lavash filiali ochildi."

Examples — Latin, Cyrillic, mixed

These are real outputs of this model.

Latin script

Samsung Oʻzbekistonga yangi smartfonlarini olib keldi, dedi Aziz Karimov.
  ORG   [ 0, 7)  Samsung
  GEO   [ 8,21)  Oʻzbekistonga
  NAME  [60,72)  Aziz Karimov

Cyrillic script

Тошкентдаги Artel заводида Азиз Каримов янги линияни ишга туширди.
  GEO   [ 0,11)  Тошкентдаги
  ORG   [12,17)  Artel
  NAME  [27,39)  Азиз Каримов

Наманганда Акмалжон Каримов Ўзбекистон темир йўллари компаниясида ишлайди.
  GEO   [ 0,10)  Наманганда
  NAME  [11,27)  Акмалжон Каримов
  ORG   [28,65)  Ўзбекистон темир йўллари компаниясида

Mixed script in one message

Centrum Air Samarqand aeroportida рейсни икки соатга кечиктирди.
  ORG   [ 0,11)  Centrum Air
  GEO   [12,33)  Samarqand aeroportida

Note on span boundaries

The model follows the annotation convention of its training data: an attached Uzbek suffix is part of the entity (Toshkentda, Oʻzbekistonga, Наманганда), while a separately written function word is not (KFC daKFC). Outer quotes, brackets, #, @ and trailing punctuation are excluded; inner apostrophes, dots and hyphens stay (Fargʻona, Kun.uz, Coca-Cola). Entities are flat — no nesting.

If you need the bare name without the suffix, strip it downstream; the offsets always point at the exact substring of your input.

Quality

The metric is strict exact-span micro-F1: an entity counts only if the class and both character boundaries match exactly.

Numbers on a held-out validation set of 1,500 documents (7,698 gold spans). They were measured on a twin model with the same architecture and recipe, trained without the validation split, so they are not inflated by memorization. This released checkpoint additionally saw the validation split during training.

Script Documents F1
Uzbek Latin 951 0.9058
Uzbek Cyrillic 390 0.8803
Mixed 159 0.9184
Class Precision Recall F1
ORG 0.8775 0.8620 0.8697
NAME 0.9265 0.9237 0.9251
GEO 0.9174 0.9029 0.9101
micro 0.9065 0.8950 0.9007

How it works

Instead of tagging tokens one by one (BIO), the model scores whole candidate spans. Every word n-gram of up to 8 words becomes a candidate. Its first and last subword tokens are projected to 256 dimensions each and combined by a biaffine form, plus a learned span-width embedding:

logits_k = [h_start ; 1]ᵀ · U_k · [h_end ; 1] + width_k        k ∈ {O, ORG, NAME, GEO}

Overlapping candidates are resolved greedily by score, and the threshold is 0.35 by default (ner.predict(text, threshold=...)). Span scoring was chosen after an error analysis of a BIO baseline: in 64% of its boundary false positives only the end of the span was off — the model misjudged how many words to include.

Training data: 14,500 annotated Uzbek documents (13,000 train + 1,500 validation). Every Cyrillic document was also added as a Latin transliteration with remapped offsets, 18,732 documents in total. Three epochs, boundary smoothing ε = 0.1, learning rate 3e-5 for the encoder and 1e-3 for the head, seed 42. The dataset itself is not redistributed here.

Speed

uzner.py processed 300 validation documents of mixed length (5 to 9,216 characters) in 4.7 s on an Apple M4 Pro GPU. On an NVIDIA A100 the same model served through HTTP handles about 96 documents per second, with a 20 ms median latency for a single short request.

Limitations

  • Three classes only: products, events, dates and other types are not labeled.
  • Cyrillic text is a little harder than Latin (0.8803 vs 0.9058 F1).
  • Names that never appeared in training are harder than familiar ones.
  • Quality on text sources unlike the training data is not measured.
  • Offsets include attached suffixes by design, see above.

Files

File
model.safetensors weights: encoder and span head
config.json ModernBERT encoder config
span_config.json window size, stride, hidden size
tokenizer.json, tokenizer_config.json mmBERT tokenizer
uzner.py self-contained inference, no other files needed

uzner.py reproduces the original inference code exactly: on 300 validation documents it returned the same spans with the same scores.

License and attribution

Apache License 2.0, see LICENSE and NOTICE. The encoder is fine-tuned from jhu-clsp/mmBERT-base (MIT License, arXiv:2509.06888).


На русском

Распознавание именованных сущностей для узбекского языка: организации, имена людей и места с точными символьными координатами в исходной строке.

Работает и на латинице, и на кириллице, а также на смешанном тексте, где обе письменности встречаются в одном сообщении. Транслитерировать текст заранее не нужно.

import sys
from huggingface_hub import snapshot_download

path = snapshot_download("Slenser0/uzbek-ner-mmbert-span")
sys.path.insert(0, path)
from uzner import UzbekNER

ner = UzbekNER.from_pretrained(path)
ner.predict("Тошкентдаги Artel заводида Азиз Каримов янги линияни ишга туширди.")
# GEO «Тошкентдаги», ORG «Artel», NAME «Азиз Каримов»

Граница сущности. Приклеенный узбекский аффикс входит в сущность (Toshkentda, Наманганда), отдельно написанное служебное слово — нет (KFC daKFC). Так устроена разметка обучающих данных.

Качество — строгий exact-span micro-F1, засчитывается только полное совпадение класса и обеих границ. На отложенной валидации из 1500 документов: micro 0.9007; латиница 0.9058, кириллица 0.8803, смешанный текст 0.9184.

Числа сняты на модели-близнеце того же рецепта, обученной без валидационной выборки. Этот выложенный чекпоинт видел валидацию при обучении.

Лицензия Apache-2.0.

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

Model tree for Slenser0/uzbek-ner-mmbert-span

Finetuned
(143)
this model

Paper for Slenser0/uzbek-ner-mmbert-span