diff --git a/cats-dogs/source/dataset.cpp b/cats-dogs/source/dataset.cpp index c5c3e0b..33af791 100644 --- a/cats-dogs/source/dataset.cpp +++ b/cats-dogs/source/dataset.cpp @@ -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. diff --git a/cats-dogs/source/global_config.h b/cats-dogs/source/global_config.h index 0a4118e..1eecf42 100644 --- a/cats-dogs/source/global_config.h +++ b/cats-dogs/source/global_config.h @@ -22,6 +22,8 @@ #pragma once #include +#include +#include /** @@ -36,7 +38,20 @@ constexpr size_t classes_amount = 2; * * @details MNIST images are 28×28 pixels, resulting in 784 total pixels per image. */ -constexpr size_t input_size = 32 * 32 * 2; +constexpr size_t default_input_size = 32 * 32 * 2; + +/** + * @brief Actual input size: `KNP_MNIST_INPUT_SIZE` environment variable or 2048. + */ +inline size_t get_input_size() +{ + static const size_t size = [] + { + const char *value = std::getenv("KNP_MNIST_INPUT_SIZE"); + return value ? static_cast(std::stoul(value)) : default_input_size; + }(); + return size; +} /** * @brief Total number of simulation steps per input image. diff --git a/cats-dogs/source/models/altai/construct_network.cpp b/cats-dogs/source/models/altai/construct_network.cpp index 2e2edec..4919671 100644 --- a/cats-dogs/source/models/altai/construct_network.cpp +++ b/cats-dogs/source/models/altai/construct_network.cpp @@ -135,7 +135,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); diff --git a/cats-dogs/source/models/blifat/construct_network.cpp b/cats-dogs/source/models/blifat/construct_network.cpp index 0a60949..7d7eb6e 100644 --- a/cats-dogs/source/models/blifat/construct_network.cpp +++ b/cats-dogs/source/models/blifat/construct_network.cpp @@ -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); diff --git a/changes-2026-10-15.md b/changes-2026-10-15.md index 4549089..e4bb34a 100644 --- a/changes-2026-10-15.md +++ b/changes-2026-10-15.md @@ -45,3 +45,9 @@ MNIST, признаки coarse, 30 000 обучение, 10 000 проверка | 3 | 4 | **69,1%** | Колонки STDP на этих же плотных признаках молчали (≈0%), на разреженных top-256 — 64–65%; ridge на этих uint8-признаках 74,2%, спайковый нейрон-разность 72,4%. Симулятор `dl_cd_r3_b4`: порог 30–250, торможение 0/255 — 67,6–68,8%. +- MNIST, штраф b: 8 — 94,22%; 12 — 92,42% (оптимум b = 4–6). Устойчивость `dl_r3_a1_b6_j20` (симулятор, 2000): как есть 94,5%, сдвиг 2 px 87,5%, толще 90,8%, тоньше 84,0%. +- MNIST с аугментацией A (`fcoarseA_val90000`, 90 000, r3 b6): KNP **94,63%**; устойчивость — 94,6 / **91,3** / 91,2 / 80,8%. +- Кошки/собаки, ещё b: r3 b6 63,5%, r3 b8 62,2%, r3 b12 60,0%, r1 b4 67,7%, r1 b8 68,4%. +- `cats-dogs/source`: размер входа — `get_input_size()` из `KNP_MNIST_INPUT_SIZE` (по умолчанию 2048), пересобран. `objects/make_knp_features.py --model --name`. Признаки онлайн WTA: K=800 (3200 входов) — r3 b4 65,3%, r1 b8 67,2%; K=1600 (6400) — 65,9% / 67,2%. +- Три прохода по обучению (обучающая часть ×3 с новым перемешиванием, `*_x3_validation`, 54 000): K=512 — r1 b4 67,3%, r1 b8 65,0%, r3 b4 66,4%; K=1600 r1 b8 63,2%. Не помогает. +- Вывод по объектам: в KNP дофамин по ошибке даёт 65–69% (колонки STDP — 64–65% на разреженных, ≈0% на плотных); ridge на тех же признаках — 74%. Вероятная причина разрыва — ограниченный и несимметричный диапазон весов KNP (−253…+92) и насыщение ресурса. diff --git a/objects/make_knp_features.py b/objects/make_knp_features.py index e1d221f..067d40e 100644 --- a/objects/make_knp_features.py +++ b/objects/make_knp_features.py @@ -23,12 +23,19 @@ def main(): ap.add_argument("--k", type=int, default=512) ap.add_argument("--alpha", type=float, default=0.25) ap.add_argument("--seed", type=int, default=20261014) + ap.add_argument("--model", help="use a learned model (npz with mean, zca, d) instead of learning K prototypes") + ap.add_argument("--name", default="kmeans", help="output file prefix") a = ap.parse_args() out = Path(a.out) out.mkdir(exist_ok=True) kf.CONTRAST_DIVIDE = False tr, va = np.load(f"{a.data}/train.npz"), np.load(f"{a.data}/val.npz") - model = kf.learn(tr["rgb"], k=a.k) + if a.model: + m = np.load(a.model) + model = {key: m[key] for key in ("mean", "zca", "d")} + a.k = len(model["d"]) + else: + model = kf.learn(tr["rgb"], k=a.k) ft = np.vstack([kf.encode(tr["rgb"], model, alpha=a.alpha), kf.encode(tr["rgb"][:, :, :, ::-1], model, alpha=a.alpha)]) yt = np.r_[tr["labels"], tr["labels"]] fv = kf.encode(va["rgb"], model, alpha=a.alpha) @@ -37,13 +44,13 @@ def main(): order = np.random.default_rng(a.seed).permutation(len(ft)) x = np.vstack([q(ft)[order], q(fv)]) y = np.r_[yt[order], va["labels"]] - assert x.shape[1] == 2048 - x.tofile(out / "kmeans_validation.bin") - (out / "kmeans_validation.target").write_text("\n".join(map(str, y)) + "\n") - np.savez(out / "kmeans_model.npz", gain=gain, **model) + assert x.shape[1] == 4 * a.k + x.tofile(out / f"{a.name}_validation.bin") + (out / f"{a.name}_validation.target").write_text("\n".join(map(str, y)) + "\n") + np.savez(out / f"{a.name}_model.npz", gain=gain, **model) info = {"k": a.k, "alpha": a.alpha, "train": len(ft), "validation": len(fv), "contrast_divide": False, - "mean_value": float(x.mean()), "spikes_per_image": float(x.mean() / 255 * 10 * 2048)} - (out / "manifest.json").write_text(json.dumps(info, indent=2) + "\n") + "mean_value": float(x.mean()), "spikes_per_image": float(x.mean() / 255 * 10 * x.shape[1])} + (out / f"{a.name}_manifest.json").write_text(json.dumps(info, indent=2) + "\n") print(info) diff --git a/Отчёт_объекты_ночь.md b/Отчёт_объекты_ночь.md index 366e3a4..2474954 100644 --- a/Отчёт_объекты_ночь.md +++ b/Отчёт_объекты_ночь.md @@ -63,6 +63,18 @@ - **Нейрон-решатель без подбора порога.** Смещение нейрона сдвинуто так, что «потенциал дошёл до порога» означает «сумма больше нуля». Результат не зависит от порога. - **Больше шагов на картинку** (20 вместо 10) — +1,3 п.п.: 72,9% вместо 71,6%. +## Обучение по ошибке прямо в KNP (утро 15 октября) + +В KNP добавлен режим `KNP_DELTA`. Метка даёт нейрону своего класса положительный дофамин на каждом шаге, а собственный спайк нейрона даёт ему отрицательный. Дофамин получается равным «цель минус ответ», то есть это правило перцептрона на нейронах. Для него понадобилась отдельная копия бэкенда, `readout/build_delta_cpu.py`: в ней синапс меняется на величину дофамина, если по нему только что пришёл спайк, даже когда сам нейрон молчал. Оригинальные библиотеки не менялись. + +| Задача (проверочная выборка) | Колонки STDP | **Дофамин по ошибке в KNP** | Выход, обученный вне чипа | +| --- | --- | --- | --- | +| Цифры, признаки штрихов, 30 000 | 83,5% (200 нейронов) | **94,8%** (10 нейронов) | 94,9% | +| Цифры, то же + аугментация, 90 000 | — | 94,6%; сдвиг 2 px **91,3%** | 95,1%; сдвиг 89,9% | +| Кошки/собаки, выученные признаки | 64–65% (на плотных молчат) | **65–69%** | 74% (без спайков) | + +На цифрах обучение внутри платформы догнало обучение вне чипа и дало самую устойчивую сеть за всё время. На объектах выигрыш у колонок небольшой. Разрыв с 74%, скорее всего, из-за диапазона весов KNP: от −253 до +92, и около краёв вес почти перестаёт меняться. + ## Цифры на странице На странице https://claude.ai/artifact/Vc1q1WCZuPY2QAHBSGtrKF появилась панель «4 · Сеть штрихов»: 16 карт штрихов, спайки 10 выходных нейронов и ответ. На тесте MNIST эта сеть даёт 94,7%, колонки — 92,2%. Сеть входит в строку «Итог всех сетей», так что её можно сравнивать на своих рисунках и сдвигах. Расчёт в браузере сверен с Python: на 105 картинках ответы совпали.