Skip to main content

Розставляє наголоси (U+0301) в українському тексті - char-level BiLSTM + контекстний резолвер омографів

Project description

stress-uk

Розставляє наголоси в українському тексті символом U+0301 (combining acute accent), напр. рукарука́. Гібрид: char-level нейромережа (BiLSTM) для самого слова + rule-based шар для слів, де наголос залежить від ЗНАЧЕННЯ в контексті (омографи, напр. за́мок-фортеця проти замо́к-механізм).

Встановлення

pip install stress-uk

Вага моделі (~9.5MB) НЕ входить у пакет — підвантажується автоматично з HuggingFace Hub при першому використанні й кешується locally (~/.cache/huggingface/), повторні запуски вже offline.

Опційно — few-shot резолвінг гетеронімів через ембединги контексту (додаткові ~47 слів покриття поверх стем-тригерів, див. нижче):

pip install "stress-uk[embeddings]"

Використання

from stress_uk import stressify_text, stressify

print(stressify_text("Привіт, як справи?"))
# Приві́т, я́к спра́ви?

print(stressify("електрифікація"))
# електрифіка́ція

Для ансамблю кількох чекпоінтів або власної моделі:

from stress_uk import Stressifier

s = Stressifier(checkpoint_paths=["шлях/до/мого_чекпоінта.pt"])
print(s.stressify_text("Текст."))

Розстановка наголосу в омографах залежить від слів навколо (±8 слів, без перетину пунктуації, ближчі слова важать більше) — словник сенсів і контекстних тригерів у stress_uk/heteronyms.py можна редагувати/ розширювати напряму, формат самоописний (HETERONYMS: dict[слово, tuple[Sense, ...]]).

Few-shot резолвінг через ембединги (опційно)

Стем-тригери — основний, дешевий механізм (мікросекунди). Якщо для конкретного слова тригери НЕ дають однозначної відповіді (0 чи ≥2 матчі), і встановлено stress-uk[embeddings], і для слова є приклади речень у stress_uk/embeddings.py (SENSE_EXAMPLES) — пробується few-shot класифікація через sentence-transformers: 2-3 приклади-речення на сенс кодуються в ембединги, усереднюються в центроїд, нове входження класифікується за найближчим центроїдом (cosine-відстань). Це НЕ заміна тригерів, а фолбек поверх них.

Покриває зараз 47 слів (не весь словник - лише ті, для яких знайшлися реальні речення в зовнішньому бенчмарку при розробці). Помітно дорожче за тригери (~30мс/речення замість мікросекунд) - тому й опційне, і фолбек, а не основний шлях. Без встановленого extra цей шар просто вимикається (no-op), решта пакета працює як завжди. Розширюється так само, як HETERONYMS - додай "слово": {"наголо́шена_форма": ["речення1", "речення2", ...]} у SENSE_EXAMPLES.

Тренування власної моделі

pip install stress-uk
python -m stress_uk.train --epochs 2          # демо на маленькій вибірці, що йде з пакетом

Формат даних — JSONL, один приклад на рядок:

{"word": "рука", "stress": [0, 0, 0, 1]}

де word — слово малими буквами без наголосу, stress — бінарна маска тієї самої довжини (1 на позиції наголошеної букви). За замовчуванням використовується невелика ілюстративна вибірка з пакета (НЕ повний навчальний набір) — передай --data-dir шлях/до/своїх/даних зі своїми train.jsonl/val.jsonl/vocab.json/word_patterns.json того самого формату (word_patterns.json потрібен лише для лагідної оцінки під час тренування: {слово: {"best": [...], "all": [[...]]}}).

Звичайний прогін ділить дані на train/val і зупиняється сам через early stopping, чекпоінт зберігається в ./models/. Для фінального прод-чекпоінта (тренування на train+val разом, без held-out оцінки) — --full-data --epochs N, де N — номер найкращої епохи звичайного прогону.

Розробка/публікація

Якщо клонував репозиторій (не просто pip install):

cd release
pip install -e .

Інструкції з публікації нової версії моделі на HuggingFace Hub і пакета на PyPI — scripts/PUBLISHING.md.

Вимоги

torch + huggingface_hub (CPU достатньо — інференс на слово займає частки мілісекунди, тренування на CPU теж практично, бо модель невелика). Опційно — sentence-transformers (pip install "stress-uk[embeddings]") для few-shot резолвінгу гетеронімів, див. розділ вище.

Чого немає в цьому репозиторії

Щоб бути чесними з тими, хто захоче розібратися, як це зроблено, чи повторити процес із власними даними:

  • Скрипти-краулери, якими зібрано вихідні лексикографічні дані (зокрема дамп Вікісловника, uk.wiktionary.org, та ще кілька джерел) — не публікую. Вони специфічні під конкретні джерела й писались для одноразового внутрішнього використання, а не як перевикористовуваний інструмент.
  • Алгоритм об'єднання й узгодження кількох датасетів між собою (як саме вирішується, який варіант наголосу "канонічний", коли різні джерела дають різну відповідь на те саме слово) — теж не публікую. Це найбільш ітеративно доопрацьована й специфічна частина роботи (там було знайдено й виправлено кілька нетривіальних багів — див. історію нижче), і вона лишається поза цим репозиторієм.

Публічно — повний код інференсу й тренування на вже підготовлених даних (з прикладом невеликої вибірки того самого формату), словник гетеронімів і сама натренована модель. Цього достатньо, щоб користуватись інструментом і навіть перенавчити модель на власних даних — просто без кроків "звідки взялись вихідні дані" і "як саме їх звести в один узгоджений датасет".

Історія розробки (коротко)

Базова модель — символьний (character-level) bidirectional LSTM: embedding → 2-шаровий BiLSTM → лінійний шар, що для кожної букви слова передбачає "ця буква наголошена чи ні" (multi-label, не softmax — природно підтримує рідкі слова з кількома наголосами, зокрема дефісні композити). Навчена на великому корпусі акцентованих українських словоформ із кількох лексикографічних джерел, об'єднаних і узгоджених між собою (конфліктні випадки — або очевидна друкарська помилка джерела, яку виправляли голосуванням, або справжня граматична омонімія, яку свідомо НЕ форсували до єдиної відповіді, а лишали моделі вчити обидва варіанти з емпіричного розподілу).

Окремо — rule-based шар для омографів (слів, де те саме написання має різний наголос залежно від значення): словник із кількох сотень ручно/LLM-курованих записів, кожен запис — наголошені варіанти + контекстні слова-тригери. Резолвер шукає тригери в ±8 словах навколо цілі (з розривом контексту по будь-якій пунктуації) і зважує кандидатів за відстанню (ближче слово — сильніший доказ), а не просто рахує бінарний "матч/не матч".

Якість систематично перевірялась проти опублікованого незалежного бенчмарку (Senyk et al., "Context-Aware Lexical Stress Prediction and Phonemization for Ukrainian TTS Systems", UNLP 2025) — це дозволило порівняти результат із кількома іншими опублікованими підходами на тому самому тестовому наборі речень, а не лише на власних тестах. Кілька раундів цільової діагностики (які саме слова й чому помиляються на цьому бенчмарку, а не "сліпе" розширення словника) підняли Macro-F1 на омографах із ~34% до ~49%, а також виявили і виправили системний баг у підрахунку голосів між джерелами навчальних даних (рідкісна граматична колізія могла переважити частотою формальних словоформ значно частотніше в реальному вжитку слово). Фінальний результат — найкращий за всіма п'ятьма метриками порівняння серед усіх перевірених систем (включно з гібридними).

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

stress_uk-0.2.3.tar.gz (96.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

stress_uk-0.2.3-py3-none-any.whl (97.3 kB view details)

Uploaded Python 3

File details

Details for the file stress_uk-0.2.3.tar.gz.

File metadata

  • Download URL: stress_uk-0.2.3.tar.gz
  • Upload date:
  • Size: 96.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.3

File hashes

Hashes for stress_uk-0.2.3.tar.gz
Algorithm Hash digest
SHA256 19107f0772fe4892bf16dbdd31f3d6920e7530342a1c34f3b527cbe56801311e
MD5 5b63cc25cc53ade0c4289f9a0416a04c
BLAKE2b-256 ff5347dcfb669deb35b14a5854c8a54f94d28d437908001de12d65971cc07842

See more details on using hashes here.

File details

Details for the file stress_uk-0.2.3-py3-none-any.whl.

File metadata

  • Download URL: stress_uk-0.2.3-py3-none-any.whl
  • Upload date:
  • Size: 97.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.3

File hashes

Hashes for stress_uk-0.2.3-py3-none-any.whl
Algorithm Hash digest
SHA256 6384d93576a9402290bb92c08b29657b6f0da29e7d74ac4953a0444a508d7095
MD5 b0a08dd66894adb9b7906442e30ec4f7
BLAKE2b-256 bf2151cfdda69a24b422b1c44c4e38d8bd9ca0dc395707b7e67199307bce8d07

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page