our_knp/reports/2026-10-12/Отчёт_MNIST_точность.md
2026-10-15 10:48:29 +03:00

94 lines
9.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Отчёт: почему KNP на MNIST отвечает только на 31% картинок
Oct 12, 2026
Сеть училась нормально, но на проверке у неё слишком высокий порог срабатывания. Если снизить порог только на этапе проверки, полный MNIST даёт 75,5% верных ответов вместо 30,9%; обучение при этом не меняется.
## Что было
В прошлом отчёте модель `blifat` после обучения на 60 000 картинках дала 3086 верных ответов из 10 000 (30,9%). Ошибалась сеть редко (77 раз), а на 6837 картинок не отвечала вовсе. Причину тогда не выясняли.
## Почему сеть молчала
- **Подсчёт метрик корректный.** По исходникам (`processor.cpp:77-82`): «верно» — сеть назвала правильную цифру, «ложное срабатывание» — назвала другую, «молчание» — на выходе не было ни одного спайка. Я разобрал лог спайков по всем 10 000 картинкам: сдвига ответов на соседнюю картинку нет, сеть действительно молчит на 68% картинок.
- **Порог застывает после обучения.** Входной нейрон срабатывает, когда сумма весов активных пикселей не меньше `8.571 + additional_threshold`. Добавка равна `0.218 × сумма всех положительных весов нейрона` (`blifat_stdp.h`). Обучение пересчитывает её при каждом спайке, а на проверке она застывает в последнем значении и переносится в обычный BLIFAT-нейрон.
- **Чем дольше обучение, тем выше порог.** Сумма положительных весов на нейрон растёт с 12,8 (3 000 картинок) до 18 (60 000), вместе с ней растёт порог, и молчаний становится больше.
- **Информации в весах хватает.** Если всегда брать колонку, ближе всех подошедшую к порогу, точность около 78% при любой длине обучения. Сеть «знает» ответ, но не дотягивает до порога.
- **Прошлое сравнение «3 000 лучше 60 000» было некорректным.** Пример берёт проверочные картинки сразу после обучающих, поэтому малый прогон проверялся на картинках 3000–3999 из обучающей части MNIST, а полный — на настоящем тестовом наборе. Для этого отчёта я сделал файлы, где после N обучающих картинок всегда идут одни и те же 10 000 тестовых.
## Результаты
Все прогоны на одних и тех же 10 000 тестовых картинках, однопоточный CPU-бэкенд.
| Обучение | Как было: верно / ошибки / молчание | Порог на проверке | Стало: верно / ошибки / молчание |
| --- | --- | --- | --- |
| 3 000 | 49,5% / 477 / 4570 | `act=6`, без добавки | **76,2%** / 1884 / 498 |
| 10 000 | 44,8% / 326 / 5198 | не проверялось | — |
| 30 000 | 42,0% / 260 / 5543 | `act=6`, без добавки | **73,3%** / 1091 / 1576 |
| 60 000 | 30,9% / 77 / 6837 | `act=6`, без добавки | 66,3% / 758 / 2614 |
| 60 000 | | `act=2`, без добавки | **75,5%** / 1369 / 1085 |
- Цена улучшения: ошибок становится больше (с 77 до 1369 на 60 000), потому что сеть теперь чаще отвечает и иногда ошибается.
- Лучший порог зависит от длины обучения. Для 60 000 я подобрал его на тех же тестовых картинках, поэтому 75,5% — немного оптимистичная оценка.
- Потолок для этих весов — около 77–78% (оценка симулятором, правило «всегда отвечать»).
### Полный MNIST с порогом `act=2`: результаты по цифрам
| Цифра | Верно | Не отвечено | Ложные срабатывания | Precision | Recall |
| --- | --- | --- | --- | --- | --- |
| 0 | 807 | 112 | 61 | 0,930 | 0,878 |
| 1 | 1032 | 4 | 99 | 0,912 | 0,996 |
| 2 | 706 | 207 | 119 | 0,856 | 0,773 |
| 3 | 722 | 124 | 164 | 0,815 | 0,853 |
| 4 | 740 | 40 | 202 | 0,786 | 0,949 |
| 5 | 600 | 165 | 127 | 0,825 | 0,784 |
| 6 | 758 | 110 | 90 | 0,894 | 0,873 |
| 7 | 719 | 90 | 219 | 0,767 | 0,889 |
| 8 | 693 | 179 | 102 | 0,872 | 0,795 |
| 9 | 769 | 54 | 186 | 0,805 | 0,934 |
| **Итого** | **7546 (75,5%)** | **1085** | **1369** | | |
## Что ещё всплыло
- **Колонка из 20 нейронов работает как один нейрон.** У всех 20 нейронов колонки идентичные веса (разброс 0,0), и на проверке они срабатывают одновременно. Во время обучения сигнал метки возбуждает всю колонку целиком.
- **Параметр `stochastic_stimulation` не используется.** Он задан в примере (2,212), но CPU-бэкенд его не читает.
- **Один прогон на 60 000 из пяти сломался.** Все веса схлопнулись к нулю, сеть молчала на всех картинках. Остальные четыре прогона совпали до картинки (3086 / 77 / 6837, как 7 октября). Причину не нашёл. Предположение (не проверено): случайный выбор победителя в WTA при ничьей, seed берётся из `std::random_device` (`wta.cpp:38`).
## Как проверял
1. Добавил в пример диагностическую выгрузку весов и порогов входного слоя после обучения.
2. Написал на numpy симулятор входного слоя по формулам бэкенда (затухание 2/3, задержка 1, порог, сброс). Он воспроизводит результаты KNP с точностью до нескольких картинок: например, 4954 / 476 / 4570 против 4953 / 477 / 4570 в KNP. На нём перебирал пороги за секунды.
3. Лучшие варианты подтвердил полными прогонами в KNP; все цифры в таблице результатов — из KNP.
## Что изменено
В `examples/mnist-learn/models/blifat/prepare_network_for_inference.cpp` добавлены переменные окружения. Без них пример работает как раньше; изменение видно в `git diff`, его можно откатить.
| Переменная | Что делает |
| --- | --- |
| `KNP_MNIST_THR` | заменяет порог `activation_threshold` входных нейронов на проверке |
| `KNP_MNIST_ADD_THR_SCALE` | умножает `additional_threshold` входных нейронов на проверке (0 — убрать) |
| `KNP_MNIST_DUMP=<папка>` | сохраняет `neurons.csv` (пороги) и `weights.csv` (веса) после обучения |
## Как запускать
```bash
cd ~/run/exp
# лучший вариант для полного MNIST
./run.sh myrun 60000 KNP_MNIST_THR=2 KNP_MNIST_ADD_THR_SCALE=0
cat myrun/result.csv
```
| Путь | Что внутри |
| --- | --- |
| `~/run/exp/tr<N>.bin`, `tr<N>.target` | N обучающих картинок + одни и те же 10 000 тестовых |
| `~/run/exp/run.sh` | запуск: `run.sh <имя> <N> [ПЕРЕМЕННАЯ=значение]` |
| `~/run/exp/<имя>/` | результат прогона и выгрузка весов |
| `~/anvenv` | Python с numpy для анализа |
## Следующие шаги
- [ ] Выбирать порог на отдельной части данных, а не на тесте, чтобы оценка была честной
- [ ] Исправить само обучение: выключить в нём добавку к порогу или не синхронизировать нейроны колонки
- [ ] Разобраться, почему один прогон на 60 000 схлопнулся
- [ ] Проверить модель `altai`: есть ли у неё та же проблема