Распознавание речи ВКонтакте: инфраструктура

МЕНЮ


Искусственный интеллект
Поиск
Регистрация на сайте
Помощь проекту

ТЕМЫ


Новости ИИРазработка ИИВнедрение ИИРабота разума и сознаниеМодель мозгаРобототехника, БПЛАТрансгуманизмОбработка текстаТеория эволюцииДополненная реальностьЖелезоКиберугрозыНаучный мирИТ индустрияРазработка ПОТеория информацииМатематикаЦифровая экономика

Авторизация



RSS


RSS новости


Публикуем расшифровку последнего доклада с онлайн-митапа от Команды ВКонтакте VK Tech Talks | ASR. В этот раз спикер Сергей Ларионенко расскажет о том, как проектировали бэкенд-сервис ASR для сотен миллионов запросов в день, с какими проблемами и трудностями пришлось столкнуться, как их решали, а также о дальнейшем пути развития сервиса.

С чего всё начиналось?

Изначально было:

  • всего несколько файлов моделей процессинга аудио;
  • пример кода C++, который непосредственно работал с этими моделями;
  • входящая очередь сообщений, в которой хранились URL на аудиосообщения;
  • серверы, в которых хранились аудиосообщения;
  • результирующая очередь, куда нужно отдавать транскрипцию, то есть распознанный текст.

С самого начала понималось, что этот сервис будет высоконагруженным — нужно обрабатывать около двухсот миллионов аудиосообщений в день, это нужно делать быстро — всего за несколько минут нужно обрабатывать любое аудиосообщение, также всё это должно делаться на 90 видеокартах Nvidia Tesla T4.

Вначале общий алгоритм выглядел достаточно просто:

  • нужно было забрать из очереди ссылку на аудиосообщение;
  • скачать с сервера это аудиосообщение;
  • раздекодить, то есть сконвертировать из opus-формата в pcm-семплы;
  • прогнать сырой звук через модели (AM, LM, PM);
  • отдать в результирующую очередь получившийся распознанный текст.

Проблемы

Однако есть ряд проблем, которые необходимо решить перед тем как реализовывать этот сервис.

Первая проблема — слишком много C++. Дело в том, что инфраструктура в основном написана на Go, и, соответственно, реализовывать библиотеки на C++ не очень хочется. Это значит что нужно каким-нибудь способом подружить C++ с Go. Также понимается, что «плюсовая» часть остаётся, которая относительно сложна в отладке и профилировании.

Решение проблемы достаточное простое: для связки C++ и Go используется расширение языка CGO, то есть верхнеуровневый код пишется на Go, а непосредственное обращение к моделям, декодинг и так далее уже реализовывается на C++. Потом берётся общий «плюсовый» код, разбивается на маленькие функции, помещается это всё в библиотеки, обкладывается тестами, и, соответственно, появлялась возможность для того, чтобы дебажить и профилировать «плюсовую» часть в отрыве от остального сервиса.

Следующая проблема — батчинг. Обычно, когда используются вычисления на карточке, применяются два основных типа ресурсов — память и Cuda-ядра. Дело в том, что если посылать, допустим, лишь одно аудиосообщение на видеокарточку для обсчёта, то, соответственно, проигрывается в перфомансе. Грубо говоря, гораздо эффективней посылать некий батч, то есть группу аудиофайлов для обсчёта на карточку. Это должно дать буст касаемо производительности — лучше будут утилизироваться и Cuda-ядра, и память.

Однако, стоит отметить, что все аудиофайлы в батче должны быть выравнены. Это означает, что если в одном батче, который посылается на видеокарту, нужно сгруппировать аудиодорожку длиной в одну секунду, и второй — длиной в две секунды, то первую придётся добивать нулями — для того, чтобы аудиозаписи были выровнены по одной длине. Момент padding, выравнивания нулями, можно только минимизировать. Полностью от него уйти невозможно — размер аудиосообщений VK никак не ограничен.

Решение этой проблемы было в группировании аудиофайлов в большие метабатчи. Затем — сортировка внутри в метабатча файлов по длине аудио, и потом разделение метабачей на маленькие батчи, которые попадают на видеокарту. Что же делать с большими аудиофайлами? Тут всё просто: если по каким-то причинам нет возможности обрабатывать большие файлы, то просто не нужно этого делать. Для этого нужно фильтровать по длине аудиозапись, которая будет обрабатываться. Единственное, что для этого нужно, — выбрать длину, по которой фильтровать. По статистике в 90 % случаев пользователи отправляют аудиосообщения VK длинной менее 30 секунд. Соответственно, эту величину выбрали как порог для фильтрации.

Система мониторинга

Стоит отметить, что когда делается большой высоконагруженный сервис, то не стоит забывать про систему мониторинга. Она нужна для того, чтобы на ранних стадиях можно было детектить какие-либо аномалии и быстро определять, что пошло не так и в каком конкретном месте. Например, один раз в VK моргнула сеть до серверов, на которых лежали аудиофайлы, и, соответственно, время скачивания аудиосообщения возросло с 7 миллисекунд до 70. В итоге начали скапливаться аудиосообщения входящей очереди, о чём был сразу получен алерт, и буквально в течение 10 минут, посмотрев на графики, было определено, что конкретно и где пошло не так, и было быстро всё пофикшено.

Первая версия

Дизайн первой версии обобщённо можно представить так: n-потоков и n Worker, которые ничего не знали друг о друге, собирали большой метабатч аудиосообщений, точнее ссылок, на аудиосообщения, и прогоняли этот метабатч по несколькими стадиям. На каждой из стадий были свои Worker, которые шарились между основными тредами. Получалась очень хорошая утилизация вычислительных ресурсов.

Чтобы развернуть первую версию не дожидаясь серверов, решили использовать видеокодировщик. VK обрабатывает очень большие объёмы видеофайлов — пользователи льют гигабиты, и это надо как-то обрабатывать, кодировать. Делается это на видеокодировщиках. По факту это сервер, на котором крутятся PHP-скрипты, которые дёргают FFmpeg, а тот — модуль NVENC от Nvidia. Кодирование, происходящее на этом модуле, никак не затрагивает Cuda-ядра, то есть они простаивают, и памяти используется очень мало.

Однако тут возникала проблема: на видеокодировщиках уже имелся сервис по нарезке фотографий. И получается, что нужно было разместить три сервиса на одной карточке. Дело в том, что когда с разных процессов обращаются к одной видеокарточке, то неизбежно попадают в ситуацию, когда идут свитчи контекстов для работы в драйвере, плюс каждый из этих процессов, когда обращается к видеокарточке, монопольно захватывает Cuda-ядра, при этом, скорее всего, он не утилизирует их на 100 %.

Для совмещения трёх сервисов, которые по максимуму утилизируют и память, и Cuda-ядра, было выбрано решение от Nvidia — MPS-технология. Грубо говоря, это прослойка между клиентским приложением, драйвером и видеокарточкой. Именно MPS берёт на себя основную работу по шерингу ресурсов видеокарточки.

Итоги первой версии

  • Работоспособный сервис показан пользователям;
  • аудиосообщения быстро обрабатываются (около трёх секунд);
  • спокойно выдерживается нагрузка в 150–170 миллионов сообщений в день;
  • этот сервис стал очень надёжным — с момента запуска никаких серьёзных проблем не было.

Проблемы первой версии

Первая версия имела ряд проблем. Из-за того, что метабатч проходит через все стадии, результат можно было отправить только после того, как распознаны все аудиосообщения в нём. Это влияет на задержку. При такой архитектуре любая ошибка ведёт к тому, что в метабатче оставалось меньше данных.

Для решения всех проблем было переписано приложение. В нём отказались от вертикальных Worker, от тредов исполнений в программе. Теперь всё приложение выглядит как единый пайплайн с унифицированными Step, каждый из которых отвечает за свою фазу обработки. На выходе получилось, что стали быстрее избавляться от плохих данных в пайплайне, быстрее стала отдача транскрипции пользователям.

Итоги второй версии

  • По факту было улучшено 90 перцентиль при обработке, что примерно составляет 30 %, то есть стали гораздо быстрее обрабатывать аудиосообщения.
  • Получили легко расширяемый пайплайн благодаря унифицированным шагам.
  • Более детальная обработка и мониторинг ошибок на каждом из этапов.

В дальнейшем, чтобы отказаться от фильтрации, нужно: разбивать длинные аудиосообщения на несколько кусков и уже их прогонять по стадиям процессинга, а потом на стадии LM-модели склеивать их обратно.

Однако здесь есть нюанс — нельзя вслепую разрезать аудиосообщение на этапе декодинга. Поэтому пришли к VAD-алгоритму (англ. Voice Activity Detection), который решает проблему идентификации голоса на каком-то куске аудио. Грубо говоря, можно с какой-то вероятностью сказать, что на 30 миллисекундах аудио есть голоc человека или нет. VAD-алгоритмы выбрали, потому что они давно используются, а также достаточно быстрые, широко представлены как в кодеках, так и в протоколах работы с голосом.

Разрезание аудио уже на этапе бета-тестирования, и в скором времени это даст возможности использования сервиса ASR в других целях. Например, транскрипция подкастов или автогенерирование субтитров к видеофайлам.

В конце подведём выводы.

  • Нужно выбирать технологии, которые подходят именно вам.
  • Много логов, статистики, мониторинга не бывает. Тем самым получается механизм, через который можно на ранней стадии детектить какие-то аномальные проблемы.
  • К решению новых проблем стоит подходить итеративно.

Вот и всё! Посмотреть видео можно тут:


Источник: m.vk.com

Комментарии: