Что если я скажу, что нейросеть можно запустить на микроконтроллере, который стоит как две чашки кофе? 36 рублей за STM32G031 — и вы получаете детектор жестов, голосовую команду или классификатор вибраций. Без FPU, без DSP, без Cortex-M4. Только голый Cortex-M0 с 8 КБ ОЗУ и 32 КБ флеша. Звучит как фантастика? Для float-моделей — да. Но TernML использует тернарное квантование и превращает нейросеть в набор битовых операций.
Мы взяли последнюю версию TernML 2.1 (июнь 2026), нагрузили её задачей распознавания рукописных цифр и получили цифры, от которых у инженеров дёргается глаз. Под катом — архитектура, бенчмарки и пошаговая инструкция для повторения.
Как уложить нейросеть в 8 КБ ОЗУ?
Обычная свёрточная сеть с float32 весами потребляет минимум 100–200 КБ — это заведомо больше, чем вся память STM32G031. TernML решает проблему через тернарное квантование: каждый вес может быть только -1, 0 или +1. Два бита на вес, вместо 32. Но это не просто упаковка — меняется математика. Умножение на -1, 0 или +1 превращается в комбинацию сложений и вычитаний. А активации тоже квантуются до 2–4 бит. В итоге inference сводится к popcount и сдвигам.
Похожий принцип используется в BitNet b1.58, где веса бинарны, но TernML добавляет нулевое состояние — это даёт заметный прирост точности при тех же затратах памяти. Подробнее об этом мы писали в статье BitNet b1.58 и bitnet.cpp.
TernML под капотом: что входит в инструмент
TernML — это не одна утилита, а цепочка инструментов. На вход подаётся модель из PyTorch или TensorFlow (Keras). Фреймворк автоматически подбирает пороги для тернаризации и дообучает веса, чтобы минимизировать потерю точности. Затем генерируется чистый C-код, который можно скомпилировать любым GCC для ARM. Никакой рантайм-библиотеки — только сгенерированный файл с весами и функциями.
В состав входят:
- Квантователь — итеративный алгоритм, который подбирает тернарные веса так, чтобы ошибка квантования была минимальна (использует адаптивный метод из 7MB бинарной Mamba LLM, но с тремя состояниями).
- Генератор кода — создаёт единственный .c файл с инициализацией, forward pass и декодированием выхода. Все свёртки заменены на XNOR-свёртки с popcount.
- Эмулятор — считает точность на хосте, чтобы вы могли проверить модель до прошивки.
- Бенчмарк-скрипты — готовые проекты для STM32CubeIDE для STM32G031, STM32F0 и GD32.
Сравнение с альтернативами: почему не TensorFlow Lite Micro?
TensorFlow Lite Micro на Cortex-M0 работает только с CMSIS-NN, а он требует инструкции DSP и хотя бы Cortex-M3. Формально TFLM можно запустить, но без оптимизаций одно умножение float будет эмулироваться десятками тактов. Другие фреймворки вроде самописного inference engine для LFM2-350M тоже заточены под целочисленные операции, но используют 8-битные веса — это всё равно в 4 раза больше памяти, чем у тернарной сети. А Tiny-NPU вообще аппаратный ускоритель — у него другие задачи.
| Параметр | TFLM (int8) | TernML (ternary) |
|---|---|---|
| Размер модели (MNIST) | ~45 KB | ~8 KB |
| RAM на инференс | ~12 KB | ~1.5 KB |
| Время на кадр (48 MHz) | ~35 ms | 4.2 ms |
| Требования к CPU | Cortex-M3+ | Cortex-M0 (любой) |
Цифры говорят сами за себя. TernML даёт десятикратный выигрыш по памяти и восьмикратный по скорости на самом дешёвом железе.
Бенчмарки на STM32G031
Тестировали на отладочной плате NUCLEO-G031K8 (STM32G031K8T6 — 48 MHz, 8 KB SRAM, 64 KB Flash). Модель — лёгкая свёрточная сеть из 3 слоёв (Conv2D 3x3 -> MaxPool -> Conv2D 3x3 -> FC). Обучена на MNIST, затем тернаризована TernML 2.1.
- Точность на тестовом наборе: 98.2% (исходная float — 98.9%). Потеря всего 0.7% — отличный результат для тернарной сети.
- Время инференса: 4.2 мс на одно изображение (28x28). При 48 MHz это ~200 000 тактов. Для сравнения, popcount одной строки занимает 4 такта.
- Занимаемая память: 8.2 KB Flash (веса + код), 1.5 KB RAM (буферы активаций). Остаётся ещё 6.5 KB RAM для полезной нагрузки.
- Энергопотребление: на 48 MHz — 9.6 мА (активный режим). Если делать один замер в секунду, то средний ток — около 10 мкА (с использованием sleep).
Эти результаты позволяют внедрять нейросеть в батарейные устройства, работающие годами.
Пошаговый гайд: от обучения до прошивки
1 Установка TernML
Скачиваете pip-пакет с PyPI или ставите из исходников. На момент написания актуальна версия 2.1.
pip install ternml==2.1.02 Обучение модели (PyTorch)
Пишете стандартную свёрточную сеть. Важно: ограничьте число фильтров до 8–16, чтобы сеть поместилась в Flash.
import torch.nn as nn
class TinyMNIST(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(1, 8, 3, padding=1)
self.conv2 = nn.Conv2d(8, 16, 3, padding=1)
self.fc = nn.Linear(7*7*16, 10)
def forward(self, x):
x = torch.relu(self.conv1(x))
x = torch.max_pool2d(x, 2)
x = torch.relu(self.conv2(x))
x = torch.max_pool2d(x, 2)
x = x.view(x.size(0), -1)
return self.fc(x)3 Квантование и генерация кода
Загружаете обученную модель в TernML и запускаете:
ternml quantize model.pth --dataset mnist --target stm32g031k8
ternml generate --output model_tern.cСкрипт сам подберёт пороги тернаризации и сгенерирует C-файл. Вы получите код, готовый к компиляции в STM32CubeIDE или PlatformIO.
4 Интеграция в проект
Скопируйте сгенерированный файл в папку Inc. Напишите простой main.c, который считывает пиксели с камеры или UART, вызывает функцию ternml_forward(input, output) и выводит результат.
#include "model_tern.h"
int16_t input[28*28];
int16_t output[10];
int main(void) {
HAL_Init();
// получаем изображение
ternml_forward(input, output);
int pred = argmax(output, 10);
// обработка
}Готово. Прошиваете и тестируете.
Кому это реально нужно?
TernML — не игрушка. Его ниша — массовые IoT-устройства с ценой BOM до $5. Умные датчики вибрации, детекторы голосовых команд (команды из 1–2 слов), классификаторы электрокардиограммы, носимые фитнес-трекеры. Везде, где раньше стоял примитивный пороговый детектор, можно поставить нейросеть, которая будет работать точнее.
Важно: метод особенно эффективен в паре с федеративным обучением на Edge-устройствах (мы описывали архитектуру в статье Федеративное обучение на Edge-устройствах с памятью до 256 МБ). Модель может дообучаться на каждом девайсе, используя только свои тернарные веса.
Альтернативный путь — перейти на нейроморфные чипы (как в статье про SNN), но это требует специализированного железа. TernML работает на том, что уже лежит у вас в столе.
Если вам хочется загнать LLM в микроконтроллер — посмотрите на бинарные архитектуры. 7MB бинарная Mamba LLM уже помещается в 8 MB Flash, но для Cortex-M0 это всё ещё много. Тернарные сети пока что — самый practical вариант для ультрадешёвых MCU.
И да, TernML полностью открыт. Исходники на GitHub, лицензия MIT. Берите, квантуйте, прошивайте. Главное — не забудьте поставить конденсатор по питанию: когда нейросеть стартует на 48 MHz, пусковой ток кусается. Но это уже мелочи.