Комментарии 7
Спасибо за статью! Интересно было почитать и повспоминать, как сам такое делал.
AEC можно сделать програмно, если в железе оно не предусмотрено. VAD крадёт некоторое время, ожидая окончания фразы - уменьшить можно семантическим и просодическим эндпоинтонгом и небольшой моделью вероятности продолжения/окончания фразы. Для более быстрой реакции робота можно параллелить vad/vosk/sst.
Какое у вас в итоге получается время между окончанием команды и началом звукового ответа/действия?
Спасибо, всё три пункта по делу.
Про AEC — да, «не будет» я написал слишком категорично. Софтовый AEC вроде WebRTC AEC3 как раз и рассчитан на разные несинхронные устройства. Изначальный блокер у нас был в другом: микрофон сидел на закрытой вендорской плате со своим beamforming и шумодавом, сырого сигнала мы просто не видели. С внешней петличкой сырой поток уже наш, так что путь открыт — просто пока не дошли, обошлись окном по времени.
Про эндпоинтинг — тут вы попали в самое больное место. Мы сейчас просто ждём тишины: 250 мс, чтобы закрыть фразу, и ещё 400 мс на случай, если человек продолжит. Эти 400 мс мы добавили ради одного сценария — диктовки списка «первое… второе…», а платят за них все команды подряд. Итого 0,65 с потерь на каждой фразе. Модель, которая по звуку скажет «фраза закончена» (например, smart-turn v3 — 8M параметров, русский есть), сняла бы обе константы разом. А для команд семантический эндпоинт почти бесплатный: если Vosk с закрытой грамматикой уже уверенно собрал «стой», ждать ещё 650 мс тишины незачем.
Про параллелить — тоже да, сейчас после закрытия фразы всё последовательно: Vosk → whisper → роутер. Vosk можно кормить потоково во время речи, whisper запускать спекулятивно на первой паузе. Это в планах.
Цифры от момента, когда человек замолчал: «секундочку» звучит через ~0,7 с (почти всё это — hangover VAD). Команда через Vosk уходит в мотор-стек примерно через секунду. В диалоге whisper на GPU 0,5–0,9 с, первое предложение от LLM ещё 0,3–2 с, TTS 0,1 с — голосовой ответ начинается через 2,5–5 с. А у вас какие цифры получались?
Я использую gigaam, поэтому время на sst сразу урезается раз в десять по сравнению с whisper. Но мне не нужна мультиязычность. Глянул последние логи: от окончания моего запроса до ответа - около 150мс на простых tool, ещё около 150-300мс если small talk с llm. Это с учетом wakeword, двух гейтов (кто сказал и действительно ли произнесено именно wake). Когда более-менее настроил, то был убеждён, что неправильно меряю время :) Проверил записью команд-ответов со стороны и анализом получившегося аудио - всё сошлось.
Спасибо за цифры — 150 мс с двумя гейтами это очень сильно, и главное — за метод: замер записью со стороны честнее любых таймстампов в логах, у нас они не видят буфер плеера и просыпающийся усилитель. Так и перемеряем. По GigaAM согласен, что быстрее на порядок; нас держит только смешанный русско-английский текст для LLM. Но вы навели на мысль: роутеру команд транслитерация не мешает — можно давать быстрый текст роутеру, а точный whisper — модели. Попробуем 👍
5506 «Продолжение следует...» за месяц — классический артефакт претрейна на ютубовских субтитрах, и порогом по громкости его правда не отсечь: VAD уже сказал «речь». Я бы смотрела не на амплитуду, а на длительность речи, которую отдает Silero: сегменты короче 300-400 мс почти гарантированно уходят в галлюцинацию, потому что декодеру нечего декодировать и он сваливается в самый частый хвост обучающих данных. Второй дешевый рычаг — no_context, иначе одна фраза-паразит начинает тянуть за собой следующие. Сколько из этих 5506 пришлось на сегменты короче полусекунды по речи, длительность в логах вообще есть?
Длительность в логах есть — и длина сегмента, и профиль Silero по каждому: сколько 32-мс окон детектор счёл речью, доля, плато. По устройству пайплайна: сегменты короче 150 мс речи до whisper не доходят вообще, к каждому приклеены 300 мс pre-roll и 250 мс хвоста — whisper всегда видит не меньше 0,7 с звука. И всё равно 5506. Так что дело не в длине, а в форме сигнала: посторонние фоновые звуки, на которых VAD дребезжит у порога вместо плато. Этот профиль и пишем в лог, чтобы посчитать порог по живым данным. Порог 300–400 мс нам закрыт и по другой причине: «иди» звучит около 300 мс, поэтому короткие команды идут через Vosk с закрытой грамматикой.
Про no_context — у нас контекст между фразами не передаётся в принципе. Каждая фраза уходит в whisper отдельным запросом, модель начинает с чистого листа и предыдущий текст не видит. Плюс в самом whisper.cpp запрещено подмешивать предыдущее распознавание в промпт (это и есть no_context, у нас включён, а переносимый контекст выставлен в ноль). Так что цепочка «фраза-паразит потянула следующие» у нас невозможна физически.
У GigaAM есть 3е версия модели (end-to-end). Она возвращает английские слова и цифры, как Whisper. Работает гораздо быстрее, конечно.
Информация
- Сайт
- 361robotics.ru
- Дата регистрации
- Дата основания
- Численность
- 2–10 человек
- Местоположение
- Россия
- Представитель
- Антон Якимов
Разговорили гуманоида: как за месяц мы построили русскоязычный голосовой стек для китайского робота