Skip to main content

sakhaspell — спелчекер якутского языка

sakhaspell.michill.ru — попробовать в браузере

Проверка орфографии для якутского (саха тыла). Два слоя: словарный работает офлайн на CPU, нейросетевой добавляет контекст.

Спелчекера для якутского до этого не было. Есть морфология — apertium-sah и yakutmorph, обе GPL-3 — но hunspell-словаря, расширения для редакторов и нейрокорректора нет; ни Яндекс.Спеллер, ни LanguageTool якутский не поддерживают. Разбор поля — docs/research.md.

Что умеет

$ python -m sakhaspell check "Ого уорэгэ кисиэхэ сана кыагы биэрэр"
1:0   Ого      →  Оҕо      [lexicon]   ещё: Ооо, Оо, Ошо
1:4   уорэгэ   →  үөрэҕэ   [lexicon]   ещё: үөрэнэ, үөрэрэ, үрэҕэ
1:11  кисиэхэ  →  киһиэхэ  [lexicon]   ещё: кимиэхэ, киниэхэ, биһиэхэ
1:19  сана     →  саҥа     [lexicon]   ещё: аана, саха, сара
1:24  кыагы    →  кыаҕы    [lexicon]   ещё: кыаны, кыргы, кыаһы

Три сценария:

  • восстановление ҕ ҥ ө һ ү — текст набран с русской раскладки;
  • постобработка OCR и ASR — машинные искажения (см. оговорку ниже);
  • проверка при наборе — подчёркивание с подсказками.

Результаты

SakhaSpellBench, отложенный test, 800 предложений на задачу. Метрики пословные, как в SAGE.

задача словарь + контекст + тэггер детекция ложные
clean — чистый текст — — — — 1.75%
denorm_full — без раскладки 93.16% 94.40% 96.51% 98.88% 1.92%
denorm_mixed — как пишут 92.59% 93.65% 95.87% 98.97% 2.08%
mixed — опечатки поверх 87.83% 90.45% — 95.76% 1.81%
typo — опечатки 76.26% 81.32% — 87.43% 1.78%
real — ошибки из корпуса 67.36% 70.46% — 65.35% 2.35%

Контекстная модель добавляет 5 пунктов на опечатках и 3 на ошибках из корпуса, тэггер — ещё 2 на деноминализации. Обе не стоят ничего в ложных срабатываниях: они переставляют кандидатов и возвращают буквы, а решение «это ошибка» принимает словарь.

Главная колонка — последняя. Ложные срабатывания на заведомо правильном тексте: 1.75% против 8.96% у apertium-sah (LREC 2022) и 4.89% помеченных токенов у bashspell, где ~46% помеченного ложно. Именно эта метрика решает, оставит пользователь спелчекер включённым или нет.

Считается доля правильных слов, которые система подчеркнула зря, — из них подсказку она предлагает для 1.33%, а остальные помечает без вариантов замены. Подчёркивание пользователь видит в любом случае, поэтому метрика по нему; так же меряют и apertium-sah с bashspell.

Скорость на CPU: проверка слова 0.0016 мс (у bashspell 0.17–0.21 мс), подсказки 51 мс медиана (у bashspell 115–132 мс), 392 слова/с на сплошном тексте. Память 470 МБ. Тэггер — 10.8M параметров, посимвольный F1 0.9919.

Оговорка про ASR

Задача постобработки ASR собрана из настоящих выходов распознавателя проекта STT, и чекер на ней проваливается: F1 1.9%. Причина не в чекере — эталонные расшифровки грязнее, чем выход модели. В 50.9% расхождений не является словом именно эталон (против 13.6% у гипотезы), и 19.1% эталонов — деноминализованные написания (против 0.7%).

Полный аудит 8 417 расшифровок: 46.0% содержат орфографическую ошибку, 6.74% всех слов. Артефакты для починки — data/asr_audit/ref_issues.jsonl и top_bad_words.tsv. Для проекта ASR это значит, что WER завышен: модель штрафуется за слова, которые написала правильно.

Устройство

L0  нормализация      NFC, невидимые символы, чужие кириллические буквы
                      (ѳ→ө, ң→ҥ, ғ→ҕ), латинские гомоглифы внутри слова
L1  словарь           301k словоформ из корпуса + фильтр теней искажения,
                      кандидаты через префиксное дерево со взвешенным
                      расстоянием, CPU, миллисекунды
L2  тэггер            посимвольная разметка: для каждой буквы решает, это «г»
                      или «ҕ». Неавторегрессивный, 10.8M параметров
LM  контекст          биграммы с откатом: выбирают между кандидатами равной
                      цены. 963k биграмм, 6.9 МБ, работают на CPU
R   правила           восемь правил из грамматики: гармония гласных, сочетания
                      гласных, позиционные ограничения, стечения согласных,
                      мягкий знак. Помогают ранжированию, доступны отдельно
from sakhaspell import violations, is_wellformed, RULES

is_wellformed("оҕо")        # True
is_wellformed("сурэ")       # False — после «у» не бывает «э»
violations("уьу")           # [soft_sign@2:ь не после д/н]
list(RULES)                 # все восемь правил по именам

Три решения, которые определили результат:

Первичный акцептор — словоформы, а не FST. У apertium-sah точность 98.52%, но наивное покрытие на газетах 91.04%: FST-акцептор подчёркивает 9% правильного текста. Корпусный лексикон даёт 1.75%.

Фильтр теней искажения. Систематическая ошибка письма без раскладки проникает в лексикон: сана (378 вхождений), киси (21), ого (17) попали в словарь, потому что так пишут во всех источниках сразу, и правило «подтверждено двумя источниками» против этого бессильно. Отдельная проверка находит формы, которые являются искажением гораздо более частой формы, и убирает их. Это дало +27.7 пункта F1 и обрушило долю «невидимых без контекста» ошибок деноминализации с 41.2% до 1.5%.

Контекст решает там, где частота ошибается. Верный вариант исправления уже находился среди кандидатов в 93.8% случаев на опечатках, но первым мы его ставили только в 80.5% — тринадцать пунктов терялись в ранжировании. Классика: сйын → кандидаты ыйын и сайын, побеждает более частотный, хотя бу сайын встречается 951 раз, а бу ыйын — ни разу. Биграммная модель это чинит.

Книжная грамматика добавляет десятые доли. Восемь правил из грамматики — гармония гласных, сочетания гласных, позиционные ограничения, стечения согласных, мягкий знак — проверены на 300 тысячах словоформ и измерены порознь. Вместе они ловят 14.6% ошибок, невидимых для словаря, но помечать по ним слова не окупается: ложные срабатывания растут с 1.79% до 3.53%. Причина — заимствования: правила исконной фонетики они нарушают законно, а отличить их без словаря нельзя. В умолчаниях остались только бесплатные применения — подсказка ранжированию и восстановление вне словаря. Замер целиком — E15 и E17.

Заодно корпус опроверг часть формулировок. Правило «слово не оканчивается на б, г, ҕ, д, һ, ч» верно только для ҕ: на «һ» оканчивается тыһ с 14 753 вхождениями, а на б/г/д/ч — заимствования вроде психолог и куб.

L2 — разметка, а не переписывание. Задача восстановления ҕҥөһү посимвольная по своей природе, и разметка ей точно соответствует: модель физически не может изменить символ, которому предсказала «оставить». Seq2seq переписывает текст целиком и правит то, о чём не просили.

Подробности с цифрами — docs/experiments.md.

Установка

pip install sakhaspell

Словарь на 300 304 словоформы встроен в пакет — скачивать и настраивать нечего. У словарного слоя нет зависимостей вообще, только стандартная библиотека.

sakhaspell check "Ого уорэгэ кисиэхэ"       # показать ошибки
sakhaspell fix --file статья.txt --in-place # исправить на месте
sakhaspell repl                             # интерактивно
cat текст.txt | sakhaspell fix > исправлено.txt

Свои имена и термины, чтобы их больше не подчёркивало:

sakhaspell dict add Ньургуйаана Хаҥалас
sakhaspell dict add "сахалыы -> саха тылынан"   # всегда исправлять
sakhaspell dict list

Из Python:

from sakhaspell import SpellChecker, Lexicon

checker = SpellChecker(Lexicon.load())
checker.correct("Ого уорэгэ кисиэхэ")       # 'Оҕо үөрэҕэ киһиэхэ'

for issue in checker.check("Мин огом онгор"):
    print(issue.token.start, issue.token.text, issue.best)

Позиции правок в исходном тексте — для подсветки в редакторе:

from sakhaspell import Pipeline

p = Pipeline()
for c in p.corrections("Ого уорэгэ кисиэхэ"):
    print(c.start, c.end, c.before, "→", c.after, c.alternatives)

Контекстная модель

Даёт +3.5 пункта F1 на восстановлении ҕҥөһү. Чекпоинт в пакет не входит: обучается за 33 минуты на одной H200 через scripts/train_tagger.py.

pip install "sakhaspell[tagger]"
sakhaspell --tagger runs/tagger_v2 check "текст"

HTTP-сервис

pip install "sakhaspell[server]"
uvicorn sakhaspell.server:app --port 8080

POST /check — проверка текста с позициями, POST /spell — быстрый вердикт по словам для подсветки, POST /suggest — подсказки по требованию. Разнесено намеренно: проверка стоит 0.0016 мс на слово, подсказки — 51 мс.

Сборка с нуля

Данные и обучение — на поде H200 (docs/experiments.md). Корпус — 8.2 млн предложений из 18 источников.

python scripts/prep_sentences.py  --corpus corpus_sah.jsonl --out data/sent --procs 90
python scripts/build_lexicon.py   --sent data/sent --out data/lexicon
python scripts/mine_errors.py     --sent data/sent --lexicon data/lexicon --out data/errors
python scripts/find_shadows.py    --lexicon data/lexicon --out data/lexicon
python scripts/build_bench.py     --lexicon data/lexicon --errors data/errors --out data/bench
python scripts/eval_bench.py      --lexicon data/lexicon --bench data/bench --split dev
python scripts/train_tagger.py    --sent data/sent --out runs/tagger_v2 --steps 30000

Готовый словарь лежит в sakhaspell/data/ сжатым и подхватывается сам; Lexicon.load("свой/каталог") берёт другой.

Сборка и выкладка

Что Когда Куда
test.yml push и PR pytest на 3.10–3.13, плюс macOS и Windows
publish.yml релиз с тегом PyPI через доверенную публикацию
deploy.yml изменения в docs/ GitHub Pages и sakhaspell.michill.ru

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

Страница

docs/ — статическая страница: sakhaspell.michill.ru, зеркало на GitHub Pages. Словарь пережат во фронт-кодированный формат и весит 864 КБ на все 300 тысяч форм, поэтому проверка идёт целиком в браузере и текст никуда не отправляется. Пересборка:

python scripts/build_web_assets.py --lexicon data/lexicon --out docs/data

Что не работает

  • Постобработка ASR. Не потому, что чекер плох: эталонные расшифровки оказались грязнее выхода модели (см. оговорку выше).
  • Постобработка OCR не проверена — нет пар «выход против эталона». Модель ошибок OCR добыта (52 015 пар), но проверить не на чем.
  • Опечатки — 76% F1. Ошибку видим в 86% случаев, но верный вариант ставим первым не всегда: ранжирование учитывает цену правки и частоту, но не контекст.
  • Пунктуация и регистр не восстанавливаются.

Лицензии

Код проекта свой. apertium-sah и yakutmorph — GPL-3, в рантайме не используются: лексикон построен из корпуса, поэтому результат не заражается.

Release files for sakhaspell 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sakhaspell 0.4.0
File Size Uploaded
sakhaspell-0.4.0.tar.gz 14.6 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for sakhaspell 0.4.0
File Interpreter ABI Platform
sakhaspell-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 29.2 MB

Release files / sakhaspell-0.4.0.tar.gz

Download URL sakhaspell-0.4.0.tar.gz
Size 14.6 MB
Tags Source
SHA-256 checksum
How to use checksums
216c1baa192cf0e2385b327ccc747ed274a46a493eea9f6cf27a5417ffff505f
BLAKE2b-256 checksum
How to use checksums
ecd2d30937788489b6b7a51cff565418c32bc8f5eeaf55f6189feb495c3eeed9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 8, 2026.

Transparency log

Release files / sakhaspell-0.4.0-py3-none-any.whl

Download URL sakhaspell-0.4.0-py3-none-any.whl
Size 14.6 MB
Tags Python 3
SHA-256 checksum
How to use checksums
6a282a5a9c3e95d9e7606f41206035de62758e6f1e3d0f0faa27233e592eeb39
BLAKE2b-256 checksum
How to use checksums
0ae8e02f0613659e1d586147411d49a0f147d314b8dea31f00d61e0dd6784315
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 8, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page