Современный ASR: CTC, seq2seq и attention
Разбираем две главные идеи нейросетевого ASR — CTC и seq2seq с attention.
End-to-end ASR — одна нейросеть, которая превращает звук прямо в текст, без отдельных словарей произношений и ручных компонентов.
Главная боль ASR — выравнивание: на входе тысячи кадров спектрограммы, на выходе десятки символов, и мы не знаем, какой кадр какому символу соответствует. Классика решала это через HMM. Нейросети предложили два изящных способа обойтись без ручного выравнивания.
Проблема выравнивания
Слово «привет» в звуке может занимать 50 кадров, причём «и» тянется 12 кадров, а «т» — 3. Сеть выдаёт предсказание на каждый кадр, но как из 50 предсказаний получить 6 букв? Нужен механизм, который сам разберётся с растяжением.
кадры: п п п р и и и и в в е е т (что выдала сеть по кадрам)
|
v схлопывание повторов и пустот
текст: п р и в е т
CTC: схлопывание повторов
CTC (Connectionist Temporal Classification) — функция потерь, которая разрешает сети предсказывать букву на каждом кадре плюс специальный «пустой» символ. Затем повторы и пустоты схлопываются: «ппп-ри-иии-вет» → «привет». Сеть сама учится, где какая буква, без ручной разметки кадров. CTC лежит в основе wav2vec2 и многих потоковых ASR — он быстр и работает в реальном времени.
seq2seq с attention
Второй подход — энкодер-декодер с attention, как в машинном переводе. Энкодер сжимает всю спектрограмму в представление, декодер порождает текст по одному токену, на каждом шаге через attention «смотрит» на нужные части звука. Это мощнее CTC (декодер учитывает уже сгенерированный текст, как языковая модель), но требует видеть всю запись — хуже для потокового распознавания. Именно так устроен Whisper.
| CTC | seq2seq + attention | |
| Потоковость | хорошо (онлайн) | хуже (нужна вся запись) |
| Учёт контекста | слабый | сильный (как ЯМ) |
| Пример | wav2vec2 | Whisper |
Имитация схлопывания CTC
Реализуем декодирование CTC: убираем повторы подряд и пустой символ «_».
raw = "ппп_рр_иии_в_еее_т"
def ctc_decode(seq):
out = []
prev = None
for ch in seq:
if ch != prev: # схлопываем повторы
if ch != "_": # выкидываем пустой символ
out.append(ch)
prev = ch
return "".join(out)
print("Сырой выход сети:", raw)
print("После CTC-декода:", ctc_decode(raw))Вывод:
Сырой выход сети: ппп_рр_иии_в_еее_т После CTC-декода: привет
Простое правило «схлопни повторы, выкинь пустоту» превращает покадровый выход в нормальный текст. Вот почему CTC так популярен — декодирование почти бесплатно.
Чтобы прочувствовать выравнивание, представьте, что вам дали растянутую во времени фотографию слова и попросили сказать, где кончается одна буква и начинается следующая, не зная заранее ни длины букв, ни их числа. Человек растеряется, а классике для этого нужна была размеченная по кадрам обучающая выборка — дорогая и редкая. Гениальность CTC в том, что он позволяет учить сеть вообще без разметки кадров: достаточно пары «звук + итоговый текст», а все возможные варианты выравнивания CTC честно суммирует внутри функции потерь. Это и сняло главный барьер на пути end-to-end ASR.
У механизма attention есть приятный побочный эффект: его веса можно нарисовать как картинку соответствия «какой кусок звука породил какой символ». Получается своеобразная карта выравнивания, которую модель построила сама, никто её этому явно не учил. По такой карте инженеры диагностируют сбои: если attention «прыгает» хаотично, модель, скорее всего, начнёт повторять слова или зацикливаться. Это наглядно показывает разницу подходов: CTC выравнивает жёстко и монотонно слева направо, а seq2seq с attention волен смотреть куда угодно — отсюда и его гибкость, и его капризность.
На практике выбор между CTC и seq2seq часто диктует не точность, а задержка. Голосовой набор в телефоне или живые субтитры на стриме обязаны показывать слова почти мгновенно, по мере речи — здесь царит CTC, потому что он выдаёт результат покадрово и не ждёт конца фразы. А вот расшифровка готовой записи подкаста никуда не торопится, и тут выгоднее seq2seq: он спокойно прочитывает всю запись и за счёт контекста делает меньше ошибок. Поэтому гибриды CTC + attention так популярны — они пытаются получить и скорость, и точность сразу.
Как работает под капотом
Энкодер в обоих подходах — это обычно стек трансформеров или свёрток, обрабатывающий мел-спектрограмму. Различие в «голове»: CTC ставит softmax по символам на каждый кадр и обучается CTC-потерей; seq2seq добавляет авторегрессионный декодер с attention. Современные модели нередко комбинируют оба (гибрид CTC + attention) для скорости и точности сразу.
Частые ошибки
- Считать CTC «языковой моделью». CTC почти не учитывает контекст; для грамотного текста нужна внешняя ЯМ или seq2seq-декодер.
- Применять Whisper для онлайна по кадрам. seq2seq хуже для строгого реального времени; для стрима лучше CTC-модели.
- Забывать про пустой символ. Без «blank» CTC не сможет различать повторяющиеся буквы.
Итоги
- End-to-end ASR — одна сеть звук → текст, без ручных словарей.
- Главная проблема — выравнивание кадров и букв.
- CTC схлопывает покадровый выход; быстр, хорош для потока (wav2vec2).
- seq2seq с attention сильнее по контексту, но требует всю запись (Whisper).