ExecuTorch на Cortex-M: PyTorch на микроконтроллерах
Модель поиска аномалий на микроконтроллере обрабатывает один сигнал за 1,2 миллисекунды и занимает 5,2 КиБ оперативной памяти. Не мегабайта, не сотни килобайт: пять килобайт, меньше, чем весит одна фотография с телефона. Именно такие цифры PyTorch Foundation приводит для нового бэкенда ExecuTorch под Arm Cortex-M, и они означают, что эпоха обязательной конвертации в чужие форматы для встраиваемых устройств заканчивается.
До сих пор запуск нейросети на микроконтроллере выглядел как отдельная профессия. Модель обучали в PyTorch, потом перегоняли в TensorFlow Lite Micro или проприетарный формат вендора, держали два тулчейна, ловили расхождения в числах после конвертации и молились, чтобы квантование не убило точность. Новая ветка ExecuTorch убирает промежуточный шаг: экспорт, квантование и понижение графа происходят в том же стеке, который обслуживает мобильные, десктопные и серверные модели.
Что такое ExecuTorch Cortex-M
Это бэкенд ExecuTorch, который заменяет стандартные операторы PyTorch на ускоренные версии из библиотеки Arm CMSIS-NN и исполняет INT8-граф прямо на ядре Cortex-M. Поддерживаются все три класса ядер, от M0 до M85, а компиляция идёт с прицелом на конкретную модель чипа, которую разработчик знает заранее.
От мобильного сценария этот отличается отсутствием борьбы за универсальность. В телефоне приложение обязано работать на десятках разных SoC, поэтому решения принимаются на лету. В микроконтроллере железо известно до сборки: точное ядро, набор инструкций, объём SRAM, размер флеша. ExecuTorch пользуется этим и переносит всю тяжёлую работу в момент компиляции.
Почему PyTorch раньше не жил на MCU
Причина не в лени разработчиков. У микроконтроллеров нет операционной системы с виртуальной памятью, нет аллокатора, который прощает фрагментацию, и почти нет запаса по питанию. Типичный бюджет выглядит так: меньше мегабайта SRAM, единицы мегабайт флеша, энергопотребление в милливаттах. Классический рантайм PyTorch в такие рамки не помещается ни в каком виде.
Отсюда и родился обходной путь с конвертацией. Он работал, но стоил дорого: любая правка архитектуры означала новый цикл экспорта, а расхождения между фреймворками проявлялись уже на устройстве, где отладка превращается в испытание. Аргумент про 90% исследовательского кода на PyTorch, который приводят авторы, здесь не про моду, а про стоимость перевода: каждый шаг конвертации добавляет день работы инженера.
Три класса ядер и три уровня ускорения
Бэкенд не требует, чтобы у вас был самый быстрый чип. Он подстраивается под то, что есть, и выбирает подходящую реализацию при компиляции, без диспетчеризации в рантайме.
| Класс | Ядра | Что даёт | Типичное применение |
|---|---|---|---|
| Scalar | M0, M0+, M3, M23 | Базовые инструкции Arm | Дешёвые датчики, минимальный кремний |
| DSP | M4, M7, M33, M35P | SIMD-инструкции multiply-accumulate | Средние MCU, обработка сигналов |
| MVE (Helium) | M55, M85 | 128-битные векторные операции, beat-based исполнение | Высокопроизводительный вывод на MCU |
Основной прирост даёт Helium. CMSIS-NN использует его модель исполнения с так называемыми beat-ами, чтобы совмещать перемещение данных и вычисления, и это особенно заметно на depthwise и pointwise свёртках, из которых собрано большинство edge-архитектур. Но сам рабочий процесс одинаков для всех трёх классов: меняется только конфигурация цели.
Что компилятор делает заранее
Самая интересная часть документации касается не цифр, а того, что вынесено из рантайма. ExecuTorch принимает решения на этапе сборки, и на устройстве не остаётся ни одной задачи, которую можно было бы решить раньше.
Удаляются лишние преобразования типов, пары quantize/dequantize и транспозиции формата памяти, которые обычно кочуют из графа в граф. Параметры квантования сворачиваются в целочисленные множители и сдвиги, поэтому инференс остаётся полностью в fixed-point арифметике, без плавающей точки. Веса свёрток транспонируются из родного для PyTorch формата OIHW в ожидаемый CMSIS-NN формат OHWI, и эта перестановка оплачивается один раз при компиляции, а не на каждом кадре.
Дальше начинается работа с памятью, и это главный подарок для встраиваемых инженеров. Все тензоры и скретч-буферы раскладываются заранее, с поддержкой нескольких регионов памяти одновременно, например TCM и SRAM. Критичные по задержке буферы можно вручную положить в быструю локальную память. На устройстве нет кучи в привычном смысле, нет аллокатора и нет риска фрагментации под длительной нагрузкой: объём RAM детерминирован и ограничен сверху.
Плюс выборочная сборка операторов: линкуется только то, что реально нужно модели, поэтому размер кода масштабируется вместе с ней, а не с фреймворком. Ядро рантайма занимает меньше 20 КиБ флеша.
Цифры: MLPerf Tiny на Alif Ensemble E8
Авторы прогнали четыре из пяти моделей набора MLPerf Tiny на плате Alif Ensemble E8, используя одно высокопроизводительное ядро Cortex-M55 на 400 МГц и не трогая NPU Ethos-U. Все модели квантованы в INT8 через CortexMQuantizer, тестовой обвязкой служил Alif ML Embedded Evaluation Kit.
| Модель | Задача | Задержка | Выводов в секунду | Пик RAM | Флеш, всего |
|---|---|---|---|---|---|
| DS-CNN | Распознавание ключевых слов | 5,8 мс | 172 | 26 КиБ | 73,7 КиБ |
| MobileNet v1 0.25 | Детекция людей (Visual Wake Words) | 21,5 мс | 47 | 85 КиБ | 289,1 КиБ |
| ResNet-8 | Классификация изображений | 15,3 мс | 65 | 55 КиБ | 119,6 КиБ |
| Autoencoder | Детекция аномалий | 1,2 мс | 828 | 5,2 КиБ | 287,0 КиБ |
Читать эту таблицу стоит не глазами, а калькулятором. Autoencoder обрабатывает 828 сигналов в секунду на пяти килобайтах памяти, то есть непрерывный поток с датчика вибрации или тока закрывается одним дешёвым чипом без облака и без задержки на сеть. MobileNet на 85 КиБ RAM означает, что камера с распознаванием людей укладывается в бюджет, где раньше жили только пороговые эвристики.
Авторы честно оговаривают: это их собственные замеры, а не официальная заявка в MLCommons, поэтому сравнивать напрямую с лидербордом нельзя. Оговорка не формальная: разные платы, частоты и настройки кэша дают разброс в разы.
Демо: детектор людей на Cortex-M85
Второй пример ближе к продукту, чем к бенчмарку. Модель µYOLO, обрезанная depthwise separable CNN с линейной головой предсказания, запущена на плате Renesas EK-RA8D1 с ядром Cortex-M85 на 480 МГц.
Модель весит 452 КиБ в INT8, весь прошивочный образ занимает 640 КиБ флеша, пиковая RAM составляет 254 КиБ. Один кадр 128×128 RGB обрабатывается за 24 миллисекунды, сквозная пропускная способность достигает 12,7 кадра в секунду. Этого достаточно для датчика присутствия, мониторинга занятости переговорных, камеры умного дома или системы безопасности, работающей от батарейки.
Отдельная деталь стоит внимания всем, кто когда-либо выжимал производительность на MCU. Когда веса самых дорогих операторов перенесли в DTCM и включили кэш инструкций, задержка упала с 191 миллисекунды до 24, то есть почти в восемь раз. Никакой магии, просто правильная раскладка по регионам памяти, и именно такие вещи ExecuTorch теперь умеет делать на этапе компиляции.
NPU рядом: смешанное исполнение вместо «всё или ничего»
На многих современных SoC рядом с Cortex-M стоит NPU Ethos-U. Бэкенд Cortex-M не конфликтует с ним: после партиционирования графа под Ethos-U через компилятор Vela, оставшиеся операции уходят на CPU и исполняются через CMSIS-NN с теми же параметрами квантования, что уже есть в графе.
Практический смысл простой: покрытие операторов перестаёт быть пропуском в продакшен. Модель, у которой несколько слоёв не поддерживаются NPU, всё равно собирается и уезжает на устройство в смешанном режиме, а по мере роста покрытия эти слои постепенно переезжают на ускоритель. Если же квантование или раскладка не подходят под требования бэкенда, операция падает на портируемые ядра ExecuTorch: работает медленнее, но работает.
Почему это важно именно сейчас
Встраиваемый ИИ долго жил в двух крайностях: либо микроконтроллер с пороговой логикой, либо полноценный одноплатный компьютер с Linux, GPU и вентилятором. Cortex-M бэкенд размывает границу: задачи, которые раньше требовали Raspberry Pi, начинают укладываться в чип за доллар, и это меняет себестоимость, а вместе с ней и архитектуру продукта.
Приватность получается побочно: кадры не покидают устройство, утечки нет, а GDPR-обвязка вокруг видеопотока схлопывается до нуля. Задержка тоже перестаёт зависеть от качества связи, что критично для промышленной автоматики и медицинских датчиков, где решение нужно за миллисекунды, а не за двести.
Есть и менее очевидный эффект для команд. Когда обучение, экспорт и деплой живут в одном стеке, промежуточные представления остаются исполняемыми, и результат каждого шага можно сравнить с исходной моделью. Ошибки ловятся на ноутбуке, а не на стенде с щупом и осциллографом. Для отладки на целевом железе есть ETDump с пооператорной стоимостью, а граф можно рассматривать в Model Explorer.
Честные ограничения
Первое и самое заметное: весь пайплайн заточен под INT8. Для задач, где точность критична, это может быть блокером, и часть роадмапа как раз про смешанную точность. Второе: покрытие операторов всё ещё ориентировано на свёрточные архитектуры. Attention, layer normalization и другие паттерны трансформеров пока только в планах. Третье: замеры сделаны на конкретных платах, и перенос цифр на другой чип требует собственной проверки.
Что в роадмапе
Разработчики обещают расширить смешанную точность A8W4 и A16W8 на весь набор операторов и вывести наружу fp32 и fp16 MVE-ядра, которые появились в CMSIS-NN 8.0.0. Дальше идёт расширение покрытия: attention, layer normalization, операторы CMSIS-DSP вроде FFT, чтобы извлечение признаков тоже жило внутри графа. Плюс новые сквозные примеры: детекция голосовой активности, распознавание жестов, предиктивное обслуживание и streaming wake word, пятый бенчмарк MLPerf Tiny, которому нужна инфраструктура для многофреймового вывода.
Частые вопросы
Нужен ли для этого NPU?
Нет. Бэкенд рассчитан на обычное ядро Cortex-M, а Ethos-U используется опционально, если он есть в чипе. Все замеры из статьи сделаны как раз без NPU, на одном ядре M55.
Можно ли запускать fp32-модели?
Пока основной путь это INT8 через CortexMQuantizer, потому что CMSIS-NN оптимизирован под целочисленные ядра. Поддержка fp32 и fp16 появится вместе с экспортом MVE-ядер из CMSIS-NN 8.0.0, до этого неполностью квантованные модели идут через портируемые операторы.
Заменяет ли это TensorFlow Lite Micro?
По возможностям это прямая альтернатива для тех, кто обучает в PyTorch. Отличие от TFLite Micro в том, что конвертации нет вовсе: один формат, один тулчейн, никаких расхождений между фреймворками. Если проект уже живёт на TFLite Micro и устраивает, повода мигрировать нет.
Итог
ExecuTorch на Cortex-M закрывает давнюю дыру между исследованием и железом. Модель, обученная в PyTorch, теперь доезжает до микроконтроллера без промежуточных форматов, с детерминированной памятью и ускорением через CMSIS-NN, а цифры вроде 1,2 миллисекунды и 5,2 КиБ RAM показывают, что речь идёт о задачах, которые можно ставить в продукт.
Если у вас на полке лежит плата Cortex-M и идея датчика, который должен думать на месте, начните с примеров Cortex-M Backend в репозитории ExecuTorch: там есть готовые сборки под Corstone FVP, Raspberry Pi Pico 2 и Zephyr. Один вечер на прогон DS-CNN скажет больше, чем любая таблица бенчмарков.