🥹 Как выжать из железа максимум: эволюция инференса в Алисе
Привет, меня зовут Витя Хаймоненко, я разработчик в команде голосовой активации Алисы. Сегодня я расскажу, как мы прошли путь от инференса на чистом C корутинами до современных движков и квантизованных моделей — и какой инженерный опыт из этого вынесли.
🅰️ Железо фиксировано, а модели растут
На колонке одновременно крутится несколько нейросетей:
🔴 Активационные модели, которые реагируют на слова «Алиса» или «Яндекс»
🔴 Командные споттеры — модели, которые поддерживают набор популярных команд, например, для управления музыкой или умным домом
🔴 Интонационные споттеры, которые делают общение с ассистентом более естественным и понимают, когда вы обращаетесь к колонке без слова «Алиса»
Все они работают в реальном времени и должны укладываться в жёсткие лимиты по latency, CPU и RAM. Докинуть ресурсы на устройство нельзя, поэтому единственный путь — оптимизация инференса.
🅰️ Как ситуация обстояла раньше
В 2014 году не было ни готовых инференс-фреймворков, ни повсеместной поддержки C++ на чипах. Мы писали на чистом C, а пайплайн описывали корутинами. Композиция получалась гибкой, но за это мы расплачивались накладными расходами на переключение контекстов и динамическими аллокациями памяти на каждом шаге.
Уже тогда появились оптимизации, которые живут до сих пор. Во-первых, батчинг: вместо обработки одного фрейма звука за раз мы ждём, пока накопится батч из нескольких фреймов, и обрабатываем их разом. Это позволяет эффективно использовать SIMD-инструкции процессора и даёт серьёзный выигрыш по скорости, жертвуя лишь примерно полусотней миллисекунд latency.
Во-вторых, стриминг: мы не пересчитываем целиком всё «окно» заново, а только обновляем его с приходом новых фреймов. Для матричных умножений использовали одну из реализаций BLAS, которая отлично утилизирует кеш процессора.
🅰️ Переезд на C++ и библиотеки инференса
С появлением нормальной поддержки C++ на чипах мы стали переписывать рантайм. На смену корутинам пришла наша внутренняя библиотека YNMT, изначально сделанная для моделей перевода. Она представляет модель как статический вычислительный граф, из-за чего отпала необходимость в корутинах. А потом, когда YNMT стала уступать по скорости опенсорсному TFLite, мы переехали на него. Он поддерживает встроенные оптимизации под разные архитектуры чипов.
🅰️ Два захода в квантизацию
Первый подход был простым и безопасным: мы сжимали веса модели с FP32 до INT8, но перед вычислением разжимали их обратно. Затем научились квантовать и активации. Теперь мы на лету сжимаем вход до INT8, перемножаем матрицы в INT16 и деквантизуем выход. Выигрываем и по памяти, и по скорости за счёт более быстрых целочисленных SIMD-инструкций.
🅰️ Ещё одна инфраструктурная оптимизация — мультиспоттер
Раньше каждая модель тащила за собой свои буфера и предобработку звука, что вело к дублированию вычислений. Теперь общая часть вынесена в отдельную сущность, и все модели пользуются ею совместно.
🅰️ Главные уроки
Не стоит бояться использовать опенсорс-решения и адаптировать их под своё железо. Мы взяли стандартную реализацию свёртки в TFLite, переработали её под конкретный чип и получили огромный прирост производительности. К тому же у опенсорсных подходов есть неплохой плюс: зачастую они поддерживаются широким сообществом.
Сейчас мы в процессе миграции на TFLite, готовимся использовать встроенную квантизацию вместо ручной и продолжаем пилить кастомные операции. Всё это позволяет делать колонки умнее и при этом сохранять доступность для пользователей.
⏩️ А больше технических деталей можно узнать в полной записи доклада с Я.Железо. Смотрите на ютубе и в VK Видео.
Подписывайтесь:
💬 @Yandex4Developers