# Отчёт: модель altai на MNIST Oct 12, 2026 Модель `altai` заметно лучше `blifat`: на полном MNIST без правок она даёт 73,3% верных ответов против 30,9%, а с порогом, подобранным на отдельной валидационной выборке, — 82,5%. ## Что это В примере `mnist-learn` две модели с одинаковой архитектурой: `blifat` (нейроны с вещественными параметрами) и `altai` (нейроны под чип «Алтай»). У `altai` все веса и пороги умножены на 1000, а перед проверкой сеть квантуется: веса и пороги сжимаются в целые числа в диапазоне ±255, как требует чип. В прошлом отчёте выяснилось, что `blifat` теряет точность из-за порога, который застывает после обучения. Здесь проверено, есть ли та же проблема у `altai`. ## Результаты Все прогоны на одних и тех же 10 000 тестовых картинках, однопоточный CPU-бэкенд. Порог для `altai` выбран на валидационной выборке (обучающие картинки 50 000–59 999), тестовые картинки в подборе не участвовали. | Обучение | blifat как есть | altai как есть | altai, `act=40`, без добавки к порогу | | --- | --- | --- | --- | | 3 000 | 49,5% | 76,2% | **84,2%** | | 30 000 | 42,0% | 75,6% | **84,1%** | | 60 000 | 30,9% | 73,3% | **82,5%** | Подробнее по `altai` (верно / ошибки / молчание): | Обучение | Как есть | С подобранным порогом | Время прогона | | --- | --- | --- | --- | | 3 000 | 7622 / 1545 / 833 | 8418 / 1521 / 61 | 1–3,5 мин | | 30 000 | 7563 / 787 / 1650 | 8409 / 1068 / 523 | 5,5–7 мин | | 60 000 | 7326 / 790 / 1884 | 8247 / 1235 / 518 | 14 мин | - Почти весь выигрыш даёт ответ на картинки, на которые сеть раньше молчала: на полном MNIST молчаний стало 518 вместо 1884. - Для сравнения: `blifat` с подобранным порогом давал 75,5%, но тот порог подбирался прямо на тесте, так что оценка была оптимистичной. ### Полный MNIST, altai с порогом `act=40`: результаты по цифрам | Цифра | Верно | Не отвечено | Ложные срабатывания | Precision | Recall | | --- | --- | --- | --- | --- | --- | | 0 | 825 | 63 | 92 | 0,900 | 0,929 | | 1 | 1062 | 2 | 71 | 0,937 | 0,998 | | 2 | 674 | 127 | 231 | 0,745 | 0,841 | | 3 | 699 | 142 | 169 | 0,805 | 0,831 | | 4 | 754 | 27 | 201 | 0,790 | 0,965 | | 5 | 718 | 34 | 140 | 0,837 | 0,955 | | 6 | 881 | 22 | 55 | 0,941 | 0,976 | | 7 | 913 | 31 | 84 | 0,916 | 0,967 | | 8 | 856 | 47 | 71 | 0,923 | 0,948 | | 9 | 865 | 23 | 121 | 0,877 | 0,974 | | **Итого** | **8247 (82,5%)** | **518** | **1235** | | | Самые слабые цифры — 2 и 3 (их чаще всего путают), самые надёжные — 1 и 6. ## Что выяснилось - **Почему altai лучше.** У `blifat` потенциал теряет треть за каждый шаг, и нейрон фактически видит только последние 2–3 шага картинки. У `altai` утечки нет (`potential_leak = 0`): нейрон суммирует все 10 шагов, то есть сравнивает с шаблоном всю картинку целиком. - **Проблема с порогом та же.** Порог входного нейрона — это основная часть плюс добавка, пропорциональная сумме положительных весов. Добавка растёт с длиной обучения (до квантования 1797 → 3008 → 3342 для 3k → 30k → 60k) и застывает на проверке. Поэтому и `altai` теряет точность при более длинном обучении. - **Квантование почти ничего не стоит.** После него веса становятся целыми от −6 до +2, и 37–60% из них нули. Но без квантования точность 76,8% против 76,2% (3 000 картинок). Вариант, где веса растягиваются на весь диапазон ±255, а порог масштабируется тем же множителем, дал 76,7%. - **Потенциал переносится между картинками.** Между картинками он не сбрасывается, и недобранный заряд переходит на следующую картинку. Сброс в офлайн-симуляции помогал только при высоком пороге; при подобранном пороге выигрыша нет, поэтому в сеть я его не добавлял. - **Лучший порог не зависит от длины обучения.** На валидации плато при `act` от 20 до 60 и для 3k, и для 30k; выбран `act=40` для всех. - **Altai воспроизводим.** Повторные прогоны совпали до картинки (на 30k и 60k), коллапса весов, как у одного прогона `blifat`, не было. - **Колонки из 20 нейронов и у altai работают как один нейрон**: веса внутри колонки идентичны. ## Как проверял 1. Добавил в пример `altai` выгрузку весов и порогов входного слоя до и после квантования. 2. Написал на numpy офлайн-симулятор нейрона AltAI по формулам бэкенда (без утечки, сброс в 0 при спайке, обнуление отрицательного потенциала, округление). Он совпадает с KNP до картинки: например, 8418 / 1521 / 61 и в KNP, и в симуляторе. 3. На симуляторе перебрал пороги, утечку и сброс между картинками на валидационной выборке. 4. Выбранный вариант подтвердил полными прогонами в KNP; все цифры в таблицах результатов — из KNP. ## Что изменено В `examples/mnist-learn/models/altai/prepare_network_for_inference.cpp` добавлены переменные окружения. Без них пример работает как раньше; изменение видно в `git diff`, его можно откатить. | Переменная | Что делает | | --- | --- | | `KNP_MNIST_THR` | заменяет порог входных нейронов на проверке (в единицах после квантования) | | `KNP_MNIST_ADD_THR_SCALE` | умножает добавку к порогу входных нейронов на проверке (0 — убрать) | | `KNP_MNIST_QUANT` | `none` — без квантования, `weights` — масштаб только по весам | | `KNP_MNIST_DUMP=<папка>` | сохраняет веса и пороги до (`float_*.csv`) и после (`quant_*.csv`) квантования | ## Как запускать ```bash cd ~/run/exp # лучший вариант для полного MNIST MODEL=altai ./run.sh myrun 60000 KNP_MNIST_THR=40 KNP_MNIST_ADD_THR_SCALE=0 cat myrun/result.csv ``` | Путь | Что внутри | | --- | --- | | `~/run/exp/tr.bin`, `tr.target` | N обучающих картинок + одни и те же 10 000 тестовых | | `~/run/exp/val.bin`, `val.target` | N обучающих картинок + валидация (картинки 50 000–59 999) | | `~/run/exp/run.sh` | запуск: `MODEL=altai run.sh <имя> [ПЕРЕМЕННАЯ=значение]` | | `~/run/exp/altai*/` | результаты прогонов altai и выгрузка весов | ## Следующие шаги - [ ] Исправить порог в самом обучении, а не только на проверке - [ ] Использовать колонки по-настоящему: развести 20 нейронов колонки, чтобы они учили разные варианты написания цифры - [ ] Запустить altai на GPU, когда заработает GPU-бэкенд