stress-uk
Розставляє наголоси в українському тексті символом U+0301 (combining
acute accent), напр. рука → рука́. Гібрид: char-level нейромережа
(BiLSTM) + кеш точних збігів (3.2M словоформ, ~13MB) + 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 + marisa-trie (CPU достатньо — інференс на
слово займає частки мілісекунди, тренування на CPU теж практично, бо
модель невелика). Опційно — sentence-transformers
(pip install "stress-uk[embeddings]") для few-shot резолвінгу
гетеронімів, див. розділ вище.
Чого немає в цьому репозиторії
Щоб бути чесними з тими, хто захоче розібратися, як це зроблено, чи повторити процес із власними даними:
- Скрипти-краулери, якими зібрано вихідні лексикографічні дані з відкритих веб-джерел — не публікую. Вони специфічні під конкретні джерела й писались для одноразового внутрішнього використання, а не як перевикористовуваний інструмент.
- Алгоритм об'єднання й узгодження кількох датасетів між собою (як саме вирішується, який варіант наголосу "канонічний", коли різні джерела дають різну відповідь на те саме слово) — теж не публікую. Це найбільш ітеративно доопрацьована й специфічна частина роботи (там було знайдено й виправлено кілька нетривіальних багів — див. історію нижче), і вона лишається поза цим репозиторієм.
Публічно — повний код інференсу й тренування на вже підготовлених даних (з прикладом невеликої вибірки того самого формату), словник гетеронімів і сама натренована модель. Цього достатньо, щоб користуватись інструментом і навіть перенавчити модель на власних даних — просто без кроків "звідки взялись вихідні дані" і "як саме їх звести в один узгоджений датасет".
Історія розробки (коротко)
Базова модель — символьний (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% до ~53%, а також виявили і виправили системний баг у підрахунку голосів між джерелами навчальних даних (рідкісна граматична колізія могла переважити частотою формальних словоформ значно частотніше в реальному вжитку слово). Фінальний результат — найкращий за всіма п'ятьма метриками порівняння серед усіх перевірених систем (включно з гібридними): 77.44% точності на рівні речень, 96.31% на рівні слів, 99.55% на однозначних словах, 82.03% precision/recall на гетеронімах.
Metadata
Release files for stress-uk 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| stress_uk-1.0.0.tar.gz | 6.6 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| stress_uk-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 13.3 MB
Release files / stress_uk-1.0.0.tar.gz
| Download URL | stress_uk-1.0.0.tar.gz |
|---|---|
| Size | 6.6 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e52bc8133c52e822ad80e204a628bb4cfb9bb056198e5340617c55de4a2bf00c
|
|
BLAKE2b-256 checksum How to use checksums |
d035d7776b10fb1fa8a8becb1b1c4121706b8d551741791725a46bb4b623d47d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.3
|
Release files / stress_uk-1.0.0-py3-none-any.whl
| Download URL | stress_uk-1.0.0-py3-none-any.whl |
|---|---|
| Size | 6.7 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
236c2a814b3eef3a6f321b710da7c7196ffc2d1e54d9a6c7f65d14b71f916163
|
|
BLAKE2b-256 checksum How to use checksums |
9316474ea07578ac7994a5b2ba9d48ef7e569c225e47df7460b6f0fb2b659265
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.3
|