Четыре грабли, один вихрь и 60% на CIFAR-10 с M0+

от автора

Продолжение цикла. До этого были базовые цифры и анонс 5 архитектур. Теперь — что сломалось, как чинили, что узнали.


GraphKAN: полный датасет меняет всё

В прошлых постах я показывал 96.15% на MNIST на 10K сабсете. Переход на полный датасет (60K) — 94.46% после 20 эпох float + 5 STE. Нашёл баг: ternary_map[2]=0 — +1 молча обнулялся на unpack.

Fashion-MNIST: 76.6% -> 86.73% (30 эпох float + 10 STE, полный датасет, 49 минут).


CNN Fashion: аугментация + cosine annealing

Было 87-88%. Схема 10+20+5+10 эпох с аугментацией и cosine annealing дала 90.54%. Сжатие 16x. Тернарная версия не потеряла точность — выиграла 0.44 п.п.


ViT: история про взрыв дисперсии

Первая попытка: loss 8.57, accuracy 16.3%. Чуть лучше random. Копаю — attention scores в one-hot, градиенты нулевые. Причина: ternary Q/K/V без нормализации выхода дают variance на порядки выше, чем нужно. Softmax вырождается.

Фикс — learnable per-projection scaling: log_scale_qk, log_scale_v, log_scale_o, log_scale1, log_scale2. Пять параметров, каждый учится гасить variance до входа в softmax.

Результат: 60.30% на CIFAR-10, 25.4 КБ. Не SOTA, но для микроконтроллера без FPU — честно.


RNN/LSTM: не взлетело

SMNIST, 28 шагов по пикселям. RNN: 18.2%. LSTM: 20.64%. Float на той же архитектуре — ~25%. Причина: hidden_dim=64 не тянет 28 шагов. Нужно 128-256, но это 4-8x больше параметров. Тернарность тут ни при чём.


C-кодогенерация: все пять под Unicorn

5 архитектур генерируются в C11, компилируются под cortex-m0plus, прогоняются через Unicorn. Bit-exact для всех.

Архитектура

.bin

R0

Статус

GraphKAN MNIST

192 KB

-88

PASS

CNN Fashion

107 KB

0

PASS

Transformer

20 KB

0

PASS

LSTM

17 KB

-3

PASS

ViT CIFAR-10

43 KB

1

PASS

M0+ специфичные грабли

memset recursion с -O2. GCC превращает while(n–) в рекурсивный bl memset. Бесконечный цикл. Лечится -fno-builtin.

sat_q7(). (int8_t)(acc >> 7) — переполнение. Нужна явная saturation.

ternary_map[2]=0. В CNN-кодогенераторе третий элемент (val=2, +1) маппился в 0. Вес обнулялся.

output_idx uint8 -> uint16. Для моделей с >255 нейронов индексы выхода — 834-843. Не влезают в uint8.


Итог

192 файла, 34K строк, 95 тестов. Пайплайн замкнут: Python -> train -> export -> C11 -> arm-gcc -> .bin -> Unicorn. Две модели не взлетели (RNN/LSTM — архитектурное ограничение), остальные работают с приростом точности относительно float.


Ссылки:


2026. Fakeonomics. Все права защищены.

ссылка на оригинал статьи https://habr.com/ru/articles/1050030/