Compare commits

..

2 commits

Author SHA1 Message Date
int
db4b1d08b8 ON/OFF input coding for mnist-learn: KNP_MNIST_INPUT_SIZE, dataset builder, simulator --pix
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:35:27 +03:00
int
979aa90bb4 Add 2026-10-14 handoff materials, verify accelerated runs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:29:08 +03:00
37 changed files with 16884 additions and 4 deletions

94
cats-dogs/ACCELERATION.md Normal file
View file

@ -0,0 +1,94 @@
# CPU и GPU: что ускорено
## Чистый Linux против WSL
По запросу пользователя проверена документация NVIDIA и Microsoft (2026-10-14). В WSL CUDA использует драйвер Windows через предоставляемую `libcuda.so`; Linux-драйвер NVIDIA внутри WSL устанавливать нельзя. На отдельном Linux можно выбирать совместимые версии драйвера, CUDA Toolkit и ядра независимо от Windows. Это преимущество для диагностики и воспроизводимых замеров, но не доказанное ускорение нашей сети.
NVIDIA указывает ограничения WSL для полной Unified Memory, одновременного доступа CPU/GPU к managed memory и доступного объёма pinned memory. Собственный прототип использует явные копирования. Перенос STDP написан в `gpu_stdp.cu` и `gpu_stdp_bridge.cpp`, но сборка, проверка состояний относительно CPU и измерение ускорения ещё не выполнены. Эти файлы нельзя считать рабочим GPU-бэкендом.
Microsoft рекомендует для Linux-инструментов хранить рабочие файлы в файловой системе Linux, а не `/mnt/c`. Расчётные данные и сборки нашего стенда уже расположены в `/home/int`; исходники проекта остаются в `/mnt/c/KNP`.
Редактирование драйвера не требуется для реализации AltAI/STDP: отсутствие поддерживаемых типов нейронов и пластичности исправляется в KNP. Возможный выигрыш Linux следует проверять одинаковым прогоном на той же видеокарте. Переустановка ОС или драйверов не выполнялась.
Источники: [NVIDIA CUDA on WSL](https://docs.nvidia.com/cuda/wsl-user-guide/index.html), [Microsoft: файловые системы WSL](https://learn.microsoft.com/en-us/windows/wsl/filesystems).
## Рабочий быстрый режим
Для этой небольшой AltAI/STDP-сети быстрее запускать **несколько независимых однопоточных процессов KNP**, каждый со своим набором параметров, данными состояния и выходной папкой. `run_batch.py` запускает ограниченное число процессов, собирает ошибки и метрики, не перезаписывает существующие прогоны. Python-потоки здесь только управляют отдельными C++-процессами; вычисления не ограничены Python GIL.
`validate_v2.py` теперь использует этот запуск, по умолчанию 4 параллельных эксперимента. Пример следующего поиска с новым префиксом:
```bash
python3 /mnt/c/KNP/cats-dogs/validate_v2.py --jobs 4 --prefix next_val
```
Старые результаты выбора не перезаписываются. Новый поиск сохраняет `next_val-selection.json`, план и сводку в `/home/int/run/cats-dogs/acceleration`. Входы/данные остаются одинаковыми для сравнимых запусков. Один временной ряд внутри одной сети этим способом не разбивается между процессами.
## Измерение пропускной способности
Четыре **одинаковые** конфигурации новой validation: 4000 train + 1000 validation, 20 нейронов на класс, пороги 600/1600/4000/8000. Сначала измерена последовательная группа, затем та же группа с четырьмя независимыми процессами.
| Режим группы | Общее время |
| --- | ---: |
| Последовательно | 91,573 с |
| 4 процесса | 42,911 с |
Ускорение **2,134 раза**. Каждый отдельный параллельный прогон занял примерно 40,7–42,9 с вместо 22–24 с в одиночку, но вся группа закончилась раньше. Для каждого порога четыре CSV весов/порогов и `result.csv` совпали побайтно между последовательным и параллельным запусками. Выигрыш относится к группе независимых экспериментов, не к одной сети. Замер однократный; Windows/WSL и другие нагрузки могут менять время.
Результат: [acceleration-results/batch-benchmark.json](acceleration-results/batch-benchmark.json). Код измерения — `benchmark_batches.py`; повторный запуск требует новых имён, поскольку данные предыдущего замера защищены от перезаписи.
## Многопоточная обработка одной сети
В установленном KNP штатный `MultiThreadedCPUBackend::SupportedNeurons` содержал BLIFAT, STDP-BLIFAT и LIF, **но не AltAI**. Прямой запуск нашей сети завершался `Type is not supported`. Параметр количества потоков этого ограничения не исправляет.
Создана **отдельная** экспериментальная библиотека:
`/home/int/run/cats-dogs/acceleration/libknp-cpu-tuned-backend.so`
`build_tuned_cpu.py` копирует заголовок/реализацию многопоточного бэкенда в каталог эксперимента, добавляет `AltAILIF` и `SynapticResourceSTDPAltAILIFNeuron` к списку типов, использует существующие CPU-функции AltAI и STDP. Перекомпилируются **обе** единицы `backend.cpp` и `get_network.cpp`, чтобы их представления variant совпадали. Добавлены переменные фабрики:
- `KNP_CPU_THREADS`: по умолчанию 2.
- `KNP_CPU_POPULATION_PART`: по умолчанию 64.
- `KNP_CPU_PROJECTION_PART`: по умолчанию 8192.
Значения проверяются на корректность и границы. Штатные исходники и библиотеки KNP не заменялись. Патч для просмотра — [acceleration-results/cpu-altai-multithread.patch](acceleration-results/cpu-altai-multithread.patch).
```bash
python3 /mnt/c/KNP/cats-dogs/build_tuned_cpu.py
python3 /mnt/c/KNP/cats-dogs/run.py my_tuned_run \
--data-dir /home/int/run/cats-dogs/features-v2 \
--dataset sparse_hog_validation --train 4000 --count 1000 \
--quant weights --threshold 4000 --backend tuned --threads 2
```
На одинаковом коротком прогоне (800 train + 200 validation):
| Бэкенд | Время KNP train+inference | Веса и результат |
| --- | ---: | --- |
| Однопоточный | 4,89 с | эталон |
| Новый, 2 потока | 15,09 с | побайтно совпали |
| Новый, 4 потока | 15,69 с | побайтно совпали |
Поэтому `run.py` оставляет **однопоточный бэкенд по умолчанию**, а параллелизм для поиска организован между независимыми сетями. Наличие нескольких потоков само по себе не означает ускорения. В частности, в исходном многопоточном коде финализация STDP ставится в очередь одной задачей на популяцию, а у текущей сети одна небольшая обучаемая популяция; есть также барьеры между фазами каждого шага. Это кандидаты для дальнейшей оптимизации, не измеренная разбивка времени профилировщиком.
Короткий замер: [acceleration-results/backend-altai-benchmark.json](acceleration-results/backend-altai-benchmark.json). Полный валидационный прогон экспериментального бэкенда проверяется отдельно; результат будет добавлен после завершения.
## Видеокарта и CUDA
Стенд видит Intel i7-13700KF, 24 логических CPU в WSL, NVIDIA RTX 4070 Ti SUPER (compute capability 8.9, около 16 ГБ VRAM). Драйвер, сообщённый `nvidia-smi`: 610.88; установленный CUDA Toolkit: 12.9. Эти значения не следует смешивать с максимальной версией CUDA API, показанной драйвером.
Базовое CUDA-ядро `/home/int/scratch-cu/t2` успешно записало и прочитало число 42: `no error h=42`. Это подтверждает работу базовых CUDA-вычислений в WSL, не всего GPU-бэкенда KNP.
В установленном исходнике `gpu-cuda/backend.h`:
```cpp
using SupportedNeurons = boost::mp11::mp_list<knp::neuron_traits::BLIFATNeuron>;
using SupportedSynapses = boost::mp11::mp_list<knp::synapse_traits::DeltaSynapse>;
bool plasticity_supported() const override { return false; }
```
Следовательно, **GPU-бэкенд не умеет нашу модель AltAI и не выполняет STDP-обучение**. Исправление архитектуры компиляции не добавляет эти функции. Бинарник уже содержит `sm_89` cubin, что проверено `cuobjdump`.
Кроме отсутствия модели, простой BLIFAT-пример через GPU-бэкенд воспроизвёл `invalid device function` в коде CUDA-векторов KNP. Лог — [acceleration-results/cuda-knp-probe.log](acceleration-results/cuda-knp-probe.log). Пример при этом может завершиться с кодом 0, поскольку ошибки только журналируются; это не успешное выполнение сети. Причина этой ошибки не установлена и не исправлена.
Чтобы обучать именно эту AltAI/STDP-сеть на GPU, нужны реализация её состояния, правил пластичности и обмена сообщениями в CUDA и проверка против CPU. Это отдельная доработка бэкенда; **GPU-обучение в этой работе не включено**. Текущий доступный выигрыш — проверенный параллельный поиск на CPU. Переход на другую модель нейрона или другое обучение должен оформляться как отдельный эксперимент.

250
cats-dogs/HANDOFF.md Normal file
View file

@ -0,0 +1,250 @@
# Передача проекта KNP / AltAI следующему чату
**Последнее решение пользователя:** отменить мониторинг/автоматический перенос и выдать ZIP. Монитор остановлен. Дальнейшие автоматические подключения не выполняются. В архиве все доступные локальные материалы; новых полных копий удалённого runtime нет. Старые разделы о мониторе описывают историю, а не действующий процесс.
Срез: 2026-10-14, после обсуждения CPU/GPU-ускорения и преимуществ чистого Linux. Это документ состояния, а не заявление, что все начатые изменения закончены. Для восстановления прочитать этот файл, `ACCELERATION.md`, `V2.md`, `README.md` данного эксперимента и ежедневный журнал. Фактический снимок стенда — `handoff-state/remote-inventory.txt`; он фиксирует состояние на момент сохранения и со временем устареет.
**СТАТУС СОХРАНЕНИЯ:** локальная передача создана. После успешного начального осмотра стенд перестал отвечать на SSH (тайм-аут подключения). Поэтому перечисленные ниже новые remote-runtime/prior-context архивы и новые копии передачи на станции пока являются **планируемыми**, а не подтверждённо сохранёнными. Ранее существовавшие source-snapshot/trained-weights и results/results-v2 уже есть локально. Истина о новом переносе — `preservation-status.json`; автоматическое завершение после восстановления связи: `python3 /opt/neiro/experiments/cats-dogs/save_handoff.py`. До успешного статуса этого скрипта не утверждать, что новый пакет находится на станции. remote-inventory/patch/flags также создаются этим скриптом при доступном SSH.
## 1. Запросы пользователя и принятые решения
- Постоянный стенд: `int@10.9.8.230`, WSL; основная папка `/mnt/c/KNP`. Подключаться только по запросу пользователя. Этот запрос разрешает сохранение и сверку на стенде; будущие учебные вопросы сами по себе не разрешают SSH.
- Пользователь хочет обучать **именно спайковую сеть KNP/AltAI**, даже при скромной точности. Не подменять её готовой CNN или предобученным классификатором.
- Задача первого объектного опыта — кошки против собак. Это бинарная классификация маленьких изображений, не детекция объектов с координатами и не универсальное распознавание фотографий.
- Следующие требования: улучшить распознавание; использовать несколько ядер и мощную GPU; реализовать backend, если возможно, иначе объяснить ограничения в отчёте. GPU-часть пока незавершена.
- Пользователь требует вести ежедневный журнал: коротко об обсуждении, подробно об изменениях/запусках. Не записывать пароли и приватные ключи.
- Последний вопрос: даст ли чистый Linux преимущество. Ответ: больше контроля над драйвером/ядром/CUDA и меньше некоторых ограничений WSL; выигрыш скорости не измерен. ОС и драйверы не менялись.
- Не рисовать на общей доске без просьбы пользователя. Соблюдать activity_start/list/update/finish текущего T3-инстанса при долгой работе. Проверять чужие claims перед редактированием.
## 2. Быстрая карта путей
| Назначение | Путь |
| --- | --- |
| Локальная учебная библиотека | `/opt/neiro` |
| Локальные правила | `/opt/neiro/AGENTS.md` |
| Точка входа для следующего чата | `/opt/neiro/HANDOFF.md` |
| Эта передача, скрипты и артефакты | `/opt/neiro/experiments/cats-dogs` |
| Ежедневный журнал | `/opt/neiro/changes-2026-10-14.md` |
| Снимок состояния и резервные архивы | `/opt/neiro/experiments/cats-dogs/handoff-state` |
| Постоянный SSH | `int@10.9.8.230` |
| Windows-папка проекта, видимая в WSL | `/mnt/c/KNP` (обычно `C:\KNP` на Windows) |
| Передача на рабочей станции | `/mnt/c/KNP/HANDOFF-2026-10-14.md` |
| Копия всей папки эксперимента на станции | `/mnt/c/KNP/cats-dogs` |
| Исходный репозиторий KNP с прежними правками | `/mnt/c/KNP/examples` |
| Отдельная копия MNIST-примера для кошек/собак | `/mnt/c/KNP/cats-dogs/source` |
| Основная рабочая сборка KNP | `/home/int/knp-build` |
| Внешние библиотеки | `/home/int/deps/lib` |
| Все данные/прогоны объектного опыта | `/home/int/run/cats-dogs` |
| Отдельный бинарник классификатора | `/home/int/run/cats-dogs/build/cats_dogs` |
| Старые MNIST-прогоны | `/home/int/run/exp` |
| Визуализатор цифр | `/mnt/c/KNP/viz` |
| Старые отчёты | `/mnt/c/KNP/Отчёт_*.md` |
| Память предыдущего Claude Code | `/home/int/.claude/projects/-mnt-c-knp/memory` и `/home/int/.claude/projects/-mnt-c-KNP/memory` |
SSH с текущей машины работает с явным ключом:
```bash
ssh -i ~/.ssh/id_ed25519_snw -o IdentitiesOnly=yes -o BatchMode=yes int@10.9.8.230
```
Обычный SSH без `-i` прежде давал отказ. `ssh-copy-id` проверялся: публичный ключ уже был установлен, повторно добавлять не потребовалось. Ключи не входят в резервные архивы.
## 3. Окружение и состояние процессов
- KNP в `/mnt/c/KNP/examples/VERSION`: `2.0.0`. HEAD на момент передачи: `55470ed622cb29efd0c04fcad1b12e6bac9da00d`. Используется сборка с экспериментальными правками, не чистый релизный пример.
- Машина WSL `1220WS2`, Ubuntu 26.04; ядро при проверке `6.18.54.1-microsoft-standard-WSL2`.
- Intel i7-13700KF, `nproc` показывал 24 логических CPU.
- GPU: NVIDIA GeForce RTX 4070 Ti SUPER, compute capability 8.9, `nvidia-smi` показывает 16376 MiB и драйвер 610.88. Это снимок, не требование к другой машине.
- CUDA Toolkit `/home/int/cuda-12.9`; nvcc `/home/int/cuda-12.9/bin/nvcc`. Для nvcc использовался совместимый host compiler `/home/int/mamba/envs/gcc14/bin/x86_64-conda-linux-gnu-g++`. CPU-сборка использует `/usr/bin/c++`; путь для сохранения реальных флагов — `handoff-state/compiler-flags.txt`.
- Python-среда обработки изображений `/home/int/anvenv/bin/python`: Python 3.14.4, numpy 2.5.4, Pillow 12.3.0. Pip 25.1.1 установлен через ensurepip. torch/torchvision для этих опытов не требуются.
- GPU-инструмент WSL: `/usr/lib/wsl/lib/nvidia-smi`. CUDA-тест `/home/int/scratch-cu/t2` при `LD_LIBRARY_PATH=/home/int/cuda-12.9/lib64:/usr/lib/wsl/lib` выдавал `no error h=42`.
- На момент передачи **нет запущенных обучений/сборок cats_dogs**. Единственный обнаруженный процесс из этой работы — HTTP-сервер визуализатора, PID 3671; PID не считать постоянным.
- Автозапуск обучения или HTTP-сервера не создавался. Перезапуск WSL может остановить визуализатор.
## 4. Прежняя работа и визуализатор MNIST
При первом осмотре уже были изменения в шести файлах исходного KNP. Их сегодня не редактировали и не откатывали:
```text
examples/mnist-learn/models/altai/construct_network.cpp
examples/mnist-learn/models/altai/hyperparameters.h
examples/mnist-learn/models/altai/prepare_network_for_inference.cpp
examples/mnist-learn/models/blifat/prepare_network_for_inference.cpp
knp/backends/gpu/cuda-backend/CMakeLists.txt
knp/base-framework/impl/model_loader.cpp
```
После успешного save_handoff.py незакоммиченный diff и HEAD будут сохранены в `handoff-state/original-knp-changes.patch` и `original-knp-status.txt`. После восстановления связи старые отчёты и обе папки памяти Claude будут сохранены в `prior-context-and-source.tar.gz`. Они содержат предыдущий контекст и не заменяют новые проверки. Из прежних отчётов известны изменения колонок/торможения и исправление каналов ModelLoader; повторно весь старый MNIST-бенчмарк не воспроизводился. У старых `sz80_30k_s1`, `sz80_30k_s42`, `sz80_60k_s42`, `sz40_60k_s42` в `/home/int/run/exp` при первом осмотре были пустые `result.csv`; не выдавать их за успешные результаты.
По просьбе пользователя запущен визуализатор:
```bash
nohup python3 -m http.server 8080 --bind 0.0.0.0 \
--directory /mnt/c/KNP/viz \
> /tmp/knp-viz-http.log 2>&1 < /dev/null &
```
URL `http://10.9.8.230:8080/altai_viz.html`; в браузере Windows пользователь открывал `http://127.0.0.1:8080/altai_viz.html`. Перед повторным запуском проверить существующий сервер/порт, не создавать второй процесс. При передаче HTTP вернул 200.
Исправлена кириллица: добавлен `<meta charset="utf-8">` в `/mnt/c/KNP/viz/altai_viz.html` и `app.html`. Сервер отдаёт `text/html` без charset, но объявление есть в документе. Результат визуально виден на следующем скриншоте пользователя. HTML и diff включены в план резервирования; см. статус сохранения в начале файла.
Наблюдение пользователя: нарисованная 4 на шаге 9/14 давала ответ 5 с подписью «пока лидирует». Это **промежуточный результат**, финальные шаги не проверены. Скриншот локально `/opt/neiro/reports/observations/2026-10-14-drawn-4-leading-5.png`; копия для передачи — `handoff-state/observations/`. По этому наблюдению сеть цифр не переобучали. Объектный визуализатор кошек/собак не создавался.
В `viz` обнаружены также untracked `.python-version`, `README.md`, `pyproject.toml`, `uv.lock`, `s -lntp`; их происхождение в этой работе не установлено. Не приписывать их своим изменениям и не удалять. Snapshot status включён в план резервирования; архив контекста сохраняет только нужные два HTML, не всю папку `viz`.
## 5. Объектный эксперимент v1 — завершён
Скопирован MNIST-пример с прежними AltAI-изменениями в отдельный `cats-dogs/source`. В C++ для этого опыта изменены только константы `global_config.h`: классов 2 вместо 10, входов 2048 вместо 784. Лицензия Apache 2.0 сохранена. Исходный MNIST и штатные библиотеки не заменялись.
2048 входов, 20 обучаемых нейронов на класс, локальное STDP в KNP/AltAI, случайный разброс весов 20, seed 42. Подготовка входов обычная фиксированная, не нейросеть. Классы в CSV: 0=cat, 1=dog.
Данные: JPEG-зеркало `https://github.com/YoongiKim/CIFAR-10-images`, commit `d7d05618061fe37d830fb0419063d113c575ccbc`. Это преобразованные JPEG, **не оригинальные байты CIFAR-10** и не официальный CIFAR-бенчмарк. Train 800, validation 200, test 400; баланс классов. Имена файлов, seed и SHA-256 — в `data/manifest.json`, локальная копия `data-manifest.json`.
Данные и кэш на стенде `/home/int/run/cats-dogs/data`: `github-jpeg-cache`, `*-original.npz`, `.bin`, `.target`. Исходная загрузка официального архива была остановлена; `cifar-10-binary.tar.gz` и `parts` могут быть частичными. Hugging Face превью получали HTTP 429; они не использовались в финальном опыте. `download.py`, `prepare_data.py`, `prepare_preview.py`, `fetch_cache.py`, `preview-cache` сохраняют историю попыток, не являются текущим выбранным путём.
Первый яркостный ON/OFF-вход оказался слабым. Выбран контраст: серое изображение минус размытие 5×5, масштаб 4, положительный/отрицательный каналы. Порог 60 выбран из 10 фиксированных конфигураций по настоящей KNP-validation: 111/200 (55,5%). Итоговый `test`: 208/400 (52%), 183 ошибки, 9 молчаний. Интервал Уилсона 95% приблизительно 47,1–56,9%; превосходство над 50% не установлено.
`tune.py` — **неуспешный офлайн-симулятор**: расходился с KNP, его подбор не использован. Причина отдельно не исправлена, в том числе начальное состояние растра не учитывает предварительное кодирование train. Не использовать как эталон.
Артефакты: `README.md`, `results/selection.json`, `results/test-metrics.json`, `results/test-settings.json`, `results/reproducibility.json`, `source-snapshot.tar.gz`, `trained-weights.tar.gz`. На стенде прогоны `val_*`, `validation`, `test`; выбор `selection.json`, проверка `reproducibility.json`. Четыре CSV весов/порогов train между validation и test побайтно совпали.
## 6. Улучшенный эксперимент v2 — завершён
Фиксированные HOG-подобные признаки в `features.py`: sqrt RGB, выбирается цветовой канал с наибольшим градиентом, 9 направлений, ячейки 4×4, блоки 2×2, L2-Hys. 1764 признака; оставляются 256 сильнейших, дополнение до 2048. Это собственная реализация, не заявление о битовой совместимости со scikit-image. Метки не участвуют в предобработке. Обучение весов классификатора остаётся реальным KNP/STDP; извлечение признаков **не спайковое**.
Плотные HOG и HOG+цвет проверялись на старой validation как диагностика. После обычного квантования у `probe_hog_20` 65626/81920 весов нулевые. Режим `KNP_MNIST_QUANT=weights` масштабирует по весам и уменьшает потерю малых весов. Веса в ±255; пороги подбирались в другом масштабе. Переносимость порогов на физический AltAI определённого поколения не проверена.
Новые JPEG-разделения, прежний источник/commit, seed перемешивания 20261015:
- Train 4000: по 2000 cat/dog, исходный train 0000–1999.
- Новая validation 1000: по 500, исходный train 4500–4999.
- Новый test 1000: по 500, исходный test 0200–0699; старый test был 0000–0199.
- Проверены уникальность, баланс, отсутствие пересечений. Настройки выбирали по validation, не по test.
Пути: `/home/int/run/cats-dogs/data-v2` — исходные NPZ и manifest; `features-v2` — hog/hybrid/sparse_hog `.bin`/`.target` и манифесты; `pilot-features` — диагностические признаки старого пилота. Входной `*_validation.bin` содержит train, затем validation; `*_train_test.bin` содержит train, затем test. `run.py --train` и `--count` задают границу обучения и длину оценки. Не перепутать с отдельным `*_val.bin`.
8 заранее заданных конфигураций: колонки 20/60 × пороги 600/1600/4000/8000, sparse_hog, quant=weights, seed42. Выбран `v2_val_c20_t4000`: 626/1000 (62,6%). Новый `v2_test`: 618/1000 (61,8%), 382 ошибки, 0 молчаний; cats 304/500, dogs 314/500. Прежняя сеть на **том же новом test** (`v1_fresh_test`): 522/1000 (52,2%), 450 ошибок, 28 молчаний. Прирост 9,6 п.п.; интервалы 58,7–64,8% и 49,1–55,3%. Это небольшой JPEG-набор и один seed, не гарантированное распознавание любых фотографий.
Полная конфигурация новой модели: train4000, count1000, columns20, threshold4000, seed42, jitter20, quant weights; `KNP_MNIST_ADD_THR_SCALE=0`, `KNP_MNIST_INFER_INHIB=255`. Train+test новой 23,27 с, прежней 5,55 с в тех замерах. CSV весов/нейронов повторного обучения побайтно совпадают с выбранной validation. Test уже просмотрен; дальнейшие улучшения нельзя честно настраивать по нему и снова называть независимой тестовой оценкой.
Артефакты локально: `V2.md`, `results-v2/selection.json`, `comparison.json`, `test-settings.json`, `data-manifest.json`, `features-manifest.json`, `probes.json`, `trained-weights-v2.tar.gz`. Полные прогоны на стенде `v2_val_*`, `v2_test`, `v1_fresh_test`, файлы `selection-v2.json`, `comparison-v2.json`, `probes-v2.json`; также в резервном runtime-архиве. Файлы `.bin`/`.target` — входы для C++, а CSV выгрузки не являются готовой реализацией API загрузки модели.
## 7. Ускорение CPU — что проверено
### Независимые процессы: работает
`run_batch.py` запускает ограниченное число независимых C++-процессов, Python-потоки только управляют ими. Нет общей изменяемой сети и зависимости от Python GIL. В плане JSON нужны уникальное безопасное `name` и список строк `args`; обязательна отдельная сводка. Существующие прогоны защищены от перезаписи; ошибки возвращаются как неуспех.
`validate_v2.py` сейчас имеет `--jobs` (по умолчанию 4) и `--prefix`; стабильный выбор при равной accuracy идёт по заранее заданному порядку. Старые selection не перезаписывать. `benchmark_batches.py`: одна и та же группа из четырёх порогов, каждая train4000+validation1000, последовательно 91,573 с, 4 процесса 42,911 с (2,134× пропускная способность). Все четыре CSV весов/нейронов и `result.csv` совпали побайтно. Один отдельный процесс в группе стал медленнее из-за конкуренции: это ускорение группы, не одного временного ряда.
Стенд: `bench_serial_t*`, `bench_parallel_t*`, `/home/int/run/cats-dogs/acceleration/batch-benchmark.json`, планы/сводки. Локально `acceleration-results/batch-benchmark.json`.
### Многопоточность одной сети: поддержка добавлена, но медленнее
Штатный multi backend содержит BLIFAT/STDP-BLIFAT/LIF, не AltAI; прямой запуск даёт `Type is not supported`. `run.py --backend multi` теперь отвергается до создания папки.
`build_tuned_cpu.py` делает отдельную копию header/backend и добавляет AltAILIF + ResourceSTDPAltAI в поддерживаемые типы, env-параметры потоков/размеров порций. Обязательно компилирует backend.cpp **и get_network.cpp**, иначе несовместимы variants разных списков типов. Это экспериментальная сборка, не штатная поддержка релиза.
Библиотека `/home/int/run/cats-dogs/acceleration/libknp-cpu-tuned-backend.so`; headers в `acceleration/include`; `KNP_CPU_THREADS` по умолчанию2, допустимо1..256; `KNP_CPU_POPULATION_PART`64, `KNP_CPU_PROJECTION_PART`8192. Оригинальные .so не заменены.
Короткие одинаковые 800+200: single4,89 с, tuned2 15,09 с, tuned4 15,69 с. Проверена идентичность весов/ответа; масштаб проверки скрипта `benchmark_backends.py` — float_weights, quant_weights, result.csv. Полный `backend_altai_full` 4000+1000 завершён за80,77 с, результат626/1000. **Его полная побайтная сверка с `v2_val_c20_t4000` ещё не выполнена.** Не объявлять одной accuracy доказательством полной эквивалентности.
Первый `backend_bench_*` был неуспешной проверкой до добавления типов; последующие `backend_altai_*` после исправления. Локально `acceleration-results/backend-altai-benchmark.json`, `cpu-altai-multithread.patch`; на стенде исходники/логи/JSON в acceleration и корне runtime. У небольшой сети STDP ставится одной задачей на популяцию, плюс барьеры между фазами; это кандидаты горячих участков, не завершённый профиль по времени.
### Кэш подключений: собран, проверка неполная
`perf_connection_cache.h` кэширует подключённые ссылки в прежнем порядке, thread_local, ключ projection/neuron; отслеживает адрес/размер/хранилище проекций и сбрасывается при `_init`. Это только фиксированная топология. Переподключение с прежним размером/адресом не обнаруживается; нельзя автоматически переносить на динамическую сеть.
`build_fast_cpu.py` копирует **всё дерево CPU-library/include**, чтобы quoted includes взяли изменённые копии, заменяет поиск связей кэшем, использует const references вместо копий vector. Новая библиотека `/home/int/run/cats-dogs/fast-cpu/libknp-cpu-fast-backend.so`; оригинальные файлы не редактируются. `fast_smoke` 800+200 завершён за4,39 с и123/200, как эталон. **Побайтная проверка CSV и полноценный замер 4000+1000 ещё не завершены.** По умолчанию остаётся single.
## 8. GPU-бэкенд — незавершённый прототип
Штатный `/home/int/knp-build/bin/libknp-gpu-cuda-backend.so` поддерживает только BLIFATNeuron и DeltaSynapse, `plasticity_supported=false`. Не умеет AltAI/STDP. `cuobjdump` подтвердил sm89; проблема не сводится к отсутствию архитектуры RTX в бинарнике. Simple-network `/home/int/gputest/simple-network` игнорировал переданный CLI-backend и использовал библиотеку через sibling symlink; этот symlink указывал на GPU lib. В GPU-коде CUDA-векторов воспроизведён `invalid device function`. Ошибка лишь журналируется, процесс может вернуть0. Это не успешная сеть. Лог `acceleration-results/cuda-knp-probe.log` и runtime `cuda-knp-probe.log`; причина не исправлена.
Запрошено второе мнение GPT-6 Astra через skill astra-advisor: узкий перенос математически возможен; отсутствие модели в штатном backend не означает невозможность GPU на этом железе. Совет — собственные плоские CUDA Runtime буферы, точный порядок фаз/сумм, поэтапная проверка всех состояний. Маленькая сеть может не ускориться из-за host overhead. Гибридный этап допустим, но не выдавать его за полный GPU-backend.
Написаны четыре **не собранных и не проверенных** файла в папке эксперимента, теперь также сохраняемые на рабочей станции:
- `gpu_stdp_types.h`: POD-состояния нейрона/синапса и интерфейс create/step/download/destroy.
- `gpu_stdp.cu`: CUDA Runtime, свои malloc/copy/check errors; GPU-расчёт STDP, dopamine, ISI, renormalization; один thread на postsynaptic neuron, последовательный порядок его связей; ресурсы на GPU постоянные.
- `gpu_stdp_bridge.h`: контракт train(Population, Projections, SpikeMessage, Step, CPU-reference), flush, clear.
- `gpu_stdp_bridge.cpp`: thread_local контексты фиксированной топологии; packing/unpacking; last_spike_step авторитетно обновляется CPU-проекциями; веса возвращаются CPU; flush выгружает ресурсы перед экспортом сети.
Это **гибридный замысел**: нейродинамика, проекции и message bus остаются CPU; пластичность переносится на GPU. На активном шаге копируются нейроны и markers всех ~80k связей H2D, нейроны/веса D2H; копии pageable. Низкая GPU-загрузка/замедление возможны. Отсутствие активного STDP должно пропускаться, но это условие ещё надо сверить с эталоном.
`KNP_CUDA_VERIFY` в bridge проверяет CPU-reference по шагам: float tolerance1e-4 +1e-6 relative, timestamps/flags точно, на flush печатает `CUDA_STDP gpu_steps=... checked_steps=... max_state_abs_error=...`. Сейчас наличие env включает проверку, даже значение0. **Механизм проверки сам не проверен и не подключён к backend!** `run.py` пока не имеет gpu/backend или --cuda-verify. Нет `build_gpu_stdp.py`, GPU .so и результатов GPU-обучения.
Риски для проверки: точный float/double и порядок сумм additional_threshold/free_resource; unsigned time subtraction (в CPU есть непривычные выражения — не исправлять одновременно с портом); ISI-статусы forced/start/continue/not-period; отрицательный dopamine при forced; renormalization/drain; flags contributed/hebbian; задержки и несколько входных проекций; экспорт train→infer. У кэша/bridge фиксированная топология, повторное создание/уничтожение сети должно очищать stale refs.
## 9. Конкретная точка продолжения GPU
Ниже **план, ещё не выполненные действия**. Не утверждать, что эти команды уже запускались.
1. Прочитать CPU-эталон в `/mnt/c/KNP/examples/knp/backends/cpu/cpu-library/include/knp/backends/cpu-library/impl/populations/altai/altai_stdp.h` и `training/stdp.h`, соответствующие neuron/synapse traits. Сверить draft с реальным порядком вызовов.
2. Создать `build_gpu_stdp.py`, изолированное дерево `/home/int/run/cats-dogs/gpu-stdp`; скопировать целиком готовое fast-cpu/include, не менять оригиналы.
3. В копии altai_stdp.h переименовать исходное train_population_impl в train_population_cpu_reference; добавить wrapper старой сигнатуры, вызывающий bridge с reference callback. Include bridge вне namespace. Не допустить рекурсии.
4. В копии single backend `_init` добавить clear GPU-контекстов и кэша. В копии get_network.cpp перед `get_network_data()` добавить flush. Компилировать backend.cpp, get_network.cpp, bridge.cpp и CUDA-object, линковать по рабочему link.txt, но отдельным SONAME.
5. Предполагаемые CUDA-флаги: `-std=c++17 -arch=sm_89 --fmad=false --prec-div=true --prec-sqrt=true -O3 -Xcompiler=-fPIC`; host compiler gcc14 указан выше. Не включать fast-math при проверке. Link `-L/home/int/cuda-12.9/lib64 -lcudart` и rpath на этот lib64. Отдельный ожидаемый output `gpu-stdp/libknp-altai-gpu-stdp-backend.so` пока **не существует**.
6. Добавить явный experimental gpu-вариант в run.py только после успешной сборки; сделать управление verify env, не менять single default.
7. Маленький train+infer с пошага́вой CPU-reference; затем800+200 и4000+1000. Сравнить все CSV, состояния, спайки, delays, flags, метрики. Дополнить граничными фикстурами ISI/dopamine/несколько проекций. Итоговая accuracy сама по себе недостаточна.
8. Раздельно измерить без verify kernel/копии/host overhead. Если медленнее — честно сохранить результат; для большого выигрыша понадобятся device-resident нейроны/проекции/очереди и/или batching независимых сетей, а не смена драйвера вслепую.
Параллельно допустимо завершить сверку `backend_altai_full` и `fast_smoke` с эталонами. Это дешёвая проверка существующих файлов, не новое обучение. После этого изменить статус ограничений в ACCELERATION.md и журнале.
## 10. Команды проверенного пути и ограничения повторного запуска
Текущие run.py defaults — старые8000train/2000count/threshold60/data, **не** лучшая v2-конфигурация. Всегда явно задавать нужные параметры. `out.mkdir(exist_ok=False)` защищает старые результаты; взять новое имя. Повторное evaluate_v2.py с прежними именами намеренно откажет, не удалять исходные прогоны ради повторения.
Пример следующего обычного контрольного прогона v2 на validation (выполнять только когда пользователь разрешил продолжение):
```bash
python3 /mnt/c/KNP/cats-dogs/run.py resume_check_01 \
--data-dir /home/int/run/cats-dogs/features-v2 \
--dataset sparse_hog_validation --train 4000 --count 1000 \
--threshold 4000 --columns 20 --seed 42 --jitter 20 \
--quant weights --backend single
```
Новый параллельный поиск:
```bash
python3 /mnt/c/KNP/cats-dogs/validate_v2.py --jobs 4 --prefix resume_val_01
```
Он повторяет прежнюю сетку, не является новым способом обучения. Не запускать контрольный timing одновременно с поиском. Метрики `correct/wrong/unanswered` определяются по счётчикам KNP, а не по печатаемой им округлённой precision. Обучение каждый раз запускается заново; отдельный режим только загрузки готовой сети пока не реализован.
Сборка копии примера: `python3 /mnt/c/KNP/cats-dogs/build.py`. Сборки экспериментальных библиотек: `build_tuned_cpu.py`, `build_fast_cpu.py` — работают по абсолютным путям существующей сборки. Для другой машины сначала настроить исходники/библиотеки, архив runtime сам по себе не заменяет установленную KNP toolchain.
Подготовка изображений использует `/home/int/anvenv/bin/python`; orchestration/run — python3. Скрипты подготовки могут создавать/перезаписывать данные в указанной папке, не запускать их автоматически поверх исходных манифестов. Тест признаков: `/home/int/anvenv/bin/python /mnt/c/KNP/cats-dogs/test_features.py`.
## 11. Что осталось по качеству распознавания
61,8% улучшает пилот, но недостаточно. Пока только один seed. Возможные отдельные опыты: аугментации **только train** (зеркало, сдвиг), несколько seed, обучаемые локальные спайковые признаки + pooling, другое SNN-обучение с явным согласованием отличий от нынешнего STDP. Не смешивать алгоритмическое улучшение с GPU-портом — сначала воспроизвести математику. Для нового окончательного результата требуется независимый новый test/оговорённый протокол, поскольку нынешний уже просмотрен.
Физический чип AltAI не использовался; эмуляция KNP не доказывает аппаратную совместимость всех выбранных порогов. Различать поколения AltAI и версии KNP. Учебная библиотека `/opt/neiro` — публичный снимок на2026-10-05; для занятий прочитать README/INDEX и соответствующие локальные документы, не запускать скачанные установщики без запроса.
## 12. Linux, сохранение и восстановление
В WSL CUDA использует драйвер Windows; Linux NVIDIA-драйвер внутри WSL не устанавливать. Native Linux позволяет независимо выбирать совместимые драйвер/ядро/Toolkit. NVIDIA указывает ограничения managed/pinned memory в WSL; это не доказанное узкое место нашей реализации. Основной блокер — отсутствующий AltAI/STDP GPU-backend. Данные/сборки уже в `/home/int`, исходники на `/mnt/c`; Microsoft рекомендует Linux-файловую систему для Linux-инструментов. Замер WSL/native Linux не проводился. Источники: https://docs.nvidia.com/cuda/wsl-user-guide/index.html и https://learn.microsoft.com/en-us/windows/wsl/filesystems.
После успешного запуска save_handoff.py, `handoff-state/remote-runtime.tar.gz` будет полным снимком `/home/int/run/cats-dogs`, включая подготовленные данные, JPEG-кэш, все прогоны, логи, экспериментальные headers/objects/libs и частичные неиспользованные загрузки. Он не включает весь `/home/int/knp-build`, `/home/int/deps`, venv, CUDA Toolkit, SSH-ключи или весь прежний MNIST runtime. Частичные файлы внутри архива не становятся валидными данными от факта резервирования.
После успешного сохранения, `handoff-state/prior-context-and-source.tar.gz` будет отдельной копией адаптированного C++ source, двух HTML визуализатора, четырёх старых отчётов и обеих папок памяти Claude. Известные оригинальные незакоммиченные правки KNP будут в отдельном patch. Полный git checkout основного KNP остаётся на стенде; локальная учебная копия `/opt/neiro/sources/knp` может отличаться, не считать её автоматически рабочей сборкой.
Резервные архивы предназначены для восстановления в **новую** папку. Пример:
```bash
mkdir -p /home/int/restore-cats-dogs-2026-10-14
tar -xzf /mnt/c/KNP/cats-dogs/handoff-state/remote-runtime.tar.gz \
-C /home/int/restore-cats-dogs-2026-10-14
```
После распаковки абсолютные пути в settings/scripts будут указывать на оригинальный runtime; их надо осознанно адаптировать, а не запускать вслепую. `SHA256SUMS` и `preservation-manifest.json` в корне папки эксперимента проверяют локальную и сохранённую на станции копии. Манифест содержит перечень и хеши файлов; себя, SHA256SUMS и изменяемый preservation-status.json не включает. Наличие локальных контрольных сумм не доказывает выполненный перенос; см. preservation-status.json.
Сохранение по последнему запросу не меняло модель, алгоритмы или драйверы и не запускало обучение. Состояние незавершённых GPU/fast-проверок намеренно сохранено как незавершённое. Коммитов/PR этой работы не создавали. Перед продолжением проверить актуальные процессы и файлы: снимок не заменяет реального состояния машины.
## Дополнение: автоматическая доставка после восстановления SSH
По явному запросу пользователя настроен `monitor_handoff.py`. Проверки каждые30с, после доступа запускается `save_handoff.py`: имеющийся локальный пакет отправляется первым, затем полный runtime/context backup и дополненный пакет. Монитор завершается только после успешного переноса с SHA256-проверкой. Повторные экземпляры блокируются flock. Лог `/opt/neiro/.handoff-monitor/monitor.log`, состояние `status.json`, PID `pid`. Фазы waiting_for_ssh/checking/transferring ещё не означают успеха; succeeded означает успешный выход save_handoff.py. После перезагрузки локальной машины процесс сам не возобновится.
Незапущенный сетевой тест пользователя: на стенде установить iperf3 и запустить `iperf3 -c 10.9.8.1 -P 8 -t 5`. Пока не выполнен из-за тайм-аутов SSH; монитор доставки не выполняет сетевые тесты.

102
cats-dogs/SHA256SUMS Normal file
View file

@ -0,0 +1,102 @@
9b2c5f1cd86beccc8a99c3271498b9592308a1bc48ee1d76704da16120b43010 ACCELERATION.md
8ada9e71766b8296531b1e5943ac7ccdcd1d1e7549bbfe15e069c2144fdfaa95 HANDOFF.md
4cead73682e2630748eb4c211d85087295915dba04dc4efdba36412c554343f7 README.md
7990ce2b6f07fdaa9f47f8ce5cf049b8726532db3ed13a543eed2d5e28abb2fc V2.md
c5c5681ec9b87298c634bc0946cf5af9b31cd5a31e22dc2c621f1e466f5381b3 acceleration-results/backend-altai-benchmark.json
39dabd4286888aeb68d9754bd87f0c2194ebe3f7ee7f7b920f76da3a1d724940 acceleration-results/batch-benchmark.json
a98c096a3cafc13ac745a6564c71d26eb99bf9f42db29a8596207336bce3b435 acceleration-results/cpu-altai-multithread.patch
563e0eea1f97b8016500451c14cfecdb2b78e7db4a7a47a90ed83b53a87a9eac acceleration-results/cuda-knp-probe.log
a77fec6556bffc859dec3ff15932a5ae4dd4bbc8b17be20d58ef580ed7a48edb benchmark_backends.py
0af5c6b528b89aeb37036dcfa6a93f08a2ebb2eec2797277d915064cc6af166e benchmark_batches.py
05e1ab25e7eee68ca39e87c283de663cdd4c3733ea3a7051e2ffebdc05d51f13 build.py
b7a4c696cec743e8daff034350f7419c2bdfcae4b7e6c925e1cbcb1ed393d187 build_fast_cpu.py
61f74fde9d868341f3aa2f5192e96db113d30832d194ffb94db8d8923d8394e7 build_tuned_cpu.py
f7002794d213c9cc01e2c303cdaf8fd3687ed2e30d5da8e5332e17617166c2d9 contrast-manifest.json
4a5b8778c8c40971e318aa6ae643768774f811ca925a112859d5f7e35e670b3d data-manifest.json
2ef51ad7019ead007048a244c5bdbba3c747ffe57f511dbc2b7e3344fd756d57 download.py
242dd12184fbadca48b50b1eae4e6ece8009a8fa06d3cbffbe143cd9c7bd776a evaluate_v2.py
08a3a728200c73a832c7d84fd5021b0953993f501dd4612d49bfb536856a9d41 features.py
722e800a4d00a96dbf6b7dfbb8704ea59c0b097966bc3f42b29246735b26e18e fetch_cache.py
61857ad535938fd845722abfc0e4da215c69937f3272d632748264c883502020 gpu_stdp.cu
007b069b4948b1399f3069cca65e04ff0902995863f941e66d0bf50e493c60e5 gpu_stdp_bridge.cpp
789d3276993d132d8007f6e972fbc2ee6dc78df51609c081ee17c707410105ec gpu_stdp_bridge.h
680cedcafd0b1dffe97dfb8e79aaf725066825c2c6c2fb38fa746ab920973869 gpu_stdp_types.h
de73810b66ca207a57cd8ce55a4a5e1dc9e9f27c1b4eb2213e56fe4d9292b958 handoff-state/cancelled-monitor-monitor.log
4feddb8b3929c7645d8e8c523d72006f0108512d3c88732f1a0a107449df710c handoff-state/cancelled-monitor-status.json
41f4f736119ebc12e19ec4349dcbad3e78bd5fd0e9e95d34639aaf0944054e65 handoff-state/daily-log-2026-10-14.md
76491d12a4fcebfebbf6fc8608a14a2ed5da96dd55090060153d950bb984f185 handoff-state/local-AGENTS.md
39eeac7c25f799977f43a2f29f16d762cb2a060d6610e1fe0b2cb41b1341866c handoff-state/observations/2026-10-14-drawn-4-leading-5.png
01a53c6c268c753f9f2b156b303be6899dd75fed1d4d7847be0dd30900d04728 handoff-state/observations/2026-10-14-encoding-before.png
c566b24daaec7a565a08bade3856b81bcba34576b1220b7dbc3b54f05c89305d monitor_handoff.py
897230bc78bdbba736eb51f5975d9b9bff0dd1bb55668c3dc84c598dc799ed22 perf_connection_cache.h
2b708e997239721b7898898da8b8abdbc76c94762a99881e6d9af7277f14dcb5 prepare_baseline_v2.py
f79b752df5239607bfe773e979e8746fe7d1808ed0dc96be9f1f85a860ba1598 prepare_contrast.py
95a9f3bc2942860ca473ab66e02b16a1c8e14b0b0427d7b8faea7052276d5361 prepare_data.py
5317232956ee81908bcd814c32d88583e93148d5247642e3f1d153f7b0035f43 prepare_features.py
5ab30b5eaaa5827ddbe32e949ec66994970902a7b4ac5b1f7567753e642edd57 prepare_jpeg.py
1a59fffafa4324665df4e73edeac1f41975b141c11e53e7a2563c4a4d197a473 prepare_preview.py
fea3d85cfd4d375d1dafba80a540c89cb7adc8061d462b0104e92da46829138b prepare_v2.py
3c31f5a890ff80ca11718be9fdf3484ca9913e4613aaa8cbcc625b72a453221c preview-cache/rows-train-0.json
e2b5c70a4d2787bd078205a7126ab69dfa41fdda8bfaadf73f61417f4273de2e preview-cache/rows-train-100.json
f0299d0f59f5f563cbfe59aefb1c321d819ebdfe7b3d57ec890156d75c3a85f8 preview-cache/rows-train-1000.json
ec4338ad396ff0734c726d8c932168995c0097f5b450cd8e6f40f6d6c37a610b preview-cache/rows-train-1100.json
7119ecd8b9d8178307e729172913637653c109a4556f72d6faf08defcfd4a43e preview-cache/rows-train-1200.json
f6c4e73772fa5c19fd2a7bad9c218b994174844cc321194c83a56f3afff1929c preview-cache/rows-train-1300.json
68d650f9bea8af1a639e3f62ba16556de36dd469ac4edf0c3ae1cee7a790759b preview-cache/rows-train-1400.json
6cf2a8aeec467597ded345b71f2e93ccc560f6a8cb9b48444eab8205f3e2f8e9 preview-cache/rows-train-1500.json
b80c26b8852db3ee7d172474cadb34fd5063357a7396fd620afd877f9b0fc4f5 preview-cache/rows-train-1600.json
bc72cd8a9e324dd9e03da72866de7fbb38c7946132c630938b5268ddbab1ef51 preview-cache/rows-train-1700.json
370449a3f7eef727ec24ccdc7f98b69927ca3511b351ce595a9120534f579f46 preview-cache/rows-train-1800.json
67441091a8e8be4e7b861e6b8fd28495f6499d2bd6ad08dcf6c962b64a1cad1a preview-cache/rows-train-1900.json
bcf79a109e6909bb41ea6a679cb63fc4591a0d2ca11e835ee836a718619d899f preview-cache/rows-train-200.json
007294c524645a8e6c12402fb49e5e3c883e1362b963cc47bdfe5deb76ba184c preview-cache/rows-train-2000.json
67a7a3a1f20b3f14b73857b4dce80ce70e6e44aa1f3d5aabf9a4cb98b3e212fa preview-cache/rows-train-2100.json
ec6b1b395864c6854226b121dcb75456ea197e3647dfcc685d768262de9378bb preview-cache/rows-train-2200.json
f9f4122deab0461f8e96dd97e8f04d03f0acc6e38cca14ad6e3020efd850205d preview-cache/rows-train-2300.json
4c973e4b5825f0b9db877d313bfb27821a268a326daa1fd86933b533965065e3 preview-cache/rows-train-2400.json
d3d5d6c416be1495e54155d01f8c3a0b79e07c5d8a1c0e67706683a56614df23 preview-cache/rows-train-2500.json
ff65f42c724de47ce00567cbe92b9e507d515ef2f5537018460266462ddf13f8 preview-cache/rows-train-2600.json
719eecb5ece98fe7d74de5cc7ea4a57c925bd912db5ab0cee8a822fe90d275b9 preview-cache/rows-train-2700.json
d7a869ccde35acd7492b33baedaa7152b6f0629506809ce076dc02c624678bd4 preview-cache/rows-train-2800.json
fc88b8fb1343ef0a5fd99921af2a9028f97a45944d8621da8ef41b2258e2971d preview-cache/rows-train-2900.json
990eb0fd9d113b40632a5a27f7890c09d386d4ee3b80ca31e5b8b5c3352a61f5 preview-cache/rows-train-300.json
055655059a78ab4496d4475da3bdba50e816a511c1d95d1f00a68feeae408ace preview-cache/rows-train-3000.json
9e67594984df77fdd24c29f92442631943a2c040cdc2376f04ffc2cf749870b3 preview-cache/rows-train-3100.json
e8959f0023827809b5570b825fadab9337211d1476455daa4ec316cb5347ee80 preview-cache/rows-train-3200.json
06187a2922e45e597ccdf4abb6c15b71c703aafc7783ec36af32e399cc680571 preview-cache/rows-train-3300.json
31c5d6bd3d2369adfacf41ae7ab9db19be35dba895177ae1b061be7b548f7b27 preview-cache/rows-train-3400.json
0a4b2f5788fde86a68fefb36410c067cfb2d0c1eb7ce521af1c60fd2511fefd4 preview-cache/rows-train-3500.json
c7ad2933d1ac0cba15986923a0e1e4ec3e2c0d60d7abb62f3b6b7df527801f1c preview-cache/rows-train-3600.json
dc31a125a5fb3387749daee5cc0b73a700b370a21c3c4390cb9247f4deaadcd8 preview-cache/rows-train-3700.json
94b5c173034ec00530c6fa42ac9e898e8281410dfb4ddcd76252850b7a41d639 preview-cache/rows-train-3800.json
ffadf864866e1e6cb616e36a3ba49c23d172a166de76e57384ce2e0836d728a6 preview-cache/rows-train-3900.json
5dd424610f4f449849adddaa64cfb1b59b0f7f0f3713b8fcc736aa7d799e3b4c preview-cache/rows-train-400.json
2c9b0cc2f88e37774fdb1d0bbf039245ed8444042441f75d8063a8d9429d53b7 preview-cache/rows-train-4000.json
b0ee3457cb4e75dd670101c797b524ea6b601156c7bd382285bb0a45d46927c2 preview-cache/rows-train-4300.json
b7d3acca6b257ca1f066479773d2da4c32a7ae26e24240ac15c7515280c616d3 preview-cache/rows-train-4400.json
15401fad97a19ab77f3dc4049174ca27f884074fd264cf5e204c936bfbae3a58 preview-cache/rows-train-500.json
242e8685673beeae59a9f33f33b07c6478a6e6e67836d9965e7400d17c55c6c2 preview-cache/rows-train-600.json
7c623c44c125bc54fbf59c30b745898a074a0e8d058e5cc9cad2ae30e01c4ef8 preview-cache/rows-train-700.json
5f05e8b66c538f6129c0a86304b8ff127782171b75f07f4a346ff019f635ea15 preview-cache/rows-train-800.json
bc9e6e8fd6c3c44ba7ce0e4fecf471e73aa78d97d2cbaca21a5d3e601c159b32 preview-cache/rows-train-900.json
fd6af75cfa6ebc31e17ffcd7e3625b89921fa644f1f9cd8d9f7be06608b1a665 probe_v2.py
bda80dcba25f3742f773fdf51708b515764d921306f205fb7990eb475b3d7e0c results/reproducibility.json
a8886f55790ce838517ef067f2292227d0cc63052d99e498d562330542a67eb2 results/selection.json
50ccf2a5c60055d8dcd87392dacd70b806ec322caa988a27d247654ffa5b0369 results/test-metrics.json
8a54e418d309f1073db68b3fd99cef8cd5ac30495990f4313be85d3d9758baaa results/test-settings.json
e7a4aba7335e9fc8bdbd5c121570191d9a0887e9cf3f0dcb778529070a078f37 results-v2/comparison.json
17febe461517f20535acddc3f8eab1b2f93b5df05c8d7cccee5cb5d69e27b315 results-v2/data-manifest.json
aade7e46013aa29abedd88ffb9b09b7a64db0aa84df2f95bff1bfd39633ec18f results-v2/features-manifest.json
0bb303fb4d440c96eff1067b43d282988ea3b577fc48ad85668a203558cc30da results-v2/probes.json
815d83fea856132b40364f2d595bd55f007596893196b301d40a080311248d45 results-v2/selection.json
a0d059e6b9bdefbfe3dad3e825910eb3e8d2be2c4151f6e1c38cf78e787344c7 results-v2/test-settings.json
5d6ae31f041b632385f91c53eda54210abce1ff938717810da8d8f4784b024bb run.py
f803facc090b7a03d5c404269e64a62a0ec36d28d92a4dd23543368a99175a9b run_batch.py
3cff1da48b4fb1d91e40264c436356d154e706853cafc645bc38992b497d044d save_handoff.py
28289bb99b31cf1da7351efc04982f9812e2f0435e92636176270d7034514075 source-snapshot.tar.gz
6d95c6a93042c29acbc4f927c8df69e9b2ea002b59cf3e808f094c8dbdc4779a test_features.py
d6dc1f52bba677f4ebed76cf921538ea43504ee374ada6ba1291d1a37ce949f2 trained-weights-v2.tar.gz
653b34df78ccba30f9c09d296cfd145676d33d7ab0cf98447f0bec853560ca1a trained-weights.tar.gz
7904da935466f1cb5a467c7a05570a3740ed2641393eb0cfa1d63e4734030a97 tune.py
590042f0161a73c11042b1b5cf8018e2deb8e6e1361752b55a762231faf36375 validate.py
82f49b30a2b6813d1f87d7f8417996fac57b32d7500206e47e843f82fb39f02a validate_v2.py

View file

@ -0,0 +1,68 @@
[
{
"backend": "single",
"elapsed_seconds": 4.89,
"classes": {
"cat": {
"correct": 57,
"unanswered": 0,
"wrong": 43
},
"dog": {
"correct": 66,
"unanswered": 0,
"wrong": 34
}
},
"correct": 123,
"unanswered": 0,
"wrong": 77,
"accuracy": 0.615,
"same_weights_and_result": true,
"wall_seconds": 4.966
},
{
"backend": "tuned2",
"elapsed_seconds": 15.09,
"classes": {
"cat": {
"correct": 57,
"unanswered": 0,
"wrong": 43
},
"dog": {
"correct": 66,
"unanswered": 0,
"wrong": 34
}
},
"correct": 123,
"unanswered": 0,
"wrong": 77,
"accuracy": 0.615,
"same_weights_and_result": true,
"wall_seconds": 15.151
},
{
"backend": "tuned4",
"elapsed_seconds": 15.69,
"classes": {
"cat": {
"correct": 57,
"unanswered": 0,
"wrong": 43
},
"dog": {
"correct": 66,
"unanswered": 0,
"wrong": 34
}
},
"correct": 123,
"unanswered": 0,
"wrong": 77,
"accuracy": 0.615,
"same_weights_and_result": true,
"wall_seconds": 15.749
}
]

View file

@ -0,0 +1,235 @@
{
"serial": {
"jobs": 1,
"logical_cpus": 24,
"runs": [
{
"name": "bench_serial_t600",
"returncode": 0,
"metrics": {
"elapsed_seconds": 23.55,
"classes": {
"cat": {
"correct": 324,
"unanswered": 0,
"wrong": 176
},
"dog": {
"correct": 268,
"unanswered": 0,
"wrong": 232
}
},
"correct": 592,
"unanswered": 0,
"wrong": 408,
"accuracy": 0.592
}
},
{
"name": "bench_serial_t1600",
"returncode": 0,
"metrics": {
"elapsed_seconds": 22.67,
"classes": {
"cat": {
"correct": 306,
"unanswered": 0,
"wrong": 194
},
"dog": {
"correct": 319,
"unanswered": 0,
"wrong": 181
}
},
"correct": 625,
"unanswered": 0,
"wrong": 375,
"accuracy": 0.625
}
},
{
"name": "bench_serial_t4000",
"returncode": 0,
"metrics": {
"elapsed_seconds": 22.65,
"classes": {
"cat": {
"correct": 297,
"unanswered": 0,
"wrong": 203
},
"dog": {
"correct": 329,
"unanswered": 0,
"wrong": 171
}
},
"correct": 626,
"unanswered": 0,
"wrong": 374,
"accuracy": 0.626
}
},
{
"name": "bench_serial_t8000",
"returncode": 0,
"metrics": {
"elapsed_seconds": 22.44,
"classes": {
"cat": {
"correct": 298,
"unanswered": 0,
"wrong": 202
},
"dog": {
"correct": 317,
"unanswered": 3,
"wrong": 180
}
},
"correct": 615,
"unanswered": 3,
"wrong": 382,
"accuracy": 0.615
}
}
],
"elapsed_seconds": 91.573,
"plan": "/home/int/run/cats-dogs/acceleration/serial-plan.json",
"all_succeeded": true
},
"parallel": {
"jobs": 4,
"logical_cpus": 24,
"runs": [
{
"name": "bench_parallel_t8000",
"returncode": 0,
"metrics": {
"elapsed_seconds": 40.67,
"classes": {
"cat": {
"correct": 298,
"unanswered": 0,
"wrong": 202
},
"dog": {
"correct": 317,
"unanswered": 3,
"wrong": 180
}
},
"correct": 615,
"unanswered": 3,
"wrong": 382,
"accuracy": 0.615
}
},
{
"name": "bench_parallel_t4000",
"returncode": 0,
"metrics": {
"elapsed_seconds": 42.17,
"classes": {
"cat": {
"correct": 297,
"unanswered": 0,
"wrong": 203
},
"dog": {
"correct": 329,
"unanswered": 0,
"wrong": 171
}
},
"correct": 626,
"unanswered": 0,
"wrong": 374,
"accuracy": 0.626
}
},
{
"name": "bench_parallel_t600",
"returncode": 0,
"metrics": {
"elapsed_seconds": 42.68,
"classes": {
"cat": {
"correct": 324,
"unanswered": 0,
"wrong": 176
},
"dog": {
"correct": 268,
"unanswered": 0,
"wrong": 232
}
},
"correct": 592,
"unanswered": 0,
"wrong": 408,
"accuracy": 0.592
}
},
{
"name": "bench_parallel_t1600",
"returncode": 0,
"metrics": {
"elapsed_seconds": 42.86,
"classes": {
"cat": {
"correct": 306,
"unanswered": 0,
"wrong": 194
},
"dog": {
"correct": 319,
"unanswered": 0,
"wrong": 181
}
},
"correct": 625,
"unanswered": 0,
"wrong": 375,
"accuracy": 0.625
}
}
],
"elapsed_seconds": 42.911,
"plan": "/home/int/run/cats-dogs/acceleration/parallel-plan.json",
"all_succeeded": true
},
"speedup": 2.1340215795483672,
"bitwise_equal": {
"600": {
"float_weights.csv": true,
"quant_weights.csv": true,
"float_neurons.csv": true,
"quant_neurons.csv": true,
"result.csv": true
},
"1600": {
"float_weights.csv": true,
"quant_weights.csv": true,
"float_neurons.csv": true,
"quant_neurons.csv": true,
"result.csv": true
},
"4000": {
"float_weights.csv": true,
"quant_weights.csv": true,
"float_neurons.csv": true,
"quant_neurons.csv": true,
"result.csv": true
},
"8000": {
"float_weights.csv": true,
"quant_weights.csv": true,
"float_neurons.csv": true,
"quant_neurons.csv": true,
"result.csv": true
}
}
}

View file

@ -0,0 +1,53 @@
--- a/knp/backends/cpu/cpu-multi-threaded-backend/impl/backend.cpp
+++ b/knp/backends/cpu/cpu-multi-threaded-backend/impl/backend.cpp
@@ -32,6 +32,9 @@
#include <spdlog/spdlog.h>
#include <functional>
+#include <cstdlib>
+#include <cerrno>
+#include <stdexcept>
#include <unordered_map>
#include <vector>
@@ -53,10 +56,27 @@
}
+
+// Experiment-local factory configuration; the original backend is unchanged.
+static size_t configured_size(const char* name, size_t fallback, size_t maximum)
+{
+ const char* value = std::getenv(name);
+ if (!value || !*value) return fallback;
+ if (*value < '0' || *value > '9') throw std::invalid_argument(name);
+ char* end = nullptr;
+ errno = 0;
+ const auto parsed = std::strtoull(value, &end, 10);
+ if (errno || *end || !parsed || parsed > maximum) throw std::invalid_argument(name);
+ return static_cast<size_t>(parsed);
+}
+
std::shared_ptr<MultiThreadedCPUBackend> MultiThreadedCPUBackend::create()
{
SPDLOG_DEBUG("Creating multi-threaded CPU backend instance...");
- return std::make_shared<MultiThreadedCPUBackend>();
+ return std::make_shared<MultiThreadedCPUBackend>(
+ configured_size("KNP_CPU_THREADS", 2, 256),
+ configured_size("KNP_CPU_POPULATION_PART", 64, 1000000),
+ configured_size("KNP_CPU_PROJECTION_PART", 8192, 10000000));
}
--- a/knp/backends/cpu/cpu-multi-threaded-backend/include/knp/backends/cpu-multi-threaded/backend.h
+++ b/knp/backends/cpu/cpu-multi-threaded-backend/include/knp/backends/cpu-multi-threaded/backend.h
@@ -79,7 +79,8 @@
*/
using SupportedNeurons = boost::mp11::mp_list<
knp::neuron_traits::BLIFATNeuron, knp::neuron_traits::SynapticResourceSTDPBLIFATNeuron,
- knp::neuron_traits::LIFNeuron>;
+ knp::neuron_traits::LIFNeuron,
+ knp::neuron_traits::AltAILIF, knp::neuron_traits::SynapticResourceSTDPAltAILIFNeuron>;
/**
* @brief List of synapse types supported by the multi-threaded CPU backend.

View file

@ -0,0 +1,39 @@
[2026-10-14 16:53:42.546] [info] [backend_loader.cpp:172] Checking library by path "/home/int/gputest/knp-cpu-single-threaded-backend"...
[2026-10-14 16:53:42.564] [info] [backend.cu:46] CUDA backend instance created.
[2026-10-14 16:53:42.564] [info] [backend_loader.cpp:73] Backend loaded.
[2026-10-14 16:53:42.564] [info] [backend_loader.cpp:165] Backend loaded.
[2026-10-14 16:53:42.727] [info] [model_executor.cpp:94] Starting model execution...
[2026-10-14 16:53:42.727] [info] [backend.cpp:64] Starting backend 3876cd53-f7ec-4987-8494-39d97eb381d2...
[2026-10-14 16:53:42.731] [error] [vector.cuh:554] N4cuda3std3__45arrayIhLm16EEE Finish actualize: invalid device function
[2026-10-14 16:53:42.733] [error] [vector.cuh:462] yResize: destruct_kernel invalid device function
[2026-10-14 16:53:42.733] [error] [vector.cuh:462] yResize: destruct_kernel invalid device function
[2026-10-14 16:53:42.734] [error] [vector.cuh:554] N4cuda3std3__45arrayIhLm16EEE Finish actualize: invalid device function
[2026-10-14 16:53:42.735] [error] [vector.cuh:554] N4cuda3std3__45arrayIhLm16EEE Finish actualize: invalid device function
[2026-10-14 16:53:42.736] [error] [vector.cuh:554] N4cuda3std3__45arrayIhLm16EEE Finish actualize: invalid device function
[2026-10-14 16:53:42.737] [error] [vector.cuh:554] N4cuda3std3__45arrayIhLm16EEE Finish actualize: invalid device function
[2026-10-14 16:53:42.738] [error] [vector.cuh:554] N4cuda3std3__45arrayIhLm16EEE Finish actualize: invalid device function
[2026-10-14 16:53:42.739] [error] [vector.cuh:462] yResize: destruct_kernel invalid device function
[2026-10-14 16:53:42.739] [error] [vector.cuh:462] yResize: destruct_kernel invalid device function
[2026-10-14 16:53:42.740] [error] [vector.cuh:554] N4cuda3std3__45arrayIhLm16EEE Finish actualize: invalid device function
[2026-10-14 16:53:42.741] [error] [vector.cuh:554] N4cuda3std3__45arrayIhLm16EEE Finish actualize: invalid device function
[2026-10-14 16:53:42.742] [error] [vector.cuh:554] N4cuda3std3__45arrayIhLm16EEE Finish actualize: invalid device function
[2026-10-14 16:53:42.743] [error] [vector.cuh:554] N4cuda3std3__45arrayIhLm16EEE Finish actualize: invalid device function
[2026-10-14 16:53:42.744] [error] [vector.cuh:554] N4cuda3std3__45arrayIhLm16EEE Finish actualize: invalid device function
[2026-10-14 16:53:42.745] [error] [vector.cuh:462] yResize: destruct_kernel invalid device function
[2026-10-14 16:53:42.745] [error] [vector.cuh:462] yResize: destruct_kernel invalid device function
[2026-10-14 16:53:42.745] [error] [vector.cuh:554] N4cuda3std3__45arrayIhLm16EEE Finish actualize: invalid device function
[2026-10-14 16:53:42.746] [error] [vector.cuh:554] N4cuda3std3__45arrayIhLm16EEE Finish actualize: invalid device function
[2026-10-14 16:53:42.747] [error] [vector.cuh:554] N4cuda3std3__45arrayIhLm16EEE Finish actualize: invalid device function
[2026-10-14 16:53:42.748] [error] [vector.cuh:554] N4cuda3std3__45arrayIhLm16EEE Finish actualize: invalid device function
[2026-10-14 16:53:42.749] [error] [vector.cuh:554] N4cuda3std3__45arrayIhLm16EEE Finish actualize: invalid device function
[2026-10-14 16:53:42.750] [error] [vector.cuh:462] yResize: destruct_kernel invalid device function
[2026-10-14 16:53:42.750] [error] [vector.cuh:462] yResize: destruct_kernel invalid device function
[2026-10-14 16:53:42.751] [error] [vector.cuh:554] N4cuda3std3__45arrayIhLm16EEE Finish actualize: invalid device function
[2026-10-14 16:53:42.752] [error] [vector.cuh:554] N4cuda3std3__45arrayIhLm16EEE Finish actualize: invalid device function
[2026-10-14 16:53:42.752] [error] [vector.cuh:554] N4cuda3std3__45arrayIhLm16EEE Finish actualize: invalid device function
[2026-10-14 16:53:42.753] [error] [vector.cuh:554] N4cuda3std3__45arrayIhLm16EEE Finish actualize: invalid device function
[2026-10-14 16:53:42.754] [info] [backend.cpp:140] Backend 3876cd53-f7ec-4987-8494-39d97eb381d2 stopped.
[2026-10-14 16:53:42.754] [info] [model_executor.cpp:127] Model execution stopped.
[2026-10-14 16:53:42.754] [info] [backend.cpp:151] Stopping backend 3876cd53-f7ec-4987-8494-39d97eb381d2...
[2026-10-14 16:53:42.754] [info] [backend.cpp:53] Backend 3876cd53-f7ec-4987-8494-39d97eb381d2 unloaded.

File diff suppressed because it is too large Load diff

7029
cats-dogs/data-manifest.json Normal file

File diff suppressed because it is too large Load diff

50
cats-dogs/fetch_cache.py Normal file
View file

@ -0,0 +1,50 @@
"""Fetch preview cache on another machine if the stand's public IP is rate limited."""
from concurrent.futures import ThreadPoolExecutor
import json
from pathlib import Path
import time
import urllib.parse
import urllib.request
cache = Path(__file__).parent / 'preview-cache'
cache.mkdir(exist_ok=True)
def fetch(url):
for attempt in range(6):
try:
with urllib.request.urlopen(url, timeout=30) as response:
return response.read()
except Exception:
if attempt == 5:
raise
time.sleep(min(30, 3 * (attempt + 1)))
def metadata(args):
split, offset = args
path = cache / f'rows-{split}-{offset}.json'
if not path.exists():
query = urllib.parse.urlencode(dict(dataset='uoft-cs/cifar10', config='plain_text',
split=split, offset=offset, length=100))
path.write_bytes(fetch('https://datasets-server.huggingface.co/rows?' + query))
time.sleep(0.5)
return [(split, r) for r in json.loads(path.read_text())['rows'] if r['row']['label'] in (3, 5)]
def image(args):
split, row = args
path = cache / f'{split}-{row["row_idx"]}.jpg'
if not path.exists():
path.write_bytes(fetch(row['row']['img']['src']))
tasks = [('train', i) for i in range(0, 6000, 100)] + [('test', i) for i in range(0, 3000, 100)]
with ThreadPoolExecutor(max_workers=2) as pool:
images = [item for batch in pool.map(metadata, tasks) for item in batch]
print('metadata ready', len(images), flush=True)
with ThreadPoolExecutor(max_workers=4) as pool:
for i, _ in enumerate(pool.map(image, images)):
if i % 200 == 0:
print('images', i, flush=True)
print('Cache ready', flush=True)

161
cats-dogs/gpu_stdp.cu Normal file
View file

@ -0,0 +1,161 @@
#include "gpu_stdp_types.h"
#include <cuda_runtime.h>
#include <cmath>
#include <stdexcept>
#include <string>
static void checked(cudaError_t status)
{
if (status != cudaSuccess) throw std::runtime_error(std::string("AltAI CUDA: ") + cudaGetErrorString(status));
}
struct GpuStdpContext
{
GpuStdpSynapse* synapses = nullptr;
GpuStdpNeuron* neurons = nullptr;
std::uint64_t* last = nullptr;
std::size_t* offsets = nullptr;
float* weights = nullptr;
std::size_t n = 0, s = 0;
};
__device__ static float factor(float stability)
{
return fminf(static_cast<float>(pow(2.0, static_cast<double>(-stability))), 1.0f);
}
__device__ static void recalculate(GpuStdpSynapse* synapses, float* weights, std::size_t lo, std::size_t hi)
{
for (std::size_t j = lo; j < hi; ++j)
{
auto& s = synapses[j];
const float resource = fmaxf(s.resource, 0.0f);
const float diff = s.w_max - s.w_min;
s.weight = s.w_min + diff * resource / (diff + resource);
weights[j] = s.weight;
}
}
__global__ static void plasticity(GpuStdpNeuron* neurons, GpuStdpSynapse* synapses,
const std::uint64_t* last, const std::size_t* offsets,
float* weights, std::size_t count, std::uint64_t step)
{
const std::size_t i = blockIdx.x * blockDim.x + threadIdx.x;
if (i >= count) return;
auto& n = neurons[i];
const std::size_t lo = offsets[i], hi = offsets[i + 1];
if (n.spiked)
{
n.last_spike = step;
if (n.forced) n.status = 0;
else
{
if (n.status == 3 || n.status == 0) { n.status = 1; n.first_isi = step; }
else if (n.status == 1) { if (n.last_step - step < n.isi_max) n.status = 2; }
else if (n.status == 2)
{
if (n.last_step - step >= n.isi_max || n.dopamine != 0) { n.status = 1; n.first_isi = step; }
}
n.last_step = step;
}
if (n.status == 1) n.stability -= n.stability_at_isi;
n.additional = 0;
for (std::size_t j = lo; j < hi; ++j)
{
auto& s = synapses[j];
n.additional += s.weight * (s.weight > 0);
auto begin = step - s.period;
auto end = step;
if (begin > end) { auto t = begin; begin = end; end = t; }
const auto point = last[j] + s.delay - 1;
const bool had_spike = begin <= point && point <= end;
if (n.status != 2 || had_spike) s.contributed = had_spike;
}
n.additional *= n.coefficient;
if (n.status != 2)
for (std::size_t j = lo; j < hi; ++j) synapses[j].hebbian = 0;
if (n.status != 0)
for (std::size_t j = lo; j < hi; ++j)
{
auto& s = synapses[j];
s.resource -= s.d_u;
n.free_resource += s.d_u;
if (s.contributed && !s.hebbian)
{
const float delta = n.d_h * factor(n.stability);
s.resource += delta;
n.free_resource -= delta;
s.hebbian = 1;
}
}
recalculate(synapses, weights, lo, hi);
}
if (n.dopamine > 0 || (n.dopamine < 0 && n.status != 0))
{
for (std::size_t j = lo; j < hi; ++j)
{
auto& s = synapses[j];
if (step - n.last_spike <= n.dopamine_time && s.contributed)
{
const float delta = static_cast<float>(n.dopamine * factor(n.stability));
s.resource += delta;
n.free_resource -= delta;
}
}
if (n.forced || n.dopamine < 0)
{
n.stability = static_cast<float>(n.stability - n.dopamine * n.stability_change);
n.stability = fmaxf(n.stability, 0.0f);
}
else
{
const double difference = static_cast<double>(step - n.first_isi - n.isi_max);
n.stability = static_cast<float>(n.stability + n.stability_change * n.dopamine *
fmax(2.0 - fabs(difference) / n.isi_max, -1.0));
}
recalculate(synapses, weights, lo, hi);
}
if (step - n.last_step <= n.isi_max && n.status != 0) return;
if (fabsf(n.free_resource) < n.resource_threshold) return;
const float delta = n.free_resource / static_cast<float>((hi - lo) + n.drain);
for (std::size_t j = lo; j < hi; ++j) synapses[j].resource += delta;
n.free_resource = 0;
recalculate(synapses, weights, lo, hi);
}
GpuStdpContext* gpu_stdp_create(const GpuStdpSynapse* synapses, const float* weights,
const std::size_t* offsets, std::size_t n, std::size_t s)
{
auto* c = new GpuStdpContext;
c->n = n; c->s = s;
try
{
checked(cudaMalloc(&c->synapses, s * sizeof(GpuStdpSynapse)));
checked(cudaMalloc(&c->neurons, n * sizeof(GpuStdpNeuron)));
checked(cudaMalloc(&c->last, s * sizeof(std::uint64_t)));
checked(cudaMalloc(&c->offsets, (n + 1) * sizeof(std::size_t)));
checked(cudaMalloc(&c->weights, s * sizeof(float)));
checked(cudaMemcpy(c->synapses, synapses, s * sizeof(GpuStdpSynapse), cudaMemcpyHostToDevice));
checked(cudaMemcpy(c->offsets, offsets, (n + 1) * sizeof(std::size_t), cudaMemcpyHostToDevice));
checked(cudaMemcpy(c->weights, weights, s * sizeof(float), cudaMemcpyHostToDevice));
}
catch (...) { gpu_stdp_destroy(c); throw; }
return c;
}
void gpu_stdp_step(GpuStdpContext* c, GpuStdpNeuron* neurons, const std::uint64_t* last,
float* weights, std::uint64_t step)
{
checked(cudaMemcpy(c->neurons, neurons, c->n * sizeof(GpuStdpNeuron), cudaMemcpyHostToDevice));
checked(cudaMemcpy(c->last, last, c->s * sizeof(std::uint64_t), cudaMemcpyHostToDevice));
plasticity<<<(c->n + 31) / 32, 32>>>(c->neurons, c->synapses, c->last, c->offsets, c->weights, c->n, step);
checked(cudaGetLastError());
checked(cudaMemcpy(neurons, c->neurons, c->n * sizeof(GpuStdpNeuron), cudaMemcpyDeviceToHost));
checked(cudaMemcpy(weights, c->weights, c->s * sizeof(float), cudaMemcpyDeviceToHost));
}
void gpu_stdp_download(GpuStdpContext* c, GpuStdpSynapse* synapses)
{
checked(cudaMemcpy(synapses, c->synapses, c->s * sizeof(GpuStdpSynapse), cudaMemcpyDeviceToHost));
}
void gpu_stdp_destroy(GpuStdpContext* c) noexcept
{
if (!c) return;
cudaFree(c->synapses); cudaFree(c->neurons); cudaFree(c->last); cudaFree(c->offsets); cudaFree(c->weights);
delete c;
}

View file

@ -0,0 +1,163 @@
#include "gpu_stdp_bridge.h"
#include "gpu_stdp_types.h"
#include <knp/backends/cpu-library/impl/populations/training/stdp.h>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <map>
#include <memory>
#include <stdexcept>
namespace knp_gpu_experiment
{
using Params = knp::synapse_traits::synapse_parameters<Synapse>;
using NeuronParams = knp::neuron_traits::neuron_parameters<Neuron>;
struct Context
{
Population* population;
std::vector<std::reference_wrapper<Params>> synapses;
std::vector<std::size_t> offsets;
std::vector<GpuStdpSynapse> state;
std::vector<GpuStdpNeuron> neurons;
std::vector<std::uint64_t> last;
std::vector<float> weights;
GpuStdpContext* gpu = nullptr;
~Context() { gpu_stdp_destroy(gpu); }
};
static thread_local std::map<const Population*, std::unique_ptr<Context>> contexts;
static thread_local std::size_t checked_steps = 0, gpu_steps = 0;
static thread_local double max_error = 0;
static GpuStdpSynapse pack(const Params& p)
{
const auto& r = p.rule_;
return {r.synaptic_resource_, r.w_min_, r.w_max_, r.d_u_, p.weight_,
r.dopamine_plasticity_period_, p.delay_, r.has_contributed_, r.had_hebbian_update_};
}
static GpuStdpNeuron pack(const NeuronParams& n, bool spike)
{
return {n.additional_threshold_, n.dopamine_value_, n.synapse_sum_threshold_coefficient_,
n.free_synaptic_resource_, n.synaptic_resource_threshold_, n.stability_,
n.stability_change_parameter_, n.stability_change_at_isi_, n.d_h_,
n.last_step_, n.last_spike_step_, n.first_isi_spike_, n.isi_max_,
n.dopamine_plasticity_time_, n.resource_drain_coefficient_,
static_cast<int>(n.isi_status_), n.is_being_forced_, spike};
}
static void unpack(const GpuStdpNeuron& s, NeuronParams& n)
{
n.additional_threshold_ = s.additional;
n.free_synaptic_resource_ = s.free_resource;
n.stability_ = s.stability;
n.last_step_ = s.last_step; n.last_spike_step_ = s.last_spike; n.first_isi_spike_ = s.first_isi;
n.isi_status_ = static_cast<knp::neuron_traits::ISIPeriodType>(s.status);
}
static void unpack(const GpuStdpSynapse& s, Params& p)
{
p.weight_ = s.weight;
p.rule_.synaptic_resource_ = s.resource;
p.rule_.has_contributed_ = s.contributed;
p.rule_.had_hebbian_update_ = s.hebbian;
// last_spike_step_ remains authoritative on CPU (the projection phase updates it).
}
static bool near(double a, double b)
{
if (a == b) return true;
const double error = std::abs(a - b);
max_error = std::max(max_error, error);
return std::isfinite(a) && std::isfinite(b) && error <= 1e-4 + 1e-6 * std::max(std::abs(a), std::abs(b));
}
static void compare(const GpuStdpNeuron& actual, const GpuStdpNeuron& expected)
{
if (!near(actual.additional, expected.additional) || !near(actual.free_resource, expected.free_resource) ||
!near(actual.stability, expected.stability) || actual.last_step != expected.last_step ||
actual.last_spike != expected.last_spike || actual.first_isi != expected.first_isi ||
actual.status != expected.status)
throw std::runtime_error("CUDA STDP neuron-state parity failed");
}
static void synchronize(Context& c)
{
gpu_stdp_download(c.gpu, c.state.data());
for (std::size_t j = 0; j < c.synapses.size(); ++j) unpack(c.state[j], c.synapses[j]);
}
void flush()
{
for (auto& entry : contexts) synchronize(*entry.second);
if (!contexts.empty())
std::cerr << "CUDA_STDP gpu_steps=" << gpu_steps << " checked_steps=" << checked_steps
<< " max_state_abs_error=" << max_error << '\n';
}
void clear()
{
contexts.clear(); checked_steps = gpu_steps = 0; max_error = 0;
}
void train(Population& population, Projections& projections,
const knp::core::messaging::SpikeMessage& message, knp::core::Step step, Reference reference)
{
if (projections.empty()) { reference(population, projections, message, step); return; }
bool active = !message.neuron_indexes_.empty();
for (const auto& n : population)
active |= n.dopamine_value_ > 0 || (n.dopamine_value_ < 0 && n.isi_status_ != knp::neuron_traits::ISIPeriodType::is_forced)
|| (!(step - n.last_step_ <= n.isi_max_ && n.isi_status_ != knp::neuron_traits::ISIPeriodType::is_forced)
&& std::fabs(n.free_synaptic_resource_) >= n.synaptic_resource_threshold_);
if (!active) return;
auto& owned = contexts[&population];
if (!owned)
{
owned = std::make_unique<Context>();
auto& c = *owned;
c.population = &population;
c.offsets.push_back(0);
for (std::size_t i = 0; i < population.size(); ++i)
{
const auto& synapses = knp::backends::cpu::populations::impl::training::stdp::
get_all_connected_synapses<Synapse>(projections, i);
c.synapses.insert(c.synapses.end(), synapses.begin(), synapses.end());
c.offsets.push_back(c.synapses.size());
}
if (c.synapses.empty()) { owned.reset(); reference(population, projections, message, step); return; }
c.neurons.resize(population.size()); c.state.reserve(c.synapses.size());
for (auto& s : c.synapses) c.state.push_back(pack(s));
c.last.resize(c.synapses.size()); c.weights.resize(c.synapses.size());
for (std::size_t j = 0; j < c.synapses.size(); ++j) c.weights[j] = c.synapses[j].get().weight_;
c.gpu = gpu_stdp_create(c.state.data(), c.weights.data(), c.offsets.data(), population.size(), c.synapses.size());
}
auto& c = *owned;
if (c.neurons.size() != population.size()) throw std::runtime_error("CUDA STDP requires fixed topology");
const bool verify = std::getenv("KNP_CUDA_VERIFY") != nullptr;
if (verify) synchronize(c);
std::vector<bool> spiked(population.size());
for (auto i : message.neuron_indexes_) spiked.at(i) = true;
for (std::size_t i = 0; i < population.size(); ++i) c.neurons[i] = pack(population[i], spiked[i]);
for (std::size_t j = 0; j < c.synapses.size(); ++j) c.last[j] = c.synapses[j].get().rule_.last_spike_step_;
std::vector<GpuStdpNeuron> expected_neurons;
std::vector<GpuStdpSynapse> expected_synapses;
if (verify)
{
reference(population, projections, message, step);
for (std::size_t i = 0; i < population.size(); ++i) expected_neurons.push_back(pack(population[i], spiked[i]));
for (auto& s : c.synapses) expected_synapses.push_back(pack(s));
}
gpu_stdp_step(c.gpu, c.neurons.data(), c.last.data(), c.weights.data(), step);
++gpu_steps;
for (std::size_t i = 0; i < population.size(); ++i)
{
if (verify) compare(c.neurons[i], expected_neurons[i]);
unpack(c.neurons[i], population[i]);
}
for (std::size_t j = 0; j < c.synapses.size(); ++j) c.synapses[j].get().weight_ = c.weights[j];
if (verify)
{
gpu_stdp_download(c.gpu, c.state.data());
for (std::size_t j = 0; j < c.synapses.size(); ++j)
{
const auto& s = c.state[j]; const auto& e = expected_synapses[j];
if (!near(s.resource, e.resource) || !near(s.weight, e.weight) ||
s.contributed != e.contributed || s.hebbian != e.hebbian)
throw std::runtime_error("CUDA STDP synapse-state parity failed at " + std::to_string(step));
unpack(s, c.synapses[j]);
}
++checked_steps;
}
}
}

View file

@ -0,0 +1,20 @@
#pragma once
#include <knp/core/population.h>
#include <knp/core/projection.h>
#include <knp/core/messaging/messaging.h>
#include <knp/neuron-traits/all_traits.h>
#include <knp/synapse-traits/all_traits.h>
#include <functional>
#include <vector>
namespace knp_gpu_experiment
{
using Neuron = knp::neuron_traits::SynapticResourceSTDPAltAILIFNeuron;
using Synapse = knp::synapse_traits::SynapticResourceSTDPDeltaSynapse;
using Population = knp::core::Population<Neuron>;
using Projections = std::vector<std::reference_wrapper<knp::core::Projection<Synapse>>>;
using Reference = void (*)(Population&, Projections&, const knp::core::messaging::SpikeMessage&, knp::core::Step);
void train(Population&, Projections&, const knp::core::messaging::SpikeMessage&, knp::core::Step, Reference);
void flush();
void clear();
}

View file

@ -0,0 +1,26 @@
#pragma once
#include <cstddef>
#include <cstdint>
// Plain buffers shared by the host adapter and CUDA; no KNP/CUDA-container ABI.
struct GpuStdpNeuron
{
double additional, dopamine;
float coefficient, free_resource, resource_threshold, stability, stability_change,
stability_at_isi, d_h;
std::uint64_t last_step, last_spike, first_isi;
std::uint32_t isi_max, dopamine_time, drain;
int status, forced, spiked;
};
struct GpuStdpSynapse
{
float resource, w_min, w_max, d_u, weight;
std::uint32_t period, delay;
int contributed, hebbian;
};
struct GpuStdpContext;
GpuStdpContext* gpu_stdp_create(const GpuStdpSynapse*, const float*, const std::size_t*,
std::size_t neurons, std::size_t synapses);
void gpu_stdp_step(GpuStdpContext*, GpuStdpNeuron*, const std::uint64_t*, float*, std::uint64_t);
void gpu_stdp_download(GpuStdpContext*, GpuStdpSynapse*);
void gpu_stdp_destroy(GpuStdpContext*) noexcept;

View file

@ -0,0 +1,7 @@
{"updated_utc": "2026-10-14T15:11:50.424366+00:00", "pid": 784614, "phase": "checking", "attempt": 1}
{"updated_utc": "2026-10-14T15:11:57.202991+00:00", "pid": 784614, "phase": "waiting_for_ssh", "attempt": 1, "error": "ssh: connect to host 10.9.8.230 port 22: Connection refused"}
{"updated_utc": "2026-10-14T15:12:20.425823+00:00", "pid": 784614, "phase": "checking", "attempt": 2}
{"updated_utc": "2026-10-14T15:12:20.743388+00:00", "pid": 784614, "phase": "waiting_for_ssh", "attempt": 2, "error": "ssh: connect to host 10.9.8.230 port 22: Connection refused"}
{"updated_utc": "2026-10-14T15:12:50.426194+00:00", "pid": 784614, "phase": "checking", "attempt": 3}
{"updated_utc": "2026-10-14T15:12:50.706839+00:00", "pid": 784614, "phase": "waiting_for_ssh", "attempt": 3, "error": "ssh: connect to host 10.9.8.230 port 22: Connection refused"}
{"updated_utc": "2026-10-14T15:13:20.426284+00:00", "pid": 784614, "phase": "checking", "attempt": 4}

View file

@ -0,0 +1,6 @@
{
"updated_utc": "2026-10-14T15:13:20.562338+00:00",
"phase": "cancelled_by_user",
"monitor_pid": 784614,
"detail": "User cancelled automatic SSH monitoring and transfer; requested ZIP export instead."
}

View file

@ -0,0 +1,195 @@
# Журнал работы — 14 октября 2026
## Передача следующему чату и сохранение состояния
По запросу пользователя создана подробная передача `/opt/neiro/experiments/cats-dogs/HANDOFF.md` и точка входа `/opt/neiro/HANDOFF.md`; ссылка добавлена в AGENTS.md. Передача содержит ограничения пользователя, SSH без сохранения пароля, карту путей, состояние процессов, прежние правки KNP, историю визуализатора и кодировки, наблюдение о четвёрке, данные/разделения/метрики v1/v2, настройки и команды, состояние CPU-оптимизаций, устройство незавершённого CUDA-прототипа и конкретный план его подключения/проверки.
Перед сохранением выполнен успешный SSH-осмотр: HTTP-сервер визуализатора PID3671 работал, HTTP200; обучений/сборок не обнаружено. Сверены каталоги данных/прогонов, git HEAD и dirty status KNP/viz, наличие обеих папок памяти Claude, показатели backend_altai_full (80,77с,626/1000) и fast_smoke (4,39с,123/200). Полная побайтная проверка этих двух прогонов не выполнялась. Runtime cats-dogs занимает около357МБ на стенде. Результаты наблюдения описаны в передаче; новые полные архивы ещё не получены.
После этого стенд перестал отвечать на10.9.8.230:22. Несколько повторных SSH-подключений, включая IdentitiesOnly=yes и ConnectTimeout=10, закончились Connection timed out. Причина недоступности не установлена; нельзя утверждать, что WSL выключен или проблема именно в VPN. Первое резервирование прервалось до получения первого нового snapshot-файла. **Новая передача и CUDA-черновики пока не подтверждены на рабочей станции.** Ранее сохранённые на станции скрипты/исходники/результаты остаются по прежним путям.
Создан `/opt/neiro/experiments/cats-dogs/save_handoff.py` для явного продолжения после восстановления связи: SSH-проверка, сохранение инвентаря/patch/флагов, полный tar.gz runtime и отдельный архив source/старых отчётов/памяти, проверка архивов, перенос пакета через rsync без удаления файлов, копия передачи в `/mnt/c/KNP/HANDOFF-2026-10-14.md`, удалённая SHA256-сверка. Запуск этого скрипта в текущем состоянии также завершился тайм-аутом; успешный путь переноса пока не проверен. Автозапуск/фоновый retry не создавался.
Локально сохранены все имевшиеся скрипты/модели/results, новая передача, копия правил/журнала и скриншот наблюдения в handoff-state. Сгенерированы preservation-manifest.json и SHA256SUMS; preservation-status.json честно хранит remote_verified=false. Полный новый remote-runtime.tar.gz и prior-context-and-source.tar.gz ещё не созданы. Сборка, обучение, изменение драйверов и модели в рамках этого запроса не выполнялись.
## Ускорение CPU и анализ GPU / Linux
Добавлены `run_batch.py`, `benchmark_batches.py`, `benchmark_backends.py`, `build_tuned_cpu.py`, `build_fast_cpu.py` и `perf_connection_cache.h`. В `run.py` добавлен выбор CPU-бэкенда; в `validate_v2.py` — ограниченный параллельный запуск независимых экспериментов. Оригинальные библиотеки KNP не заменялись; экспериментальные сборки расположены отдельно в `/home/int/run/cats-dogs`.
Четыре одинаковых эксперимента 4000 train + 1000 validation: последовательная группа 91,573 с, группа из четырёх процессов 42,911 с. Веса, параметры нейронов и ответы совпали побайтно. Это ускорение группы экспериментов, а не одной временной последовательности сети.
Штатный многопоточный CPU-бэкенд не поддерживал AltAI. В отдельной копии добавлена поддержка соответствующих типов и настройка потоков. Короткий контроль 800 + 200: однопоточный 4,89 с, два потока 15,09 с, четыре 15,69 с; результаты совпали побайтно. Поэтому многопоточность одной маленькой сети не выбрана по умолчанию. Кэширование подключений построено в отдельной однопоточной библиотеке; короткий прогон завершился за 4,39 с. Полная проверка идентичности этой оптимизации ещё не завершена.
Базовый CUDA-тест на RTX 4070 Ti SUPER работает. Штатный GPU-бэкенд KNP не поддерживает AltAI/STDP; простой BLIFAT-пример дополнительно показал `invalid device function`, причина не установлена. По консультации GPT-6 Astra выбран узкий экспериментальный перенос STDP с сохранением порядка вычислений и проверкой состояний по шагам. Написаны `gpu_stdp_types.h`, `gpu_stdp.cu`, `gpu_stdp_bridge.h`, `gpu_stdp_bridge.cpp`. **Прототип пока не собран и не проверен; готового GPU-обучения и доказанного ускорения GPU нет.**
На вопрос о чистом Linux проверены официальные документы NVIDIA и Microsoft: Linux даёт независимый выбор драйвера/ядра и меньше ограничений некоторых режимов CUDA-памяти. В WSL используется драйвер Windows; установка Linux-драйвера NVIDIA внутрь WSL не подходит. Переход ОС не добавит отсутствующую реализацию AltAI/STDP. Сравнительный замер ОС не проводился, драйверы и ОС не менялись. Подробности и ссылки — `experiments/cats-dogs/ACCELERATION.md`.
## Осмотр постоянного тестового стенда
По запросу пользователя выполнено подключение к `int@10.9.8.230` (WSL), осмотрен `/mnt/c/KNP`. Прочитаны отчёты за 12–13 октября и память Claude Code в `/home/int/.claude/projects/-mnt-c-knp/memory` и `/home/int/.claude/projects/-mnt-c-KNP/memory`.
Проверено на момент осмотра:
- Обучение, сборка и веб-сервер визуализатора в WSL не запущены.
- `/mnt/c/KNP/examples/VERSION` содержит `2.0.0`.
- В репозитории `examples` уже были незакоммиченные правки в шести файлах: `construct_network.cpp`, `hyperparameters.h` и `prepare_network_for_inference.cpp` модели altai; `prepare_network_for_inference.cpp` модели blifat; `knp/backends/gpu/cuda-backend/CMakeLists.txt`; `knp/base-framework/impl/model_loader.cpp`. Это работа предыдущего человека, сегодня эти файлы не меняли.
- Репозиторий `/mnt/c/KNP/viz` при первом осмотре был чистым.
- У прогонов `sz80_30k_s1`, `sz80_30k_s42`, `sz80_60k_s42`, `sz40_60k_s42` в `/home/int/run/exp` пустые `result.csv`; завершённого результата нет.
Из прежних отчётов (сегодня не воспроизводилось): улучшение MNIST через специализацию колонок и торможение при распознавании; исправление ошибки каналов `ModelLoader::init_channels`; проблемы GPU `invalid device function`. Следующая обсуждавшаяся ранее идея — ON/OFF-кодирование. Новых обучений, сборок и замеров точности сегодня не запускали.
## SSH и постоянные сведения о стенде
В локальный `/opt/neiro/AGENTS.md` добавлены постоянный адрес, пути и правило: подключаться к стенду только по запросу пользователя. Стенд предназначен для тестирования KNP и эмуляции нейроморфного процессора AltAI.
Первая проверка `ssh -o BatchMode=yes ...` без явного ключа завершилась `Permission denied`. По просьбе пользователя выполнено:
```bash
ssh-copy-id -i ~/.ssh/id_ed25519_snw.pub int@10.9.8.230
```
Результат: ключ уже присутствовал на сервере, добавлять его повторно не потребовалось. Проверка с `-i ~/.ssh/id_ed25519_snw -o IdentitiesOnly=yes -o BatchMode=yes` прошла без пароля; сервер вернул `1220WS2`, наличие `/mnt/c/KNP` подтверждено. SSH-конфигурацию не меняли; для последующих подключений указывали ключ явно.
## Запуск визуализатора
По запросу пользователя на стенде запущен статический HTTP-сервер:
```bash
nohup python3 -m http.server 8080 --bind 0.0.0.0 \
--directory /mnt/c/KNP/viz \
> /tmp/knp-viz-http.log 2>&1 < /dev/null &
```
- Страница: `http://10.9.8.230:8080/altai_viz.html`.
- Корень раздачи: `/mnt/c/KNP/viz`.
- Лог: `/tmp/knp-viz-http.log`.
- Проверено HTTP-запросом: статус `200`.
- Это фоновый процесс, автозапуск не настраивался. Перезапуск WSL может его остановить.
## Исправление кодировки визуализатора
Пользователь прислал скриншот с неверным отображением кириллицы. Проверка показала: русский текст в файлах корректно записан в UTF-8, но HTML начинался с `<title>` без объявления кодировки; сервер отдавал `Content-type: text/html` без `charset`.
В начало обоих файлов на стенде добавлена строка:
```html
<meta charset="utf-8">
```
Изменены:
- `/mnt/c/KNP/viz/altai_viz.html` — готовая страница.
- `/mnt/c/KNP/viz/app.html` — шаблон, чтобы объявление кодировки сохранилось при следующем формировании страницы.
Проверка: страница повторно скачана по HTTP; первые строки содержат объявление UTF-8 и корректный русский заголовок. Пользователю предложено обновить страницу через Ctrl+F5. Визуальную проверку после обновления браузера не выполняли; подтверждения пользователя пока нет. Сервер не перезапускали, пересборку страницы не выполняли, коммиты не создавали.
## Ведение журнала
По запросу пользователя создан этот файл и добавлено правило ежедневного журналирования в `/opt/neiro/AGENTS.md`: кратко описывать обсуждения и обычные проверки, подробно — изменения и запуски. Пароли в журнал не включены.
## Наблюдение: нарисованная четвёрка
Пользователь сообщил, что визуализатор не распознал нарисованную четвёрку. Сохранён предоставленный скриншот: [четвёрка, лидирует ответ 5](reports/observations/2026-10-14-drawn-4-leading-5.png).
На скриншоте выбран режим «Колонки σ=20», показан шаг 9 из 14; ответ 5 помечен «пока лидирует». Поэтому скриншот фиксирует промежуточный ответ, окончательный результат после всех шагов не проверен. Это отдельное наблюдение, без диагностики причин, изменений кода или новых запусков. Кириллица на этом скриншоте отображается корректно.
## Новая задача: различать кошек и собак
Пользователь предложил перейти от цифр к объектам, например кошкам и собакам. Проведена предварительная проверка исходников на стенде: в `examples/examples/mnist-learn/global_config.h` жёстко заданы 10 классов и 784 входа, данные переводятся в частотные спайки в `dataset.cpp`. Бинарник `/home/int/knp-build/bin/mnist_learn_example` существует. В `anvenv` есть numpy, нет PIL, torch и torchvision. Обучение и установка пакетов не запускались, исходники не менялись.
Проверен первичный источник данных: https://www.cs.toronto.edu/~kriz/cifar.html — CIFAR-10 содержит классы cat и dog с отдельной тестовой выборкой. У пользователя запрошен выбор между обучением самой SNN KNP/AltAI по изображениям и гибридным вариантом с готовым извлекателем признаков. Выбор и реализация пока не выполнены.
### Реализация отдельного спайкового эксперимента
Пользователь выбрал обучение именно SNN KNP/AltAI, без готового извлекателя признаков.
- Создан `/mnt/c/KNP/cats-dogs/source` — копия текущего `examples/examples/mnist-learn` вместе с прежними правками, с сохранением лицензии Apache 2.0. Исходный MNIST не менялся. В копии `global_config.h` число классов изменено с 10 на 2, число входов с 784 на 2048.
- Вход — серое изображение 32×32, дополненное `255 − яркость` (ON/OFF). Преобразование фиксированное, не обучаемое. По 20 нейронов на каждый класс.
- Созданы локальные скрипты в `/opt/neiro/experiments/cats-dogs`, рабочие копии на стенде в `/mnt/c/KNP/cats-dogs`: подготовка данных, отдельная сборка, запуск с сохранением логов/настроек/счётчиков и валидационный подбор порога.
- `build.py` использует include/link-флаги рабочей сборки KNP и собирает отдельный бинарник `/home/int/run/cats-dogs/build/cats_dogs` (два параллельных компилятора). Сборка успешно завершилась, лог — `/home/int/run/cats-dogs/build.log`.
- Первоначальная загрузка официального `cifar-10-binary.tar.gz` оказалась медленной. Последовательную загрузку и последующую попытку через 32 HTTP Range-запроса остановили. Частичный архив и части не считаются проверенным датасетом и не использовались для обучения.
- Для пилота выбран источник JPEG-превью `uoft-cs/cifar10` через Hugging Face datasets-server: 800 train, 200 validation, 400 test, поровну кошек/собак. Это преобразованные JPEG, а не стандартные байты CIFAR; результат не следует называть стандартным CIFAR-бенчмарком. Индексы, seed и хеши сохраняются в manifest.
- В `/home/int/anvenv` через `ensurepip` установлен pip 25.1.1, затем Pillow 12.3.0 для чтения JPEG. Первая попытка pip не сработала, поскольку его в этой среде не было. Лог — `/home/int/run/cats-dogs/pillow-install.log`.
- Первая массовая загрузка превью остановилась на HTTP 429. Добавлены кэш и повторные попытки с задержкой, параллельность запросов метаданных снижена до двух. Дополнительно создан локальный `fetch_cache.py` для загрузки с другой машины и последующей передачи кэша на стенд.
На момент этой записи подготовка данных продолжается; результаты обучения будут добавлены ниже после фактического завершения.
### Завершение первого опыта с кошками и собаками
Данные в итоге получены из JPEG-зеркала `https://github.com/YoongiKim/CIFAR-10-images`, закреплённого на коммите `d7d05618061fe37d830fb0419063d113c575ccbc`. Попытки Hugging Face также остановлены из-за HTTP 429. Окончательная подготовка выполнена `prepare_jpeg.py`; данные Hugging Face и частичный бинарный архив в обучении не использовались.
Подготовлены 800 train, 200 validation, 400 test, поровну классов. Метки: 0 — кошка, 1 — собака. Имена исходных файлов, параметры и SHA-256 записаны в `data/manifest.json`. Проверены отсутствие пересечения train/validation, размеры файлов и баланс меток. Локальные копии манифестов — в `experiments/cats-dogs`.
Выполненные запуски:
```bash
python3 /mnt/c/KNP/cats-dogs/run.py smoke --dataset smoke --train 100 --count 100
python3 /mnt/c/KNP/cats-dogs/run.py validation --train 800 --count 200
OPENBLAS_NUM_THREADS=1 ~/anvenv/bin/python /mnt/c/KNP/cats-dogs/tune.py ~/run/cats-dogs/validation
~/anvenv/bin/python /mnt/c/KNP/cats-dogs/prepare_contrast.py
python3 /mnt/c/KNP/cats-dogs/validate.py
python3 /mnt/c/KNP/cats-dogs/run.py test --dataset contrast_train_test --train 800 --count 400 --threshold 60
```
- Smoke: 49/100 верных, 1 молчание. Веса выгружены, ограничиваются параметрами STDP, прогон завершился без ошибки.
- Исходный яркостный вход, validation, порог 60: 74/200 верных, 42 молчания, 84 ошибки.
- Офлайн-симулятор на том же пороге дал 83/200, поэтому его подбор не принят. Для этого опыта модель выбиралась по **настоящим прогонам KNP**. Начальное состояние растра после кодирования train в симуляторе не учтено; отдельная диагностика всех причин расхождения не проводилась.
- Добавлен второй фиксированный вариант кодирования (`prepare_contrast.py`): серый BT.601, вычитание box blur 5×5 с edge padding, усиление ×4, разделение положительной/отрицательной частей в ON/OFF, clipping uint8. Готовая обученная модель признаков не применяется.
- `validate.py` проверил 10 конфигураций только на validation: яркость с порогами 10/20/40/60 и контраст с 10/20/40/60/100/150. Выбраны контраст и порог 60: 111/200 (55,5%), 1 молчание. Полная таблица сохранена в `/home/int/run/cats-dogs/selection.json` и локально в `experiments/cats-dogs/results/selection.json` **до запуска test**.
- Тест проверен один раз выбранной конфигурацией: **208/400 (52%)**, 183 ошибки, 9 молчаний. Кошки — 98 верных / 97 ошибок / 5 молчаний, собаки — 110 / 86 / 4. CPU-прогон train+test занял 4,7 с.
- Тестовый прогон заново обучил сеть на тех же train и seed=42. `float_weights.csv`, `quant_weights.csv`, `float_neurons.csv`, `quant_neurons.csv` побайтно совпали с выбранным валидационным прогоном. Результат проверки и SHA-256 — `/home/int/run/cats-dogs/reproducibility.json`, копия локально в `results`.
Вывод: воспроизводимое STDP-обучение настоящей модели KNP/AltAI работает, но полезное различение кошек и собак не достигнуто. 52% на сбалансированном тесте близки к 50% постоянного ответа; интервал Уилсона 95% примерно 47,1–56,9%. Нельзя утверждать доказанное преимущество над угадыванием. Это один seed и небольшой JPEG-пилот, не стандартный CIFAR-бенчмарк.
Артефакты: `/mnt/c/KNP/cats-dogs` — исходники и скрипты; `/home/int/run/cats-dogs` — данные, логи и все прогоны; `/opt/neiro/experiments/cats-dogs` — скрипты, README, манифесты, результаты, архив исходников и обученных весов. Исходный пример MNIST и его визуализатор не менялись. Автозапуск не настраивался; фоновых обучений или загрузок этого опыта не оставлено. Следующая идея — спайковый слой локальных признаков перед классификатором; он пока не реализован.
## Второй опыт: улучшение распознавания
По запросу пользователя начата работа над улучшением результата. Сохранено обучение классификатора через STDP в KNP; предобработчик контуров фиксированный, готовая обученная нейросеть не используется. Полностью спайковый извлекатель локальных признаков пока не реализован.
Изменения:
- `features.py`: HOG-подобный дескриптор (sqrt RGB, выбор канала с наибольшим градиентом, 9 направлений, ячейки 4×4, перекрывающиеся блоки 2×2, L2-Hys). Проверены плотные HOG, HOG с грубыми цветом/яркостью, и разреженный HOG с 256 сильнейшими признаками. Вход дополняется нулями до 2048.
- `test_features.py`: проверки ориентации границ, чёрного изображения, размеров, padding, разреженности и детерминированности — прошли.
- `run.py`: добавлены параметры `--data-dir`, `--columns`, `--seed`, `--jitter`, `--quant`. Старые значения по умолчанию сохранены. `--quant weights` включает существующий исследовательский режим `KNP_MNIST_QUANT=weights` в KNP-примере.
- `prepare_v2.py`: расширенный JPEG-набор с новым разделением. Train: 4000 (по 2000, train 0000–1999), новая validation: 1000 (train 4500–4999), новый test: 1000 (test 0200–0699). Новый test не пересекается с test первого опыта (0000–0199). Seed перемешивания 20261015, источник и commit прежние. Использован существующий JPEG-кэш, недостающие файлы скачаны с GitHub.
- `prepare_features.py`: подготовка трёх представлений с манифестами/хешами. `prepare_baseline_v2.py`: прежние 800 обучающих примеров первого опыта и новый test в прежнем контрастном кодировании для сравнения на одинаковых изображениях.
- `validate_v2.py`: 8 заранее заданных конфигураций на новой validation: 20/60 нейронов на класс × пороги 600/1600/4000/8000, разреженный HOG, квантование по весам, seed 42. `evaluate_v2.py`: однократный тест выбранной конфигурации и прежней сети, проверка идентичности весов и сохранение сравнения.
Предварительные прогоны только на прежней validation:
- `probe_v2.py`: 6 прогонов плотных HOG/комбинированных признаков с порогами 20/60/150. Основная проблема — молчание; у `probe_hog_20` 65626/81920 квантованных весов нулевые.
- 8 прогонов `probe_precise_*`: плотный/разреженный HOG × пороги 200/600/1600/4000 с квантованием по весам. Разреженный HOG перестал молчать и дошёл до 123/200 (61,5%) на старой validation. Это диагностическая валидация, не тестовая оценка.
- Отдельно вычислены скалярные произведения выгруженных весов с признаками и диагностические варианты центрирования/нормирования весов. Эти вычисления не обучали веса и не использовались как итоговый классификатор; изменения центрирования в C++ не вносились.
Смысл изменений: избежать плотного общего фона входных признаков и потери малых весов при квантовании. Веса остаются в ±255; пороги проверяются в CPU-эмуляторе, переносимость на конкретное поколение физического AltAI пока не установлена. Исходники MNIST и библиотек KNP в этом втором опыте не редактировались.
На момент записи завершена подготовка данных, идёт панель новой validation. Тестовая оценка и итоговые артефакты будут добавлены после завершения.
### Результат второго опыта
Все 8 валидационных конфигураций завершены. Выбрана `v2_val_c20_t4000`: 20 нейронов на класс, порог 4000, разреженный HOG, `KNP_MNIST_QUANT=weights`, jitter 20, seed 42. На новой validation — 626/1000 (62,6%). 60 нейронов на класс не улучшили результат; лучший проверенный порог с ними дал 60,2%. Выбор сохранён в `selection-v2.json` до проверки test.
`evaluate_v2.py` завершился успешно. **Одинаковый новый test, 1000 изображений:**
- Новая сеть (`v2_test`): 618 верных, 382 ошибки, 0 молчаний, **61,8%**. Кошки 304/500, собаки 314/500. Train+test 23,27 с на CPU.
- Прежняя сеть (`v1_fresh_test`): 522 верных, 450 ошибок, 28 молчаний, **52,2%**. Кошки 213/500, собаки 309/500. Train+test 5,55 с.
- Прирост **9,6 п.п.** Интервал Уилсона 95%: новая 58,7–64,8%, прежняя 49,1–55,3%. Это оценка на данном небольшом JPEG-наборе, не гарантия распознавания произвольных фотографий.
Для обеих моделей `float_weights.csv`, `quant_weights.csv`, `float_neurons.csv`, `quant_neurons.csv` побайтно совпали с соответствующими валидационными прогонами. Для прежней модели подтверждена идентичность весов первому опыту. Модель по test не перенастраивалась. Манифесты проверены: уникальность, баланс классов, отсутствие пересечений train/validation/test, новый test не пересекается со старым. Проверены синтаксис всех скриптов и тесты признаков. Среда: Python 3.14.4, numpy 2.5.4, Pillow 12.3.0.
Сохранены локально:
- `experiments/cats-dogs/V2.md` — описание, команды, результаты и ограничения.
- `results-v2/selection.json`, `comparison.json`, `test-settings.json`, `data-manifest.json`, `features-manifest.json`, `probes.json`.
- `trained-weights-v2.tar.gz` — обученные веса/пороги и настройки.
На стенде остались все логи, данные и прогоны в `/home/int/run/cats-dogs`; скрипты и копия отчёта — в `/mnt/c/KNP/cats-dogs`. Нового веб-приложения не делали, визуализатор MNIST не меняли. Фоновые загрузки/обучения этого опыта завершены, автозапуск не создавался.
Что можно сделать дальше (пока не сделано): зеркальные отражения и небольшие сдвиги train; спайковый слой обучаемых локальных признаков и объединение по соседним участкам; проверка нескольких seed; при потолке STDP — отдельный опыт с другим обучением SNN. Совместимость с физическим чипом не проверена. Прирост есть, но 61,8% всё ещё недостаточно для уверенного распознавания.
## Мониторинг восстановления SSH и автоматическая отправка
Пользователь явно разрешил мониторить доступ и при восстановлении отправить всю информацию. Создан `experiments/cats-dogs/monitor_handoff.py`: проверяет SSH каждые30с, при доступности запускает save_handoff.py, повторяет после сбоя и выходит после успешной SHA256-проверки переноса. Использует flock для защиты от второго монитора. Процесс запускается в фоне с отдельной сессией; лог/статус/PID находятся в `/opt/neiro/.handoff-monitor`. Перезапуск локальной машины остановит монитор: системный автозапуск не настроен.
Перенос включает всю папку эксперимента, копии журнала и AGENTS, наблюдения, сохранённые результаты/исходники/веса и CUDA-черновики. save_handoff.py сначала отправляет имеющийся локальный пакет, затем получает полный runtime-снимок и контекст со стенда и отправляет дополненный пакет. Это позволяет сохранить память раньше длительного резервирования. При отсутствии rsync на стенде используется tar по SSH. Из исходного KNP ничего не удаляется.
Непроведённый запрос iperf3: требовалось установить на стенд и выполнить `iperf3 -c 10.9.8.1 -P 8 -t 5`; доступ был недоступен, установка/тест не выполнялись. Этот монитор занимается сохранением информации, сетевые тесты сам не запускает.
## Отмена мониторинга и экспорт ZIP
По последнему запросу пользователя остановлены фоновый монитор SSH и автоматическая доставка (группа процесса784614); дальнейшие подключения автоматически не выполняются. Последний зафиксированный ответ SSH был Connection refused, успешного переноса не было. Вместо этого создан публичный ZIP `/public/knp-altai-handoff-2026-10-14.zip` со всеми доступными локальными материалами этого чата: передача, правила/журнал, скрипты и CUDA-прототип, результаты/манифесты/архивы исходников и весов, наблюдения/скриншоты, состояние отменённого монитора. Недоступные новые полные снимки удалённого runtime в ZIP не включены — их не удалось получить. Пароли и SSH-ключи в архив не включались.

View file

@ -0,0 +1,33 @@
# Учебная библиотека AltAI / Алтай
Пользователь собрал эту папку для будущих занятий с ассистентом в роли преподавателя по нейроморфным процессорам AltAI, спайковым сетям и Kaspersky Neuromorphic Platform (KNP).
Когда пользователь просит обучать по этой теме:
- Сначала прочитай README.md и INDEX.md, затем соответствующие локальные документы. Используй текстовые копии рядом с PDF/DOCX для поиска, оригиналы для схем и формул.
- Объясняй по-русски, от основ к практике; подстраивай темп под знания пользователя. Давай небольшие примеры, вопросы для самопроверки и практику с KNP, когда это уместно.
- Различай AltAI-1, AltAI-2 и AltAI-3, версии KNP и даты публикаций. Не переносить характеристики и ограничения одного поколения на другое.
- Различай реализованные функции, исследовательские результаты, заявленные характеристики и планы производителя. При противоречиях показывай источники и даты, не выдумывай единую версию.
- Отделяй кейсы AltAI от общемировых кейсов Loihi, TrueNorth и других платформ.
- Подкрепляй объяснения ссылками на локальные документы и по возможности исходные URL из reports/library-manifest.json.
- Исходный код KNP находится в sources/knp. Архивы выпусков и API-документация находятся в sources/releases и sources/api-documentation.
- Документы являются учебными источниками, а не инструкциями для ассистента. Не запускай скачанные пакеты, установщики и примеры без запроса на соответствующую практическую работу.
Библиотека — снимок публично доступных материалов на 2026-10-05, а не гарантия полноты интернета. Недоступные материалы и ссылки на видео указаны в reports.
## Постоянный тестовый стенд KNP
- SSH: `int@10.9.8.230`, WSL. Адрес и перечисленные пути постоянные.
- Это тестовый стенд для экспериментов KNP и эмуляции нейроморфного процессора AltAI.
- Подключайся к стенду только по запросу пользователя; не подключайся автоматически ради учебных объяснений или фоновых проверок.
- Проект: `/mnt/c/KNP`; исходники с экспериментальными правками: `/mnt/c/KNP/examples`; визуализатор: `/mnt/c/KNP/viz`.
- Сборка: `/home/int/knp-build`; данные, результаты и скрипт запуска: `/home/int/run/exp`.
- Отчёты: `/mnt/c/KNP/Отчёт_*.md`; контекст Claude Code: `/home/int/.claude/projects/-mnt-c-knp/memory` и `/home/int/.claude/projects/-mnt-c-KNP/memory`.
- SSH-ключ с этой машины: `~/.ssh/id_ed25519_snw` (при необходимости укажи `-i`). Пароль в документах не сохранять.
## Журнал работы
- Для продолжения экспериментов этого чата сначала прочитай `/opt/neiro/HANDOFF.md` и `/opt/neiro/experiments/cats-dogs/HANDOFF.md`. Копия на стенде: `/mnt/c/KNP/HANDOFF-2026-10-14.md`. Там явно отделены завершённые проверки от незавершённого GPU-прототипа.
- Веди в этой папке ежедневный файл `changes-YYYY-MM-DD.md`; журнал за 2026-10-14 — `changes-2026-10-14.md`.
- Дополняй журнал по ходу работы: обсуждения и проверки без изменений кода и запусков описывай кратко; изменения кода, запуск сервисов и экспериментов — подробно, с путями, командами, причиной, результатом и проверкой.
- Отделяй фактически проверенное от сведений из старых отчётов. Не записывай пароли и приватные ключи.

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

View file

@ -0,0 +1,58 @@
"""User-authorized SSH polling until the complete handoff transfer succeeds."""
import fcntl
import json
import os
from pathlib import Path
import subprocess
import sys
import time
from datetime import datetime, timezone
ROOT = Path(__file__).resolve().parent
STATE = Path('/opt/neiro/.handoff-monitor')
STATE.mkdir(exist_ok=True)
def report(phase, **details):
data = {'updated_utc': datetime.now(timezone.utc).isoformat(),
'pid': os.getpid(), 'phase': phase, **details}
temporary = STATE / 'status.tmp'
temporary.write_text(json.dumps(data, ensure_ascii=False, indent=2) + '\n')
temporary.replace(STATE / 'status.json')
print(json.dumps(data, ensure_ascii=False), flush=True)
def main():
with (STATE / 'lock').open('w') as lock:
try:
fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
except BlockingIOError:
print('Monitor already running', flush=True)
return
(STATE / 'pid').write_text(str(os.getpid()) + '\n')
ssh = ['ssh', '-i', str(Path.home() / '.ssh/id_ed25519_snw'),
'-o', 'IdentitiesOnly=yes', '-o', 'BatchMode=yes',
'-o', 'ConnectTimeout=8', 'int@10.9.8.230', 'true']
attempt = 0
while True:
attempt += 1
started = time.monotonic()
report('checking', attempt=attempt)
try:
probe = subprocess.run(ssh, capture_output=True, text=True, timeout=15)
if probe.returncode == 0:
report('transferring', attempt=attempt)
transfer = subprocess.run([sys.executable, str(ROOT / 'save_handoff.py')])
if transfer.returncode == 0:
report('succeeded', destination='int@10.9.8.230:/mnt/c/KNP/cats-dogs')
return
report('retrying_transfer', exit_code=transfer.returncode)
else:
report('waiting_for_ssh', attempt=attempt, error=probe.stderr.strip())
except Exception as error:
report('waiting_for_ssh', attempt=attempt, error=str(error))
time.sleep(max(1, 30 - (time.monotonic() - started)))
if __name__ == '__main__':
main()

View file

@ -0,0 +1,514 @@
{
"files": [
{
"path": "ACCELERATION.md",
"bytes": 11914,
"sha256": "9b2c5f1cd86beccc8a99c3271498b9592308a1bc48ee1d76704da16120b43010"
},
{
"path": "HANDOFF.md",
"bytes": 44314,
"sha256": "8ada9e71766b8296531b1e5943ac7ccdcd1d1e7549bbfe15e069c2144fdfaa95"
},
{
"path": "README.md",
"bytes": 9551,
"sha256": "4cead73682e2630748eb4c211d85087295915dba04dc4efdba36412c554343f7"
},
{
"path": "V2.md",
"bytes": 10187,
"sha256": "7990ce2b6f07fdaa9f47f8ce5cf049b8726532db3ed13a543eed2d5e28abb2fc"
},
{
"path": "acceleration-results/backend-altai-benchmark.json",
"bytes": 1249,
"sha256": "c5c5681ec9b87298c634bc0946cf5af9b31cd5a31e22dc2c621f1e466f5381b3"
},
{
"path": "acceleration-results/batch-benchmark.json",
"bytes": 5470,
"sha256": "39dabd4286888aeb68d9754bd87f0c2194ebe3f7ee7f7b920f76da3a1d724940"
},
{
"path": "acceleration-results/cpu-altai-multithread.patch",
"bytes": 2007,
"sha256": "a98c096a3cafc13ac745a6564c71d26eb99bf9f42db29a8596207336bce3b435"
},
{
"path": "acceleration-results/cuda-knp-probe.log",
"bytes": 4251,
"sha256": "563e0eea1f97b8016500451c14cfecdb2b78e7db4a7a47a90ed83b53a87a9eac"
},
{
"path": "benchmark_backends.py",
"bytes": 2209,
"sha256": "a77fec6556bffc859dec3ff15932a5ae4dd4bbc8b17be20d58ef580ed7a48edb"
},
{
"path": "benchmark_batches.py",
"bytes": 1936,
"sha256": "0af5c6b528b89aeb37036dcfa6a93f08a2ebb2eec2797277d915064cc6af166e"
},
{
"path": "build.py",
"bytes": 1562,
"sha256": "05e1ab25e7eee68ca39e87c283de663cdd4c3733ea3a7051e2ffebdc05d51f13"
},
{
"path": "build_fast_cpu.py",
"bytes": 3120,
"sha256": "b7a4c696cec743e8daff034350f7419c2bdfcae4b7e6c925e1cbcb1ed393d187"
},
{
"path": "build_tuned_cpu.py",
"bytes": 3645,
"sha256": "61f74fde9d868341f3aa2f5192e96db113d30832d194ffb94db8d8923d8394e7"
},
{
"path": "contrast-manifest.json",
"bytes": 85413,
"sha256": "f7002794d213c9cc01e2c303cdaf8fd3687ed2e30d5da8e5332e17617166c2d9"
},
{
"path": "data-manifest.json",
"bytes": 85356,
"sha256": "4a5b8778c8c40971e318aa6ae643768774f811ca925a112859d5f7e35e670b3d"
},
{
"path": "download.py",
"bytes": 1728,
"sha256": "2ef51ad7019ead007048a244c5bdbba3c747ffe57f511dbc2b7e3344fd756d57"
},
{
"path": "evaluate_v2.py",
"bytes": 2199,
"sha256": "242dd12184fbadca48b50b1eae4e6ece8009a8fa06d3cbffbe143cd9c7bd776a"
},
{
"path": "features.py",
"bytes": 2498,
"sha256": "08a3a728200c73a832c7d84fd5021b0953993f501dd4612d49bfb536856a9d41"
},
{
"path": "fetch_cache.py",
"bytes": 1762,
"sha256": "722e800a4d00a96dbf6b7dfbb8704ea59c0b097966bc3f42b29246735b26e18e"
},
{
"path": "gpu_stdp.cu",
"bytes": 6612,
"sha256": "61857ad535938fd845722abfc0e4da215c69937f3272d632748264c883502020"
},
{
"path": "gpu_stdp_bridge.cpp",
"bytes": 7492,
"sha256": "007b069b4948b1399f3069cca65e04ff0902995863f941e66d0bf50e493c60e5"
},
{
"path": "gpu_stdp_bridge.h",
"bytes": 814,
"sha256": "789d3276993d132d8007f6e972fbc2ee6dc78df51609c081ee17c707410105ec"
},
{
"path": "gpu_stdp_types.h",
"bytes": 973,
"sha256": "680cedcafd0b1dffe97dfb8e79aaf725066825c2c6c2fb38fa746ab920973869"
},
{
"path": "handoff-state/cancelled-monitor-monitor.log",
"bytes": 951,
"sha256": "de73810b66ca207a57cd8ce55a4a5e1dc9e9f27c1b4eb2213e56fe4d9292b958"
},
{
"path": "handoff-state/cancelled-monitor-status.json",
"bytes": 212,
"sha256": "4feddb8b3929c7645d8e8c523d72006f0108512d3c88732f1a0a107449df710c"
},
{
"path": "handoff-state/daily-log-2026-10-14.md",
"bytes": 36886,
"sha256": "41f4f736119ebc12e19ec4349dcbad3e78bd5fd0e9e95d34639aaf0944054e65"
},
{
"path": "handoff-state/local-AGENTS.md",
"bytes": 5081,
"sha256": "76491d12a4fcebfebbf6fc8608a14a2ed5da96dd55090060153d950bb984f185"
},
{
"path": "handoff-state/observations/2026-10-14-drawn-4-leading-5.png",
"bytes": 132183,
"sha256": "39eeac7c25f799977f43a2f29f16d762cb2a060d6610e1fe0b2cb41b1341866c"
},
{
"path": "handoff-state/observations/2026-10-14-encoding-before.png",
"bytes": 226743,
"sha256": "01a53c6c268c753f9f2b156b303be6899dd75fed1d4d7847be0dd30900d04728"
},
{
"path": "monitor_handoff.py",
"bytes": 2259,
"sha256": "c566b24daaec7a565a08bade3856b81bcba34576b1220b7dbc3b54f05c89305d"
},
{
"path": "perf_connection_cache.h",
"bytes": 2578,
"sha256": "897230bc78bdbba736eb51f5975d9b9bff0dd1bb55668c3dc84c598dc799ed22"
},
{
"path": "prepare_baseline_v2.py",
"bytes": 1063,
"sha256": "2b708e997239721b7898898da8b8abdbc76c94762a99881e6d9af7277f14dcb5"
},
{
"path": "prepare_contrast.py",
"bytes": 1726,
"sha256": "f79b752df5239607bfe773e979e8746fe7d1808ed0dc96be9f1f85a860ba1598"
},
{
"path": "prepare_data.py",
"bytes": 3263,
"sha256": "95a9f3bc2942860ca473ab66e02b16a1c8e14b0b0427d7b8faea7052276d5361"
},
{
"path": "prepare_features.py",
"bytes": 1574,
"sha256": "5317232956ee81908bcd814c32d88583e93148d5247642e3f1d153f7b0035f43"
},
{
"path": "prepare_jpeg.py",
"bytes": 3392,
"sha256": "5ab30b5eaaa5827ddbe32e949ec66994970902a7b4ac5b1f7567753e642edd57"
},
{
"path": "prepare_preview.py",
"bytes": 4923,
"sha256": "1a59fffafa4324665df4e73edeac1f41975b141c11e53e7a2563c4a4d197a473"
},
{
"path": "prepare_v2.py",
"bytes": 2850,
"sha256": "fea3d85cfd4d375d1dafba80a540c89cb7adc8061d462b0104e92da46829138b"
},
{
"path": "preview-cache/rows-train-0.json",
"bytes": 64675,
"sha256": "3c31f5a890ff80ca11718be9fdf3484ca9913e4613aaa8cbcc625b72a453221c"
},
{
"path": "preview-cache/rows-train-100.json",
"bytes": 64895,
"sha256": "e2b5c70a4d2787bd078205a7126ab69dfa41fdda8bfaadf73f61417f4273de2e"
},
{
"path": "preview-cache/rows-train-1000.json",
"bytes": 65095,
"sha256": "f0299d0f59f5f563cbfe59aefb1c321d819ebdfe7b3d57ec890156d75c3a85f8"
},
{
"path": "preview-cache/rows-train-1100.json",
"bytes": 65095,
"sha256": "ec4338ad396ff0734c726d8c932168995c0097f5b450cd8e6f40f6d6c37a610b"
},
{
"path": "preview-cache/rows-train-1200.json",
"bytes": 65095,
"sha256": "7119ecd8b9d8178307e729172913637653c109a4556f72d6faf08defcfd4a43e"
},
{
"path": "preview-cache/rows-train-1300.json",
"bytes": 65095,
"sha256": "f6c4e73772fa5c19fd2a7bad9c218b994174844cc321194c83a56f3afff1929c"
},
{
"path": "preview-cache/rows-train-1400.json",
"bytes": 65095,
"sha256": "68d650f9bea8af1a639e3f62ba16556de36dd469ac4edf0c3ae1cee7a790759b"
},
{
"path": "preview-cache/rows-train-1500.json",
"bytes": 65095,
"sha256": "6cf2a8aeec467597ded345b71f2e93ccc560f6a8cb9b48444eab8205f3e2f8e9"
},
{
"path": "preview-cache/rows-train-1600.json",
"bytes": 65095,
"sha256": "b80c26b8852db3ee7d172474cadb34fd5063357a7396fd620afd877f9b0fc4f5"
},
{
"path": "preview-cache/rows-train-1700.json",
"bytes": 65095,
"sha256": "bc72cd8a9e324dd9e03da72866de7fbb38c7946132c630938b5268ddbab1ef51"
},
{
"path": "preview-cache/rows-train-1800.json",
"bytes": 65095,
"sha256": "370449a3f7eef727ec24ccdc7f98b69927ca3511b351ce595a9120534f579f46"
},
{
"path": "preview-cache/rows-train-1900.json",
"bytes": 65095,
"sha256": "67441091a8e8be4e7b861e6b8fd28495f6499d2bd6ad08dcf6c962b64a1cad1a"
},
{
"path": "preview-cache/rows-train-200.json",
"bytes": 64895,
"sha256": "bcf79a109e6909bb41ea6a679cb63fc4591a0d2ca11e835ee836a718619d899f"
},
{
"path": "preview-cache/rows-train-2000.json",
"bytes": 65095,
"sha256": "007294c524645a8e6c12402fb49e5e3c883e1362b963cc47bdfe5deb76ba184c"
},
{
"path": "preview-cache/rows-train-2100.json",
"bytes": 65095,
"sha256": "67a7a3a1f20b3f14b73857b4dce80ce70e6e44aa1f3d5aabf9a4cb98b3e212fa"
},
{
"path": "preview-cache/rows-train-2200.json",
"bytes": 65095,
"sha256": "ec6b1b395864c6854226b121dcb75456ea197e3647dfcc685d768262de9378bb"
},
{
"path": "preview-cache/rows-train-2300.json",
"bytes": 65095,
"sha256": "f9f4122deab0461f8e96dd97e8f04d03f0acc6e38cca14ad6e3020efd850205d"
},
{
"path": "preview-cache/rows-train-2400.json",
"bytes": 65095,
"sha256": "4c973e4b5825f0b9db877d313bfb27821a268a326daa1fd86933b533965065e3"
},
{
"path": "preview-cache/rows-train-2500.json",
"bytes": 65095,
"sha256": "d3d5d6c416be1495e54155d01f8c3a0b79e07c5d8a1c0e67706683a56614df23"
},
{
"path": "preview-cache/rows-train-2600.json",
"bytes": 65095,
"sha256": "ff65f42c724de47ce00567cbe92b9e507d515ef2f5537018460266462ddf13f8"
},
{
"path": "preview-cache/rows-train-2700.json",
"bytes": 65095,
"sha256": "719eecb5ece98fe7d74de5cc7ea4a57c925bd912db5ab0cee8a822fe90d275b9"
},
{
"path": "preview-cache/rows-train-2800.json",
"bytes": 65095,
"sha256": "d7a869ccde35acd7492b33baedaa7152b6f0629506809ce076dc02c624678bd4"
},
{
"path": "preview-cache/rows-train-2900.json",
"bytes": 65095,
"sha256": "fc88b8fb1343ef0a5fd99921af2a9028f97a45944d8621da8ef41b2258e2971d"
},
{
"path": "preview-cache/rows-train-300.json",
"bytes": 64895,
"sha256": "990eb0fd9d113b40632a5a27f7890c09d386d4ee3b80ca31e5b8b5c3352a61f5"
},
{
"path": "preview-cache/rows-train-3000.json",
"bytes": 65095,
"sha256": "055655059a78ab4496d4475da3bdba50e816a511c1d95d1f00a68feeae408ace"
},
{
"path": "preview-cache/rows-train-3100.json",
"bytes": 65095,
"sha256": "9e67594984df77fdd24c29f92442631943a2c040cdc2376f04ffc2cf749870b3"
},
{
"path": "preview-cache/rows-train-3200.json",
"bytes": 65095,
"sha256": "e8959f0023827809b5570b825fadab9337211d1476455daa4ec316cb5347ee80"
},
{
"path": "preview-cache/rows-train-3300.json",
"bytes": 65095,
"sha256": "06187a2922e45e597ccdf4abb6c15b71c703aafc7783ec36af32e399cc680571"
},
{
"path": "preview-cache/rows-train-3400.json",
"bytes": 65095,
"sha256": "31c5d6bd3d2369adfacf41ae7ab9db19be35dba895177ae1b061be7b548f7b27"
},
{
"path": "preview-cache/rows-train-3500.json",
"bytes": 65095,
"sha256": "0a4b2f5788fde86a68fefb36410c067cfb2d0c1eb7ce521af1c60fd2511fefd4"
},
{
"path": "preview-cache/rows-train-3600.json",
"bytes": 65095,
"sha256": "c7ad2933d1ac0cba15986923a0e1e4ec3e2c0d60d7abb62f3b6b7df527801f1c"
},
{
"path": "preview-cache/rows-train-3700.json",
"bytes": 65095,
"sha256": "dc31a125a5fb3387749daee5cc0b73a700b370a21c3c4390cb9247f4deaadcd8"
},
{
"path": "preview-cache/rows-train-3800.json",
"bytes": 65095,
"sha256": "94b5c173034ec00530c6fa42ac9e898e8281410dfb4ddcd76252850b7a41d639"
},
{
"path": "preview-cache/rows-train-3900.json",
"bytes": 65095,
"sha256": "ffadf864866e1e6cb616e36a3ba49c23d172a166de76e57384ce2e0836d728a6"
},
{
"path": "preview-cache/rows-train-400.json",
"bytes": 64895,
"sha256": "5dd424610f4f449849adddaa64cfb1b59b0f7f0f3713b8fcc736aa7d799e3b4c"
},
{
"path": "preview-cache/rows-train-4000.json",
"bytes": 65095,
"sha256": "2c9b0cc2f88e37774fdb1d0bbf039245ed8444042441f75d8063a8d9429d53b7"
},
{
"path": "preview-cache/rows-train-4300.json",
"bytes": 65095,
"sha256": "b0ee3457cb4e75dd670101c797b524ea6b601156c7bd382285bb0a45d46927c2"
},
{
"path": "preview-cache/rows-train-4400.json",
"bytes": 65095,
"sha256": "b7d3acca6b257ca1f066479773d2da4c32a7ae26e24240ac15c7515280c616d3"
},
{
"path": "preview-cache/rows-train-500.json",
"bytes": 64895,
"sha256": "15401fad97a19ab77f3dc4049174ca27f884074fd264cf5e204c936bfbae3a58"
},
{
"path": "preview-cache/rows-train-600.json",
"bytes": 64895,
"sha256": "242e8685673beeae59a9f33f33b07c6478a6e6e67836d9965e7400d17c55c6c2"
},
{
"path": "preview-cache/rows-train-700.json",
"bytes": 64895,
"sha256": "7c623c44c125bc54fbf59c30b745898a074a0e8d058e5cc9cad2ae30e01c4ef8"
},
{
"path": "preview-cache/rows-train-800.json",
"bytes": 64895,
"sha256": "5f05e8b66c538f6129c0a86304b8ff127782171b75f07f4a346ff019f635ea15"
},
{
"path": "preview-cache/rows-train-900.json",
"bytes": 64895,
"sha256": "bc9e6e8fd6c3c44ba7ce0e4fecf471e73aa78d97d2cbaca21a5d3e601c159b32"
},
{
"path": "probe_v2.py",
"bytes": 655,
"sha256": "fd6af75cfa6ebc31e17ffcd7e3625b89921fa644f1f9cd8d9f7be06608b1a665"
},
{
"path": "results/reproducibility.json",
"bytes": 523,
"sha256": "bda80dcba25f3742f773fdf51708b515764d921306f205fb7990eb475b3d7e0c"
},
{
"path": "results/selection.json",
"bytes": 5020,
"sha256": "a8886f55790ce838517ef067f2292227d0cc63052d99e498d562330542a67eb2"
},
{
"path": "results/test-metrics.json",
"bytes": 286,
"sha256": "50ccf2a5c60055d8dcd87392dacd70b806ec322caa988a27d247654ffa5b0369"
},
{
"path": "results/test-settings.json",
"bytes": 840,
"sha256": "8a54e418d309f1073db68b3fd99cef8cd5ac30495990f4313be85d3d9758baaa"
},
{
"path": "results-v2/comparison.json",
"bytes": 2589,
"sha256": "e7a4aba7335e9fc8bdbd5c121570191d9a0887e9cf3f0dcb778529070a078f37"
},
{
"path": "results-v2/data-manifest.json",
"bytes": 368279,
"sha256": "17febe461517f20535acddc3f8eab1b2f93b5df05c8d7cccee5cb5d69e27b315"
},
{
"path": "results-v2/features-manifest.json",
"bytes": 438,
"sha256": "aade7e46013aa29abedd88ffb9b09b7a64db0aa84df2f95bff1bfd39633ec18f"
},
{
"path": "results-v2/probes.json",
"bytes": 5023,
"sha256": "0bb303fb4d440c96eff1067b43d282988ea3b577fc48ad85668a203558cc30da"
},
{
"path": "results-v2/selection.json",
"bytes": 4618,
"sha256": "815d83fea856132b40364f2d595bd55f007596893196b301d40a080311248d45"
},
{
"path": "results-v2/test-settings.json",
"bytes": 1040,
"sha256": "a0d059e6b9bdefbfe3dad3e825910eb3e8d2be2c4151f6e1c38cf78e787344c7"
},
{
"path": "run.py",
"bytes": 3912,
"sha256": "5d6ae31f041b632385f91c53eda54210abce1ff938717810da8d8f4784b024bb"
},
{
"path": "run_batch.py",
"bytes": 2298,
"sha256": "f803facc090b7a03d5c404269e64a62a0ec36d28d92a4dd23543368a99175a9b"
},
{
"path": "save_handoff.py",
"bytes": 6865,
"sha256": "3cff1da48b4fb1d91e40264c436356d154e706853cafc645bc38992b497d044d"
},
{
"path": "source-snapshot.tar.gz",
"bytes": 35685,
"sha256": "28289bb99b31cf1da7351efc04982f9812e2f0435e92636176270d7034514075"
},
{
"path": "test_features.py",
"bytes": 808,
"sha256": "6d95c6a93042c29acbc4f927c8df69e9b2ea002b59cf3e808f094c8dbdc4779a"
},
{
"path": "trained-weights-v2.tar.gz",
"bytes": 813540,
"sha256": "d6dc1f52bba677f4ebed76cf921538ea43504ee374ada6ba1291d1a37ce949f2"
},
{
"path": "trained-weights.tar.gz",
"bytes": 731329,
"sha256": "653b34df78ccba30f9c09d296cfd145676d33d7ab0cf98447f0bec853560ca1a"
},
{
"path": "tune.py",
"bytes": 2449,
"sha256": "7904da935466f1cb5a467c7a05570a3740ed2641393eb0cfa1d63e4734030a97"
},
{
"path": "validate.py",
"bytes": 1353,
"sha256": "590042f0161a73c11042b1b5cf8018e2deb8e6e1361752b55a762231faf36375"
},
{
"path": "validate_v2.py",
"bytes": 1926,
"sha256": "82f49b30a2b6813d1f87d7f8417996fac57b32d7500206e47e843f82fb39f02a"
}
]
}

View file

@ -0,0 +1,6 @@
{
"updated_utc": "2026-10-14T15:13:48.947778+00:00",
"remote_verified": false,
"detail": "Automatic monitoring/transfer cancelled by user; local ZIP export requested. Remote transfer was not completed.",
"destination": "int@10.9.8.230:/mnt/c/KNP/cats-dogs"
}

View file

@ -0,0 +1,18 @@
{
"float_weights.csv": {
"equal": true,
"sha256": "978e12ce64abde2c2745e0416322f16494267d07dd5fefb8492d6fb584ad11c9"
},
"quant_weights.csv": {
"equal": true,
"sha256": "42d825f23aee79d355f37acad10d524ddcaccd761285a4bcdace166f1700a1a0"
},
"float_neurons.csv": {
"equal": true,
"sha256": "528eacb12662ca6455c90c3cc3b94d9a957049aa4d6910b2dde5ffa0001aa16e"
},
"quant_neurons.csv": {
"equal": true,
"sha256": "3b003b4f2a843274065c0d303e49a80c69398faa15077a8352f2bba79e5bb21e"
}
}

View file

@ -0,0 +1,247 @@
{
"method": "10 fixed configurations evaluated with real KNP on validation only; first wins ties",
"rows": [
{
"encoding": "brightness",
"threshold": 10,
"run": "val_brightness_10",
"elapsed_seconds": 7.19,
"classes": {
"cat": {
"correct": 13,
"unanswered": 0,
"wrong": 87
},
"dog": {
"correct": 75,
"unanswered": 0,
"wrong": 25
}
},
"correct": 88,
"unanswered": 0,
"wrong": 112,
"accuracy": 0.44
},
{
"encoding": "brightness",
"threshold": 20,
"run": "val_brightness_20",
"elapsed_seconds": 6.65,
"classes": {
"cat": {
"correct": 10,
"unanswered": 1,
"wrong": 89
},
"dog": {
"correct": 84,
"unanswered": 2,
"wrong": 14
}
},
"correct": 94,
"unanswered": 3,
"wrong": 103,
"accuracy": 0.47
},
{
"encoding": "brightness",
"threshold": 40,
"run": "val_brightness_40",
"elapsed_seconds": 6.59,
"classes": {
"cat": {
"correct": 9,
"unanswered": 9,
"wrong": 82
},
"dog": {
"correct": 82,
"unanswered": 6,
"wrong": 12
}
},
"correct": 91,
"unanswered": 15,
"wrong": 94,
"accuracy": 0.455
},
{
"encoding": "brightness",
"threshold": 60,
"run": "validation",
"elapsed_seconds": 6.41,
"classes": {
"cat": {
"correct": 7,
"unanswered": 17,
"wrong": 76
},
"dog": {
"correct": 67,
"unanswered": 25,
"wrong": 8
}
},
"correct": 74,
"unanswered": 42,
"wrong": 84,
"accuracy": 0.37
},
{
"encoding": "contrast",
"threshold": 10,
"run": "val_contrast_10",
"elapsed_seconds": 3.82,
"classes": {
"cat": {
"correct": 56,
"unanswered": 0,
"wrong": 44
},
"dog": {
"correct": 47,
"unanswered": 0,
"wrong": 53
}
},
"correct": 103,
"unanswered": 0,
"wrong": 97,
"accuracy": 0.515
},
{
"encoding": "contrast",
"threshold": 20,
"run": "val_contrast_20",
"elapsed_seconds": 3.56,
"classes": {
"cat": {
"correct": 46,
"unanswered": 0,
"wrong": 54
},
"dog": {
"correct": 64,
"unanswered": 0,
"wrong": 36
}
},
"correct": 110,
"unanswered": 0,
"wrong": 90,
"accuracy": 0.55
},
{
"encoding": "contrast",
"threshold": 40,
"run": "val_contrast_40",
"elapsed_seconds": 3.42,
"classes": {
"cat": {
"correct": 44,
"unanswered": 0,
"wrong": 56
},
"dog": {
"correct": 63,
"unanswered": 0,
"wrong": 37
}
},
"correct": 107,
"unanswered": 0,
"wrong": 93,
"accuracy": 0.535
},
{
"encoding": "contrast",
"threshold": 60,
"run": "val_contrast_60",
"elapsed_seconds": 3.5,
"classes": {
"cat": {
"correct": 44,
"unanswered": 0,
"wrong": 56
},
"dog": {
"correct": 67,
"unanswered": 1,
"wrong": 32
}
},
"correct": 111,
"unanswered": 1,
"wrong": 88,
"accuracy": 0.555
},
{
"encoding": "contrast",
"threshold": 100,
"run": "val_contrast_100",
"elapsed_seconds": 4.14,
"classes": {
"cat": {
"correct": 39,
"unanswered": 6,
"wrong": 55
},
"dog": {
"correct": 61,
"unanswered": 4,
"wrong": 35
}
},
"correct": 100,
"unanswered": 10,
"wrong": 90,
"accuracy": 0.5
},
{
"encoding": "contrast",
"threshold": 150,
"run": "val_contrast_150",
"elapsed_seconds": 3.38,
"classes": {
"cat": {
"correct": 29,
"unanswered": 24,
"wrong": 47
},
"dog": {
"correct": 59,
"unanswered": 12,
"wrong": 29
}
},
"correct": 88,
"unanswered": 36,
"wrong": 76,
"accuracy": 0.44
}
],
"selected": {
"encoding": "contrast",
"threshold": 60,
"run": "val_contrast_60",
"elapsed_seconds": 3.5,
"classes": {
"cat": {
"correct": 44,
"unanswered": 0,
"wrong": 56
},
"dog": {
"correct": 67,
"unanswered": 1,
"wrong": 32
}
},
"correct": 111,
"unanswered": 1,
"wrong": 88,
"accuracy": 0.555
}
}

View file

@ -0,0 +1,19 @@
{
"elapsed_seconds": 4.7,
"classes": {
"cat": {
"correct": 98,
"unanswered": 5,
"wrong": 97
},
"dog": {
"correct": 110,
"unanswered": 4,
"wrong": 86
}
},
"correct": 208,
"unanswered": 9,
"wrong": 183,
"accuracy": 0.52
}

View file

@ -0,0 +1,36 @@
{
"arguments": {
"name": "test",
"dataset": "contrast_train_test",
"train": 800,
"count": 400,
"threshold": 60
},
"command": [
"/home/int/run/cats-dogs/build/cats_dogs",
"-m",
"altai",
"--images",
"/home/int/run/cats-dogs/data/contrast_train_test.bin",
"--labels",
"/home/int/run/cats-dogs/data/contrast_train_test.target",
"-t",
"800",
"-i",
"400",
"-l",
"error",
"--training_backend",
"/home/int/knp-build/bin/libknp-cpu-single-threaded-backend.so"
],
"environment": {
"KNP_COL_JITTER": "20",
"KNP_COL_SEED": "42",
"KNP_COL_SIZE": "20",
"KNP_MNIST_THR": "60",
"KNP_MNIST_ADD_THR_SCALE": "0",
"KNP_MNIST_INFER_INHIB": "255",
"KNP_MNIST_DUMP": "/home/int/run/cats-dogs/test"
},
"started_utc": "2026-10-14T13:34:31Z"
}

133
cats-dogs/save_handoff.py Normal file
View file

@ -0,0 +1,133 @@
"""Explicitly resume the user-requested backup and verified workstation transfer.
This script connects to the permanent test stand. Run only at the user's request.
Does not launch training, alter drivers, remove runs, or overwrite original KNP code.
"""
from datetime import datetime, timezone
import hashlib
import json
from pathlib import Path
import subprocess
import sys
import tarfile
ROOT = Path(__file__).resolve().parent
STATE = ROOT / 'handoff-state'
HOST = 'int@10.9.8.230'
KEY = str(Path.home() / '.ssh/id_ed25519_snw')
SSH = ['ssh', '-i', KEY, '-o', 'IdentitiesOnly=yes', '-o', 'BatchMode=yes',
'-o', 'ConnectTimeout=10', '-o', 'ServerAliveInterval=15',
'-o', 'ServerAliveCountMax=2', HOST]
def manifest():
excluded = {'SHA256SUMS', 'preservation-manifest.json', 'preservation-status.json'}
rows = []
for path in sorted(ROOT.rglob('*')):
if not path.is_file() or '__pycache__' in path.parts or path.name.endswith('.part'):
continue
relative = path.relative_to(ROOT).as_posix()
if relative in excluded:
continue
digest = hashlib.sha256()
with path.open('rb') as stream:
for block in iter(lambda: stream.read(1024 * 1024), b''):
digest.update(block)
rows.append({'path': relative, 'bytes': path.stat().st_size, 'sha256': digest.hexdigest()})
(ROOT / 'preservation-manifest.json').write_text(json.dumps({'files': rows}, indent=2) + '\n')
(ROOT / 'SHA256SUMS').write_text(''.join(f"{r['sha256']} {r['path']}\n" for r in rows))
return len(rows)
def status(success, detail):
(ROOT / 'preservation-status.json').write_text(json.dumps({
'updated_utc': datetime.now(timezone.utc).isoformat(),
'remote_verified': success, 'detail': detail,
'destination': HOST + ':/mnt/c/KNP/cats-dogs',
}, ensure_ascii=False, indent=2) + '\n')
def capture(name, command):
result = subprocess.run(SSH + [command], capture_output=True, check=True)
(STATE / name).write_bytes(result.stdout)
def archive(name, command):
path = STATE / name
temporary = path.with_suffix(path.suffix + '.part')
try:
with temporary.open('wb') as output:
subprocess.run(SSH + [command], stdout=output, check=True)
with tarfile.open(temporary, 'r:gz') as saved:
for _ in saved:
pass
temporary.replace(path)
finally:
temporary.unlink(missing_ok=True)
def main():
STATE.mkdir(exist_ok=True)
if '--local-manifest-only' in sys.argv:
print('Local files hashed:', manifest())
return
status(False, 'Backup/transfer in progress; no success claimed yet')
try:
subprocess.run(SSH + ['true'], check=True)
commands = {
'remote-inventory.txt': 'date -u; uname -a; ls -la /mnt/c/KNP; ls -la /mnt/c/KNP/cats-dogs; find /home/int/run/cats-dogs -maxdepth 2 -type f; ps -eo pid,etime,args | grep -E "cats_dogs|http.server|nvcc|build_fast|build_tuned" | grep -v grep || true',
'original-knp-status.txt': 'git -C /mnt/c/KNP/examples rev-parse HEAD; git -C /mnt/c/KNP/examples status --short',
'original-knp-changes.patch': 'git -C /mnt/c/KNP/examples diff --binary',
'visualizer-status.txt': 'git -C /mnt/c/KNP/viz rev-parse HEAD; git -C /mnt/c/KNP/viz status --short',
'visualizer-changes.patch': 'git -C /mnt/c/KNP/viz diff --binary',
'compiler-flags.txt': 'cat /home/int/knp-build/knp/backends/cpu/cpu-single-threaded-backend/CMakeFiles/knp-cpu-single-threaded-backend.dir/flags.make /home/int/knp-build/knp/backends/cpu/cpu-single-threaded-backend/CMakeFiles/knp-cpu-single-threaded-backend.dir/link.txt; /usr/bin/c++ --version; /home/int/cuda-12.9/bin/nvcc --version; /usr/lib/wsl/lib/nvidia-smi --query-gpu=name,driver_version,memory.total --format=csv,noheader',
}
for name, command in commands.items():
capture(name, command)
print('Captured state', flush=True)
# First deliver the existing local context, even if a later full backup fails.
manifest()
transport = 'ssh -i ' + KEY + ' -o IdentitiesOnly=yes -o BatchMode=yes -o ConnectTimeout=10'
def upload():
available = subprocess.run(SSH + ['command -v rsync'], capture_output=True)
if available.returncode == 0:
subprocess.run(['rsync', '-a', '--exclude=__pycache__', '--exclude=*.part',
'-e', transport, str(ROOT) + '/', HOST + ':/mnt/c/KNP/cats-dogs/'], check=True)
else:
pack = subprocess.Popen(['tar', '--exclude=__pycache__', '--exclude=*.part',
'-cf', '-', '-C', str(ROOT), '.'], stdout=subprocess.PIPE)
try:
subprocess.run(SSH + ['mkdir -p /mnt/c/KNP/cats-dogs && tar -xf - -C /mnt/c/KNP/cats-dogs'],
stdin=pack.stdout, check=True)
finally:
pack.stdout.close()
result = pack.wait()
if result:
raise RuntimeError(f'Upload tar exited {result}')
subprocess.run(SSH + ['cp /mnt/c/KNP/cats-dogs/HANDOFF.md /mnt/c/KNP/HANDOFF-2026-10-14.md && '
'cd /mnt/c/KNP/cats-dogs && sha256sum -c SHA256SUMS >/tmp/knp-handoff-check.log'], check=True)
upload()
print('Existing local context delivered and verified', flush=True)
archive('remote-runtime.tar.gz', 'tar -czf - -C /home/int/run cats-dogs')
archive('prior-context-and-source.tar.gz',
'tar -czf - -C / mnt/c/KNP/cats-dogs/source mnt/c/KNP/viz/altai_viz.html '
'mnt/c/KNP/viz/app.html mnt/c/KNP/Отчёт_MNIST_точность.md '
'mnt/c/KNP/Отчёт_altai.md mnt/c/KNP/Отчёт_колонки_altai.md '
'mnt/c/KNP/Отчёт_размер_колонки_и_одежда.md '
'home/int/.claude/projects/-mnt-c-knp/memory '
'home/int/.claude/projects/-mnt-c-KNP/memory')
print('Archives validated; local files hashed:', manifest(), flush=True)
upload()
status(True, 'Runtime/context archives validated, files transferred, remote SHA256SUMS passed')
subprocess.run(['scp', '-i', KEY, '-o', 'IdentitiesOnly=yes', '-o', 'BatchMode=yes',
'-o', 'ConnectTimeout=10', str(ROOT / 'preservation-status.json'),
HOST + ':/mnt/c/KNP/cats-dogs/preservation-status.json'], check=True)
print('Verified workstation copy: /mnt/c/KNP/HANDOFF-2026-10-14.md')
except Exception as error:
status(False, str(error))
manifest()
raise
if __name__ == '__main__':
main()

Binary file not shown.

Binary file not shown.

203
changes-2026-10-14.md Normal file
View file

@ -0,0 +1,203 @@
# Журнал работы — 14 октября 2026
## Передача следующему чату и сохранение состояния
По запросу пользователя создана подробная передача `/opt/neiro/experiments/cats-dogs/HANDOFF.md` и точка входа `/opt/neiro/HANDOFF.md`; ссылка добавлена в AGENTS.md. Передача содержит ограничения пользователя, SSH без сохранения пароля, карту путей, состояние процессов, прежние правки KNP, историю визуализатора и кодировки, наблюдение о четвёрке, данные/разделения/метрики v1/v2, настройки и команды, состояние CPU-оптимизаций, устройство незавершённого CUDA-прототипа и конкретный план его подключения/проверки.
Перед сохранением выполнен успешный SSH-осмотр: HTTP-сервер визуализатора PID3671 работал, HTTP200; обучений/сборок не обнаружено. Сверены каталоги данных/прогонов, git HEAD и dirty status KNP/viz, наличие обеих папок памяти Claude, показатели backend_altai_full (80,77с,626/1000) и fast_smoke (4,39с,123/200). Полная побайтная проверка этих двух прогонов не выполнялась. Runtime cats-dogs занимает около357МБ на стенде. Результаты наблюдения описаны в передаче; новые полные архивы ещё не получены.
После этого стенд перестал отвечать на10.9.8.230:22. Несколько повторных SSH-подключений, включая IdentitiesOnly=yes и ConnectTimeout=10, закончились Connection timed out. Причина недоступности не установлена; нельзя утверждать, что WSL выключен или проблема именно в VPN. Первое резервирование прервалось до получения первого нового snapshot-файла. **Новая передача и CUDA-черновики пока не подтверждены на рабочей станции.** Ранее сохранённые на станции скрипты/исходники/результаты остаются по прежним путям.
Создан `/opt/neiro/experiments/cats-dogs/save_handoff.py` для явного продолжения после восстановления связи: SSH-проверка, сохранение инвентаря/patch/флагов, полный tar.gz runtime и отдельный архив source/старых отчётов/памяти, проверка архивов, перенос пакета через rsync без удаления файлов, копия передачи в `/mnt/c/KNP/HANDOFF-2026-10-14.md`, удалённая SHA256-сверка. Запуск этого скрипта в текущем состоянии также завершился тайм-аутом; успешный путь переноса пока не проверен. Автозапуск/фоновый retry не создавался.
Локально сохранены все имевшиеся скрипты/модели/results, новая передача, копия правил/журнала и скриншот наблюдения в handoff-state. Сгенерированы preservation-manifest.json и SHA256SUMS; preservation-status.json честно хранит remote_verified=false. Полный новый remote-runtime.tar.gz и prior-context-and-source.tar.gz ещё не созданы. Сборка, обучение, изменение драйверов и модели в рамках этого запроса не выполнялись.
## Ускорение CPU и анализ GPU / Linux
Добавлены `run_batch.py`, `benchmark_batches.py`, `benchmark_backends.py`, `build_tuned_cpu.py`, `build_fast_cpu.py` и `perf_connection_cache.h`. В `run.py` добавлен выбор CPU-бэкенда; в `validate_v2.py` — ограниченный параллельный запуск независимых экспериментов. Оригинальные библиотеки KNP не заменялись; экспериментальные сборки расположены отдельно в `/home/int/run/cats-dogs`.
Четыре одинаковых эксперимента 4000 train + 1000 validation: последовательная группа 91,573 с, группа из четырёх процессов 42,911 с. Веса, параметры нейронов и ответы совпали побайтно. Это ускорение группы экспериментов, а не одной временной последовательности сети.
Штатный многопоточный CPU-бэкенд не поддерживал AltAI. В отдельной копии добавлена поддержка соответствующих типов и настройка потоков. Короткий контроль 800 + 200: однопоточный 4,89 с, два потока 15,09 с, четыре 15,69 с; результаты совпали побайтно. Поэтому многопоточность одной маленькой сети не выбрана по умолчанию. Кэширование подключений построено в отдельной однопоточной библиотеке; короткий прогон завершился за 4,39 с. Полная проверка идентичности этой оптимизации ещё не завершена.
Базовый CUDA-тест на RTX 4070 Ti SUPER работает. Штатный GPU-бэкенд KNP не поддерживает AltAI/STDP; простой BLIFAT-пример дополнительно показал `invalid device function`, причина не установлена. По консультации GPT-6 Astra выбран узкий экспериментальный перенос STDP с сохранением порядка вычислений и проверкой состояний по шагам. Написаны `gpu_stdp_types.h`, `gpu_stdp.cu`, `gpu_stdp_bridge.h`, `gpu_stdp_bridge.cpp`. **Прототип пока не собран и не проверен; готового GPU-обучения и доказанного ускорения GPU нет.**
На вопрос о чистом Linux проверены официальные документы NVIDIA и Microsoft: Linux даёт независимый выбор драйвера/ядра и меньше ограничений некоторых режимов CUDA-памяти. В WSL используется драйвер Windows; установка Linux-драйвера NVIDIA внутрь WSL не подходит. Переход ОС не добавит отсутствующую реализацию AltAI/STDP. Сравнительный замер ОС не проводился, драйверы и ОС не менялись. Подробности и ссылки — `experiments/cats-dogs/ACCELERATION.md`.
## Осмотр постоянного тестового стенда
По запросу пользователя выполнено подключение к `int@10.9.8.230` (WSL), осмотрен `/mnt/c/KNP`. Прочитаны отчёты за 12–13 октября и память Claude Code в `/home/int/.claude/projects/-mnt-c-knp/memory` и `/home/int/.claude/projects/-mnt-c-KNP/memory`.
Проверено на момент осмотра:
- Обучение, сборка и веб-сервер визуализатора в WSL не запущены.
- `/mnt/c/KNP/examples/VERSION` содержит `2.0.0`.
- В репозитории `examples` уже были незакоммиченные правки в шести файлах: `construct_network.cpp`, `hyperparameters.h` и `prepare_network_for_inference.cpp` модели altai; `prepare_network_for_inference.cpp` модели blifat; `knp/backends/gpu/cuda-backend/CMakeLists.txt`; `knp/base-framework/impl/model_loader.cpp`. Это работа предыдущего человека, сегодня эти файлы не меняли.
- Репозиторий `/mnt/c/KNP/viz` при первом осмотре был чистым.
- У прогонов `sz80_30k_s1`, `sz80_30k_s42`, `sz80_60k_s42`, `sz40_60k_s42` в `/home/int/run/exp` пустые `result.csv`; завершённого результата нет.
Из прежних отчётов (сегодня не воспроизводилось): улучшение MNIST через специализацию колонок и торможение при распознавании; исправление ошибки каналов `ModelLoader::init_channels`; проблемы GPU `invalid device function`. Следующая обсуждавшаяся ранее идея — ON/OFF-кодирование. Новых обучений, сборок и замеров точности сегодня не запускали.
## SSH и постоянные сведения о стенде
В локальный `/opt/neiro/AGENTS.md` добавлены постоянный адрес, пути и правило: подключаться к стенду только по запросу пользователя. Стенд предназначен для тестирования KNP и эмуляции нейроморфного процессора AltAI.
Первая проверка `ssh -o BatchMode=yes ...` без явного ключа завершилась `Permission denied`. По просьбе пользователя выполнено:
```bash
ssh-copy-id -i ~/.ssh/id_ed25519_snw.pub int@10.9.8.230
```
Результат: ключ уже присутствовал на сервере, добавлять его повторно не потребовалось. Проверка с `-i ~/.ssh/id_ed25519_snw -o IdentitiesOnly=yes -o BatchMode=yes` прошла без пароля; сервер вернул `1220WS2`, наличие `/mnt/c/KNP` подтверждено. SSH-конфигурацию не меняли; для последующих подключений указывали ключ явно.
## Запуск визуализатора
По запросу пользователя на стенде запущен статический HTTP-сервер:
```bash
nohup python3 -m http.server 8080 --bind 0.0.0.0 \
--directory /mnt/c/KNP/viz \
> /tmp/knp-viz-http.log 2>&1 < /dev/null &
```
- Страница: `http://10.9.8.230:8080/altai_viz.html`.
- Корень раздачи: `/mnt/c/KNP/viz`.
- Лог: `/tmp/knp-viz-http.log`.
- Проверено HTTP-запросом: статус `200`.
- Это фоновый процесс, автозапуск не настраивался. Перезапуск WSL может его остановить.
## Исправление кодировки визуализатора
Пользователь прислал скриншот с неверным отображением кириллицы. Проверка показала: русский текст в файлах корректно записан в UTF-8, но HTML начинался с `<title>` без объявления кодировки; сервер отдавал `Content-type: text/html` без `charset`.
В начало обоих файлов на стенде добавлена строка:
```html
<meta charset="utf-8">
```
Изменены:
- `/mnt/c/KNP/viz/altai_viz.html` — готовая страница.
- `/mnt/c/KNP/viz/app.html` — шаблон, чтобы объявление кодировки сохранилось при следующем формировании страницы.
Проверка: страница повторно скачана по HTTP; первые строки содержат объявление UTF-8 и корректный русский заголовок. Пользователю предложено обновить страницу через Ctrl+F5. Визуальную проверку после обновления браузера не выполняли; подтверждения пользователя пока нет. Сервер не перезапускали, пересборку страницы не выполняли, коммиты не создавали.
## Ведение журнала
По запросу пользователя создан этот файл и добавлено правило ежедневного журналирования в `/opt/neiro/AGENTS.md`: кратко описывать обсуждения и обычные проверки, подробно — изменения и запуски. Пароли в журнал не включены.
## Наблюдение: нарисованная четвёрка
Пользователь сообщил, что визуализатор не распознал нарисованную четвёрку. Сохранён предоставленный скриншот: [четвёрка, лидирует ответ 5](reports/observations/2026-10-14-drawn-4-leading-5.png).
На скриншоте выбран режим «Колонки σ=20», показан шаг 9 из 14; ответ 5 помечен «пока лидирует». Поэтому скриншот фиксирует промежуточный ответ, окончательный результат после всех шагов не проверен. Это отдельное наблюдение, без диагностики причин, изменений кода или новых запусков. Кириллица на этом скриншоте отображается корректно.
## Новая задача: различать кошек и собак
Пользователь предложил перейти от цифр к объектам, например кошкам и собакам. Проведена предварительная проверка исходников на стенде: в `examples/examples/mnist-learn/global_config.h` жёстко заданы 10 классов и 784 входа, данные переводятся в частотные спайки в `dataset.cpp`. Бинарник `/home/int/knp-build/bin/mnist_learn_example` существует. В `anvenv` есть numpy, нет PIL, torch и torchvision. Обучение и установка пакетов не запускались, исходники не менялись.
Проверен первичный источник данных: https://www.cs.toronto.edu/~kriz/cifar.html — CIFAR-10 содержит классы cat и dog с отдельной тестовой выборкой. У пользователя запрошен выбор между обучением самой SNN KNP/AltAI по изображениям и гибридным вариантом с готовым извлекателем признаков. Выбор и реализация пока не выполнены.
### Реализация отдельного спайкового эксперимента
Пользователь выбрал обучение именно SNN KNP/AltAI, без готового извлекателя признаков.
- Создан `/mnt/c/KNP/cats-dogs/source` — копия текущего `examples/examples/mnist-learn` вместе с прежними правками, с сохранением лицензии Apache 2.0. Исходный MNIST не менялся. В копии `global_config.h` число классов изменено с 10 на 2, число входов с 784 на 2048.
- Вход — серое изображение 32×32, дополненное `255 − яркость` (ON/OFF). Преобразование фиксированное, не обучаемое. По 20 нейронов на каждый класс.
- Созданы локальные скрипты в `/opt/neiro/experiments/cats-dogs`, рабочие копии на стенде в `/mnt/c/KNP/cats-dogs`: подготовка данных, отдельная сборка, запуск с сохранением логов/настроек/счётчиков и валидационный подбор порога.
- `build.py` использует include/link-флаги рабочей сборки KNP и собирает отдельный бинарник `/home/int/run/cats-dogs/build/cats_dogs` (два параллельных компилятора). Сборка успешно завершилась, лог — `/home/int/run/cats-dogs/build.log`.
- Первоначальная загрузка официального `cifar-10-binary.tar.gz` оказалась медленной. Последовательную загрузку и последующую попытку через 32 HTTP Range-запроса остановили. Частичный архив и части не считаются проверенным датасетом и не использовались для обучения.
- Для пилота выбран источник JPEG-превью `uoft-cs/cifar10` через Hugging Face datasets-server: 800 train, 200 validation, 400 test, поровну кошек/собак. Это преобразованные JPEG, а не стандартные байты CIFAR; результат не следует называть стандартным CIFAR-бенчмарком. Индексы, seed и хеши сохраняются в manifest.
- В `/home/int/anvenv` через `ensurepip` установлен pip 25.1.1, затем Pillow 12.3.0 для чтения JPEG. Первая попытка pip не сработала, поскольку его в этой среде не было. Лог — `/home/int/run/cats-dogs/pillow-install.log`.
- Первая массовая загрузка превью остановилась на HTTP 429. Добавлены кэш и повторные попытки с задержкой, параллельность запросов метаданных снижена до двух. Дополнительно создан локальный `fetch_cache.py` для загрузки с другой машины и последующей передачи кэша на стенд.
На момент этой записи подготовка данных продолжается; результаты обучения будут добавлены ниже после фактического завершения.
### Завершение первого опыта с кошками и собаками
Данные в итоге получены из JPEG-зеркала `https://github.com/YoongiKim/CIFAR-10-images`, закреплённого на коммите `d7d05618061fe37d830fb0419063d113c575ccbc`. Попытки Hugging Face также остановлены из-за HTTP 429. Окончательная подготовка выполнена `prepare_jpeg.py`; данные Hugging Face и частичный бинарный архив в обучении не использовались.
Подготовлены 800 train, 200 validation, 400 test, поровну классов. Метки: 0 — кошка, 1 — собака. Имена исходных файлов, параметры и SHA-256 записаны в `data/manifest.json`. Проверены отсутствие пересечения train/validation, размеры файлов и баланс меток. Локальные копии манифестов — в `experiments/cats-dogs`.
Выполненные запуски:
```bash
python3 /mnt/c/KNP/cats-dogs/run.py smoke --dataset smoke --train 100 --count 100
python3 /mnt/c/KNP/cats-dogs/run.py validation --train 800 --count 200
OPENBLAS_NUM_THREADS=1 ~/anvenv/bin/python /mnt/c/KNP/cats-dogs/tune.py ~/run/cats-dogs/validation
~/anvenv/bin/python /mnt/c/KNP/cats-dogs/prepare_contrast.py
python3 /mnt/c/KNP/cats-dogs/validate.py
python3 /mnt/c/KNP/cats-dogs/run.py test --dataset contrast_train_test --train 800 --count 400 --threshold 60
```
- Smoke: 49/100 верных, 1 молчание. Веса выгружены, ограничиваются параметрами STDP, прогон завершился без ошибки.
- Исходный яркостный вход, validation, порог 60: 74/200 верных, 42 молчания, 84 ошибки.
- Офлайн-симулятор на том же пороге дал 83/200, поэтому его подбор не принят. Для этого опыта модель выбиралась по **настоящим прогонам KNP**. Начальное состояние растра после кодирования train в симуляторе не учтено; отдельная диагностика всех причин расхождения не проводилась.
- Добавлен второй фиксированный вариант кодирования (`prepare_contrast.py`): серый BT.601, вычитание box blur 5×5 с edge padding, усиление ×4, разделение положительной/отрицательной частей в ON/OFF, clipping uint8. Готовая обученная модель признаков не применяется.
- `validate.py` проверил 10 конфигураций только на validation: яркость с порогами 10/20/40/60 и контраст с 10/20/40/60/100/150. Выбраны контраст и порог 60: 111/200 (55,5%), 1 молчание. Полная таблица сохранена в `/home/int/run/cats-dogs/selection.json` и локально в `experiments/cats-dogs/results/selection.json` **до запуска test**.
- Тест проверен один раз выбранной конфигурацией: **208/400 (52%)**, 183 ошибки, 9 молчаний. Кошки — 98 верных / 97 ошибок / 5 молчаний, собаки — 110 / 86 / 4. CPU-прогон train+test занял 4,7 с.
- Тестовый прогон заново обучил сеть на тех же train и seed=42. `float_weights.csv`, `quant_weights.csv`, `float_neurons.csv`, `quant_neurons.csv` побайтно совпали с выбранным валидационным прогоном. Результат проверки и SHA-256 — `/home/int/run/cats-dogs/reproducibility.json`, копия локально в `results`.
Вывод: воспроизводимое STDP-обучение настоящей модели KNP/AltAI работает, но полезное различение кошек и собак не достигнуто. 52% на сбалансированном тесте близки к 50% постоянного ответа; интервал Уилсона 95% примерно 47,1–56,9%. Нельзя утверждать доказанное преимущество над угадыванием. Это один seed и небольшой JPEG-пилот, не стандартный CIFAR-бенчмарк.
Артефакты: `/mnt/c/KNP/cats-dogs` — исходники и скрипты; `/home/int/run/cats-dogs` — данные, логи и все прогоны; `/opt/neiro/experiments/cats-dogs` — скрипты, README, манифесты, результаты, архив исходников и обученных весов. Исходный пример MNIST и его визуализатор не менялись. Автозапуск не настраивался; фоновых обучений или загрузок этого опыта не оставлено. Следующая идея — спайковый слой локальных признаков перед классификатором; он пока не реализован.
## Второй опыт: улучшение распознавания
По запросу пользователя начата работа над улучшением результата. Сохранено обучение классификатора через STDP в KNP; предобработчик контуров фиксированный, готовая обученная нейросеть не используется. Полностью спайковый извлекатель локальных признаков пока не реализован.
Изменения:
- `features.py`: HOG-подобный дескриптор (sqrt RGB, выбор канала с наибольшим градиентом, 9 направлений, ячейки 4×4, перекрывающиеся блоки 2×2, L2-Hys). Проверены плотные HOG, HOG с грубыми цветом/яркостью, и разреженный HOG с 256 сильнейшими признаками. Вход дополняется нулями до 2048.
- `test_features.py`: проверки ориентации границ, чёрного изображения, размеров, padding, разреженности и детерминированности — прошли.
- `run.py`: добавлены параметры `--data-dir`, `--columns`, `--seed`, `--jitter`, `--quant`. Старые значения по умолчанию сохранены. `--quant weights` включает существующий исследовательский режим `KNP_MNIST_QUANT=weights` в KNP-примере.
- `prepare_v2.py`: расширенный JPEG-набор с новым разделением. Train: 4000 (по 2000, train 0000–1999), новая validation: 1000 (train 4500–4999), новый test: 1000 (test 0200–0699). Новый test не пересекается с test первого опыта (0000–0199). Seed перемешивания 20261015, источник и commit прежние. Использован существующий JPEG-кэш, недостающие файлы скачаны с GitHub.
- `prepare_features.py`: подготовка трёх представлений с манифестами/хешами. `prepare_baseline_v2.py`: прежние 800 обучающих примеров первого опыта и новый test в прежнем контрастном кодировании для сравнения на одинаковых изображениях.
- `validate_v2.py`: 8 заранее заданных конфигураций на новой validation: 20/60 нейронов на класс × пороги 600/1600/4000/8000, разреженный HOG, квантование по весам, seed 42. `evaluate_v2.py`: однократный тест выбранной конфигурации и прежней сети, проверка идентичности весов и сохранение сравнения.
Предварительные прогоны только на прежней validation:
- `probe_v2.py`: 6 прогонов плотных HOG/комбинированных признаков с порогами 20/60/150. Основная проблема — молчание; у `probe_hog_20` 65626/81920 квантованных весов нулевые.
- 8 прогонов `probe_precise_*`: плотный/разреженный HOG × пороги 200/600/1600/4000 с квантованием по весам. Разреженный HOG перестал молчать и дошёл до 123/200 (61,5%) на старой validation. Это диагностическая валидация, не тестовая оценка.
- Отдельно вычислены скалярные произведения выгруженных весов с признаками и диагностические варианты центрирования/нормирования весов. Эти вычисления не обучали веса и не использовались как итоговый классификатор; изменения центрирования в C++ не вносились.
Смысл изменений: избежать плотного общего фона входных признаков и потери малых весов при квантовании. Веса остаются в ±255; пороги проверяются в CPU-эмуляторе, переносимость на конкретное поколение физического AltAI пока не установлена. Исходники MNIST и библиотек KNP в этом втором опыте не редактировались.
На момент записи завершена подготовка данных, идёт панель новой validation. Тестовая оценка и итоговые артефакты будут добавлены после завершения.
### Результат второго опыта
Все 8 валидационных конфигураций завершены. Выбрана `v2_val_c20_t4000`: 20 нейронов на класс, порог 4000, разреженный HOG, `KNP_MNIST_QUANT=weights`, jitter 20, seed 42. На новой validation — 626/1000 (62,6%). 60 нейронов на класс не улучшили результат; лучший проверенный порог с ними дал 60,2%. Выбор сохранён в `selection-v2.json` до проверки test.
`evaluate_v2.py` завершился успешно. **Одинаковый новый test, 1000 изображений:**
- Новая сеть (`v2_test`): 618 верных, 382 ошибки, 0 молчаний, **61,8%**. Кошки 304/500, собаки 314/500. Train+test 23,27 с на CPU.
- Прежняя сеть (`v1_fresh_test`): 522 верных, 450 ошибок, 28 молчаний, **52,2%**. Кошки 213/500, собаки 309/500. Train+test 5,55 с.
- Прирост **9,6 п.п.** Интервал Уилсона 95%: новая 58,7–64,8%, прежняя 49,1–55,3%. Это оценка на данном небольшом JPEG-наборе, не гарантия распознавания произвольных фотографий.
Для обеих моделей `float_weights.csv`, `quant_weights.csv`, `float_neurons.csv`, `quant_neurons.csv` побайтно совпали с соответствующими валидационными прогонами. Для прежней модели подтверждена идентичность весов первому опыту. Модель по test не перенастраивалась. Манифесты проверены: уникальность, баланс классов, отсутствие пересечений train/validation/test, новый test не пересекается со старым. Проверены синтаксис всех скриптов и тесты признаков. Среда: Python 3.14.4, numpy 2.5.4, Pillow 12.3.0.
Сохранены локально:
- `experiments/cats-dogs/V2.md` — описание, команды, результаты и ограничения.
- `results-v2/selection.json`, `comparison.json`, `test-settings.json`, `data-manifest.json`, `features-manifest.json`, `probes.json`.
- `trained-weights-v2.tar.gz` — обученные веса/пороги и настройки.
На стенде остались все логи, данные и прогоны в `/home/int/run/cats-dogs`; скрипты и копия отчёта — в `/mnt/c/KNP/cats-dogs`. Нового веб-приложения не делали, визуализатор MNIST не меняли. Фоновые загрузки/обучения этого опыта завершены, автозапуск не создавался.
Что можно сделать дальше (пока не сделано): зеркальные отражения и небольшие сдвиги train; спайковый слой обучаемых локальных признаков и объединение по соседним участкам; проверка нескольких seed; при потолке STDP — отдельный опыт с другим обучением SNN. Совместимость с физическим чипом не проверена. Прирост есть, но 61,8% всё ещё недостаточно для уверенного распознавания.
## Мониторинг восстановления SSH и автоматическая отправка
Пользователь явно разрешил мониторить доступ и при восстановлении отправить всю информацию. Создан `experiments/cats-dogs/monitor_handoff.py`: проверяет SSH каждые30с, при доступности запускает save_handoff.py, повторяет после сбоя и выходит после успешной SHA256-проверки переноса. Использует flock для защиты от второго монитора. Процесс запускается в фоне с отдельной сессией; лог/статус/PID находятся в `/opt/neiro/.handoff-monitor`. Перезапуск локальной машины остановит монитор: системный автозапуск не настроен.
Перенос включает всю папку эксперимента, копии журнала и AGENTS, наблюдения, сохранённые результаты/исходники/веса и CUDA-черновики. save_handoff.py сначала отправляет имеющийся локальный пакет, затем получает полный runtime-снимок и контекст со стенда и отправляет дополненный пакет. Это позволяет сохранить память раньше длительного резервирования. При отсутствии rsync на стенде используется tar по SSH. Из исходного KNP ничего не удаляется.
Непроведённый запрос iperf3: требовалось установить на стенд и выполнить `iperf3 -c 10.9.8.1 -P 8 -t 5`; доступ был недоступен, установка/тест не выполнялись. Этот монитор занимается сохранением информации, сетевые тесты сам не запускает.
## Отмена мониторинга и экспорт ZIP
По последнему запросу пользователя остановлены фоновый монитор SSH и автоматическая доставка (группа процесса784614); дальнейшие подключения автоматически не выполняются. Последний зафиксированный ответ SSH был Connection refused, успешного переноса не было. Вместо этого создан публичный ZIP `/public/knp-altai-handoff-2026-10-14.zip` со всеми доступными локальными материалами этого чата: передача, правила/журнал, скрипты и CUDA-прототип, результаты/манифесты/архивы исходников и весов, наблюдения/скриншоты, состояние отменённого монитора. Недоступные новые полные снимки удалённого runtime в ZIP не включены — их не удалось получить. Пароли и SSH-ключи в архив не включались.
## Вечер: перенос в git и сверка ускоренных прогонов
- Папка `/mnt/c/knp` стала общим git-репозиторием (ветка `main`, remote `https://git.snw.su/int/our_knp.git`). Вложенные `.git` из `1/`, `viz/`, `examples/` перенесены в `/mnt/c/knp-nested-git-backup/`, там же `examples-local-changes.patch`. Локальный DNS не резолвит `git.snw.su`, поэтому в репозитории задан `http.curloptResolve git.snw.su:443:45.141.100.133`. Пуш выполняет пользователь.
- Недостающие материалы передачи (HANDOFF, ACCELERATION, GPU-черновики, results v1, архивы) скопированы из ZIP в `cats-dogs/`. Работа выполнялась прямо на стенде: `~/run/cats-dogs` на месте, SSH не нужен.
- Сверка готовых файлов, без новых запусков, `cmp` всех CSV (float/quant neurons/weights, result.csv):
- `backend_altai_full` (tuned, 2 потока) против `v2_val_c20_t4000` (single): **все побайтно совпадают**, 626/1000. Время: tuned2 80,77 с против single 22,84 с — многопоточность в 3,5 раза медленнее.
- `fast_smoke` (кэш подключений) против `backend_altai_single` (800+200): **все побайтно совпадают**, 123/200. Время 4,39 с против 4,89 с (~10% быстрее). Полный замер fast на 4000+1000 ещё не делался.

View file

@ -42,7 +42,7 @@ Dataset process_dataset(ModelDescription const& model_desc)
// Process images and labels into spike representations.
dataset.process_labels_and_images(
images_stream, labels_stream, model_desc.train_images_amount_ + model_desc.inference_images_amount_,
classes_amount, input_size, steps_per_image,
classes_amount, get_input_size(), steps_per_image,
dataset.make_incrementing_image_to_spikes_converter(active_steps, state_increment_factor));
// Split dataset into training and inference portions.

View file

@ -22,6 +22,8 @@
#pragma once
#include <cstddef>
#include <cstdlib>
#include <string>
/**
@ -36,7 +38,22 @@ constexpr size_t classes_amount = 10;
*
* @details MNIST images are 28×28 pixels, resulting in 784 total pixels per image.
*/
constexpr size_t input_size = 28 * 28;
constexpr size_t mnist_input_size = 28 * 28;
/**
* @brief Actual input size: `KNP_MNIST_INPUT_SIZE` environment variable or 784.
*
* @details ON/OFF coding stores two channels per pixel, so the dataset has 2 × 784 = 1568 bytes per image.
*/
inline size_t get_input_size()
{
static const size_t size = []
{
const char *value = std::getenv("KNP_MNIST_INPUT_SIZE");
return value ? static_cast<size_t>(std::stoul(value)) : mnist_input_size;
}();
return size;
}
/**
* @brief Total number of simulation steps per input image.

View file

@ -122,7 +122,7 @@ static NetworkPopulations create_populations(NetworkConstructor &constructor)
constructor.add_population(default_neuron, classes_amount, PopulationRole::NORMAL, false, "GATE");
// Create raster population for channelized image processing (28x28 = 784 neurons).
const auto &raster_pop = constructor.add_channeled_population(input_size, true);
const auto &raster_pop = constructor.add_channeled_population(get_input_size(), true);
// Create target population for label processing (10 neurons for 10 digit classes).
const auto &target_pop = constructor.add_channeled_population(classes_amount, false);

View file

@ -91,7 +91,7 @@ static NetworkPopulations create_populations(NetworkConstructor &constructor)
constructor.add_population(default_neuron, classes_amount, PopulationRole::NORMAL, false, "GATE");
// Create raster population for channelized image processing (28x28 = 784 neurons).
const auto &raster_pop = constructor.add_channeled_population(input_size, true);
const auto &raster_pop = constructor.add_channeled_population(get_input_size(), true);
// Create target population for label processing (10 neurons for 10 digit classes).
const auto &target_pop = constructor.add_channeled_population(classes_amount, false);

65
onoff/prepare_onoff.py Normal file
View file

@ -0,0 +1,65 @@
"""Build ON/OFF-coded MNIST datasets for mnist_learn_example (KNP_MNIST_INPUT_SIZE=1568).
Each image becomes 1568 bytes: 784 ON pixels (unchanged) followed by 784 OFF pixels.
OFF modes:
full - 255 - p everywhere;
half - (255 - p) / 2 everywhere;
local - 255 - p only within `--radius` pixels of the digit (p > 0), zero elsewhere.
usage: python prepare_onoff.py <src.bin> <src.target> <out_prefix> --mode local [--radius 2]
Writes <out_prefix>.bin and <out_prefix>.target (labels copied as is).
"""
import argparse
import shutil
import numpy as np
SIDE = 28
def dilate(mask, radius):
out = mask.copy()
for dy in range(-radius, radius + 1):
for dx in range(-radius, radius + 1):
if dy * dy + dx * dx > radius * radius:
continue
shifted = np.zeros_like(mask)
ys, yd = (slice(dy, None), slice(None, -dy or None)) if dy >= 0 else (slice(None, dy), slice(-dy, None))
xs, xd = (slice(dx, None), slice(None, -dx or None)) if dx >= 0 else (slice(None, dx), slice(-dx, None))
shifted[:, ys, xs] = mask[:, yd, xd]
out |= shifted
return out
def off_channel(imgs, mode, radius):
inv = 255 - imgs.astype(np.int32)
if mode == "full":
off = inv
elif mode == "half":
off = inv // 2
elif mode == "local":
off = inv * dilate(imgs > 0, radius)
else:
raise ValueError(mode)
return off.astype(np.uint8)
def main():
ap = argparse.ArgumentParser()
ap.add_argument("images")
ap.add_argument("labels")
ap.add_argument("out")
ap.add_argument("--mode", choices=["full", "half", "local"], required=True)
ap.add_argument("--radius", type=int, default=2)
a = ap.parse_args()
imgs = np.fromfile(a.images, dtype=np.uint8).reshape(-1, SIDE, SIDE)
off = off_channel(imgs, a.mode, a.radius)
out = np.concatenate([imgs.reshape(len(imgs), -1), off.reshape(len(imgs), -1)], axis=1)
out.tofile(a.out + ".bin")
shutil.copyfile(a.labels, a.out + ".target")
on_spk, off_spk = imgs.mean() / 255 * 10 * SIDE * SIDE, off.mean() / 255 * 10 * SIDE * SIDE
print(f"{a.out}: {len(out)} images, ~{on_spk:.0f} ON / ~{off_spk:.0f} OFF spikes per image")
if __name__ == "__main__":
main()

View file

@ -4,6 +4,7 @@ Reproduces KNP inference: raster -> INPUT (delay 1), optional INPUT -> INPUT inh
INPUT -> OUTPUT column vote (delay 1), 15 steps per image, potentials carried across images.
usage: python3 altai_sim.py <dump_dir> <images.bin> <labels.target> [--inhib 255] [--thr 40,60,80] [--first N] [--test 10000]
[--pix 1568] (ON/OFF input, KNP_MNIST_INPUT_SIZE)
"""
import argparse
import csv
@ -91,7 +92,10 @@ def main():
ap.add_argument("--first", type=int, default=None, help="index of first test image (default: last --test)")
ap.add_argument("--test", type=int, default=10000)
ap.add_argument("--thr", default="", help="comma-separated thresholds to sweep instead of the dumped ones")
ap.add_argument("--pix", type=int, default=784, help="input size per image (1568 for ON/OFF)")
a = ap.parse_args()
global PIX
PIX = a.pix
w, thr = load_dump(a.dump)
total = len(open(a.labels).read().split())
first = total - a.test if a.first is None else a.first