Нормализация текста для TTS: правильное произношение чисел и сокращений | AiManual
AiManual Logo Ai / Manual.
01 Мар 2026 Гайд

Нормализация текста для TTS: как заставить голосового ассистента правильно произносить числа, сокращения и формулы

Практический гайд по нормализации текста для синтеза речи. Узнайте, как заставить TTS правильно произносить числа, сокращения, формулы и омонимы на 2026 год.

Почему ваш голосовой ассистент говорит "двадцать пять" вместо "25"?

Вы запускаете свой голосовой ассистент на Rockchip, даете команду: "Напомни мне о встрече 25.03 в 14:30". А он в ответ выдает нечто вроде "Напомни о встрече двадцать пятая точка ноль три в четырнадцать часов тридцать". Или еще веселее: "Прочитай рецепт: 100 г муки, 2 ст.л. сахара". TTS выдает "сто грамм муки, два стэлэ сахара". Знакомо? Это не проблема модели. Это проблема текста.

Современные TTS-модели в 2026 году, будь то Sonya TTS или Pocket TTS, научились копировать интонации, эмоции, даже диалекты. Но они по-прежнему тупят на уровне символов. Модель видит последовательность символов "25.03" и не знает, что это дата. Для нее это два числа, точка, ноль, три. И она произносит это буквально.

TTS-модель - это не искусственный интеллект в полном смысле. Это сложный статистический преобразователь текста в звук. Она не понимает смысла. Она видит графемы. Ваша задача - превратить "непонятные" графемы в те, которые модель умеет произносить правильно.

Что ломается чаще всего? Три кита проблем TTS

Прежде чем чинить, нужно понять, что именно сломалось. Вот три главных врага натурального звучания.

1. Числа и их контекст

  • Даты: "25.03.2026" должно звучать как "двадцать пятого марта две тысячи двадцать шестого года", а не "двадцать пять точка ноль три точка два ноль два шесть".
  • Время: "14:30" - это "четырнадцать тридцать" или "полтретьего дня"? А "2:00" - "два часа" или "две ноль ноль"?
  • Порядковые номера: "1-й этаж" vs "1 этаж". Одно - "первый этаж", другое - "один этаж". Контекст решает все.
  • Деньги: "100$" - "сто долларов" или "сто знаков доллара"? А "100 руб."?

2. Сокращения, которые нужно склонять

Русский язык жесток к TTS. Аббревиатуры и сокращения живут по своим законам.

  • И.о. директора - произносится как "исполняющий обязанности директора", а не "и точка о точка директора".
  • ст. л. - "столовая ложка", а не "стэ лэ".
  • кг, см, м - обычно произносятся как "килограмм", "сантиметр", "метр". Но в составе фразы "5 кг" - "пять килограммов" (родительный падеж!).
  • вуз, загс, НАТО - некоторые аббревиатуры стали словами и читаются как единое целое. Другие, как "США", произносятся по буквам.

3. Омонимы и формулы

Самое больное место.

  • Что проще: "ключ от замка" или "замок на ключ"? Слова одинаковые, ударение разное. Текст не содержит ударения. TTS может выбрать случайный вариант.
  • Формулы: "H2O" - это "аш два о" или "вода"? "E=mc^2" - как это вообще озвучить? "Э равно эм цэ квадрат"?
  • Символы: "C++" - "си плюс плюс". "#тег" - "хештег тег". "@user" - "собака user" или "at user"?
💡
Проблема не в мощности модели. Даже огромные модели вроде Qwen3 TTS, о которых мы писали в обзоре конвертера аудиокниг, требуют предварительной нормализации текста для идеального результата. Это отдельный, критически важный слой.

Решение: Нормализатор. Это не исправление ошибок, это перевод на язык TTS

Нормализация текста - это процесс преобразования исходного текста в последовательность слов, которые TTS-модель однозначно и правильно произнесет. Вы не исправляете текст. Вы переводите его с "письменного диалекта" на "устный".

Плохая новость: универсального решения нет. Хорошая новость: это инженерная задача, которую можно решить комбинацией правил и, в 2026 году, маленьких моделей классификации контекста.

1 Собираем коллекцию ошибок

Не пытайтесь угадать. Запустите свою TTS на реальных данных. Что у вас будет озвучиваться? Новости, команды, оповещения? Соберите 100-200 примеров и отметьте, где произношение неверное. Это ваш тестовый набор. Без него вы будете чинить то, что не ломалось.

2 Пишем ядро нормализатора на Python

Вам не нужен машинный learning для базовых вещей. Регулярные выражения и словари справятся на 80%.

Как НЕ надо делать: пытаться одной регуляркой найти все числа.

# ПЛОХО: Слишком просто, контекст не учитывается.
import re
text = "Встреча 25.03 в 14:30"
# Найдем все последовательности цифр и точек? Не поможет.

Как надо делать: разбивать на модули по типу сущности.

# Основа нормализатора
class TextNormalizer:
    def __init__(self):
        self.date_pattern = re.compile(r'\b(\d{1,2})[./](\d{1,2})[./]?(\d{2,4})?\b')
        self.time_pattern = re.compile(r'\b(\d{1,2})[:.]?(\d{2})\b')
        self.currency_pattern = re.compile(r'\b(\d+)\s*([$€₽]|руб|usd)\b', re.IGNORECASE)

    def normalize_dates(self, text: str) -> str:
        # Заменяет 25.03.2026 на «двадцать пятого марта две тысячи двадцать шестого года»
        def _replace_date(match):
            day, month, year = match.groups()
            # Здесь должна быть логика склонения и форматирования.
            # Для простоты:
            return f"{day} числа {month} месяца {year if year else 'текущего'} года"
        return self.date_pattern.sub(_replace_date, text)

    def normalize_times(self, text: str) -> str:
        # Заменяет 14:30 на «четырнадцать часов тридцать минут»
        def _replace_time(match):
            hours, minutes = match.groups()
            # Логика для утра/вечера, склонения "час" / "часа" / "часов"
            return f"{hours} часов {minutes} минут"
        return self.time_pattern.sub(_replace_time, text)

    def normalize_currency(self, text: str) -> str:
        # Заменяет 100$ на «сто долларов»
        currency_map = {'$': 'долларов', '€': 'евро', '₽': 'рублей', 'руб': 'рублей', 'usd': 'долларов'}
        def _replace_currency(match):
            amount, curr = match.groups()
            curr_lower = curr.lower()
            currency_name = currency_map.get(curr_lower, curr_lower)
            # Нужно преобразовать число amount в слова (100 → "сто").
            # Используйте библиотеку num2words или свою функцию.
            amount_words = num2words(int(amount), lang='ru')
            return f"{amount_words} {currency_name}"
        return self.currency_pattern.sub(_replace_currency, text)

    def normalize(self, text: str) -> str:
        text = self.normalize_dates(text)
        text = self.normalize_times(text)
        text = self.normalize_currency(text)
        # ... и другие модули
        return text

# Использование
normalizer = TextNormalizer()
result = normalizer.normalize("Купить за 100$ до 25.03 в 14:30")
print(result)  # «Купить за сто долларов до 25 числа 03 месяца текущего года в 14 часов 30 минут»

Для преобразования чисел в слова (num2words) используйте актуальную библиотеку. В 2026 году она должна поддерживать все нужные склонения и валюты. Не пишите свои конвертеры — это грабли, на которые уже наступили.

3 Добавляем контекстные правила для омонимов

Это сложнее. Нужно понять, в каком значении используется слово. Иногда помогает простой анализ соседних слов.

def normalize_homonyms(text: str) -> str:
    # Простейший пример: "ключ" и "замок"
    words = text.split()
    for i, word in enumerate(words):
        if word.lower() == "ключ":
            # Смотрим соседние слова
            next_word = words[i+1].lower() if i+1 < len(words) else ""
            if next_word.startswith("от") or next_word in ["дверной", "металлический"]:
                # Это ключ от чего-то. Нужно как-то указать ударение?
                # В тексте ударение не обозначишь. Меняем формулировку?
                # Для TTS можно использовать символ ударения, если модель его понимает.
                # Например, некоторые TTS понимают разметку типа клЮч.
                pass
            elif "информации" in text or "шифрования" in text:
                # Криптографический ключ
                pass
    return " ".join(words)

Для сложных случаев в 2026 уже можно использовать крошечную языковую модель (например, на основе BERT-подобной архитектуры с 10-50 млн параметров), которая по контексту классифицирует, о чем речь. Но для embedded-устройств типа Rockchip это может быть тяжеловато. Альтернатива — простой классификатор на n-граммах.

4 Интеграция с TTS-пайплайном

Нормализатор должен работать ДО того, как текст попадет в модель. Если вы используете голосового ассистента, вставьте нормализацию после модуля NLU (понимания естественного языка) и перед TTS.

# Упрощенный пайплайн ассистента
def process_command(command: str, tts_engine):
    # 1. NLU: Извлечение намерений и сущностей (уже может распознать даты)
    intent = nlu_module.understand(command)
    
    # 2. Формирование ответа в текстовом виде
    response_text = dialog_manager.generate_response(intent)
    
    # 3. КРИТИЧЕСКИЙ ШАГ: Нормализация текста ответа
    normalized_text = text_normalizer.normalize(response_text)
    
    # 4. Синтез речи
    audio = tts_engine.synthesize(normalized_text)
    
    return audio

Если вы используете AnyTTS для подключения к ChatGPT, нормализатор нужно встроить в клиентскую часть, которая передает текст в TTS-движок.

Где спрятаны грабли? Нюансы, которые сведут вас с ума

Производительность на embedded-устройствах

Если ваш ассистент работает на Rockchip или аналогичном слабом железе, каждое дополнительное правило — это задержка. Регулярные выражения быстры, но сложные контекстные анализаторы — нет. Профилируйте. Кэшируйте результаты для часто встречающихся фраз.

Языковая зависимость

Все, что мы обсуждали, — для русского языка. В английском свои проблемы: "Dr. Smith" vs "driving", "St. John" vs "street". Ваш нормализатор должен знать язык текста. В многоязычных ассистентах это отдельный головняк.

Переобучение на правилах

Вы написали правило, что "ст." перед фамилией — это "святая" или "святой". А потом попадается "ст. лейтенант". Или "ст. 25 УК РФ". Правила должны иметь приоритеты и проверяться в определенном порядке. А лучше — использовать классификатор.

Когда не нужно нормализовать

Бывают случаи, когда буквальное произношение — это правильно. Например, в программировании: "переменная tmp2". Это должно звучать как "тэ эм пэ два", а не "временная два". Ваш нормализатор должен уметь это определять (например, по окружению других терминов кода).

Частые вопросы (FAQ)

Вопрос Короткий ответ Что делать
Можно ли использовать нейросеть для нормализации? Да, но осторожно. Для сложных случаев (омонимы) можно дообучить маленькую модель на размеченных данных. Для чисел и дат — избыточно.
Какие TTS-модели лучше справляются сами? Почти никакие. Модели, обученные на качественно размеченных данных (с явно прописанными произношениями), могут быть лучше. Но универсального решения нет. Смотрите наш тест TTS 2026.
Как протестировать нормализатор? На реальных данных. Соберите корпус текстов, которые будет озвучивать ассистент. Пропустите через нормализатор, затем через TTS. Слушайте. Автоматизировать сложно — нужна человеческая оценка.
Есть готовые библиотеки? Частично. Для русского языка есть Natasha, PyMorphy, но они для морфологического анализа, не для TTS-нормализации. Адаптируйте под свои нужды. Или напишите свой набор правил, как описано выше.

Итог: Нормализация — это не опция, это обязательный слой

Хотите, чтобы ваш DIY-ассистент на Rockchip звучал профессионально? Не надейтесь на одну лишь TTS, даже если это супер-новая LuxTTS. Потратьте 80% времени не на выбор модели, а на подготовку текста для нее.

Начните с простого: даты, время, деньги. Добавьте 20 самых частых сокращений из вашей предметной области. Уже это даст огромный прирост натуральности. А дальше — бесконечная борьба с контекстом и омонимами. Добро пожаловать в мир разработки голосовых интерфейсов.

P.S. Если вы делаете ассистента для узкой области (например, чтение кулинарных рецептов), вам повезло. Всего 50 правил — и он будет звучать идеально. Если для общего общения — готовьтесь к долгой итеративной разработке. Но без этого никак.

Подписаться на канал