Современный Deep Learning уперся в производительность вычислений с плавающей точкой и пропускную способность памяти. Мы предлагаем архитектуру, где нейрон — это не сумма произведений, а битовая хэш-функция. Ноль умножений, ноль сложений. Только логика и статистика.

Концепт: Нейрон как компаратор
Классический нейрон: Activation(Sum(Weight * Input))
Наш нейрон: Output = A[ popcount( Input XOR Mask ) ]
Разберем формулу:
Input (64 бита): Входные данные.
Mask (64 бита): «Взгляд» нейрона. Фиксированный случайный (для изображений) или не случайный (для текста) шаблон.
XOR: Битовое сравнение. 0 — совпадение, 1 — отличие.
popcount: Аппаратная инструкция процессора (Population Count). Считает количество единиц в слове. Это Дистанция Хэмминга — насколько вход отличается от маски (число от 0 до 64).
A [...] (LUT): Массив отклика. Это память нейрона, где хранится его «опыт».
Массив A: Эволюция от статистики к биту
Самое важное — как живет и меняется массив A.
1. Обучение (Накопление) *Концепт
На этапе обучения A — это массив счетчиков (например, uint16 размером 65 ячеек).
Подаем пример.
Вычисляем дистанцию dist = popcount( Input XOR Mask ).
Если пример «полезный» — делаем A[dist]++.
Мы просто копим гистограмму: при каком расстоянии Хэмминга этот нейрон должен активироваться.
2. Компиляция (Порог)
Обучение закончено. Мы превращаем «жирные» счетчики в чистую логику.
Применяем Threshold:
Если A[i] > порог — записываем 1.
Иначе — 0.
Теперь массив A — это набор битов. Так как индексов всего 65 (от 0 до 64), весь массив A сжимается в одно 64-битное целое число.
3. Инференс (Скорость)
В рабочем режиме нейрон — это два регистра процессора: Mask и сжатый A.
Логика умещается в 4 инструкции ассемблера:
XOR (Сравнить маску и вход)
POPCNT (Получить дистанцию)
SHIFT (Сдвинуть сжатый массив A на величину дистанции)
AND (Взять младший бит)
Никакого FPU. Теоретическая производительность ограничена только частотой процессора.
Попробовать нейросеть в деле можно здесь:
https://schoolscience.org/bit_nn/
Let the bitwise revolution begin!
Комментарии (21)

SensDj
13.02.2026 13:45я попросил чат гпт 5.2 высказаться про вашу идею, вот его мнение
Ниже — разбор без «хайпа»: идея сама по себе не бессмысленная (это по сути нейрон радиальной базисной функции в пространстве Хэмминга), но в изложении есть как минимум несколько логических/технических дыр и несколько мест, где автор сильно перепродал скорость/универсальность.
1) Что это на самом деле за «нейрон»
Функция вида
y=A[popcount(x⊕m)]
зависит только от расстояния Хэмминга до некоторого центра m. Это значит:
нейрон не различает, какие именно биты отличаются, важна лишь сколько отличается;
это «кольцевая» (по радиусу) функция вокруг маски m в 64‑мерном булевом кубе.
Это близко по духу к:
RBF/gaussian-like признакам, но в дискретном (Hamming) пространстве;
старым RAM-based моделям и «weightless neural networks» (WiSARD и т.п.), хотя там обычно LUT по адресу из битов, а не по расстоянию.
Вывод: это не замена классического нейрона вообще. Это один конкретный тип признака: “насколько похоже на шаблон”.
2) Главная концептуальная проблема обучения в статье: «копим только полезные примеры»
Автор пишет: “если пример полезный — A[dist]++”. Но если вы увеличиваете счётчики только для положительных/“полезных” примеров, то вы не учите нейрон избегать ложных срабатываний.
Типичная ситуация:
у вас есть класс “кот” и класс “не кот”;
и для “не кот” тоже будут встречаться некоторые расстояния до маски;
без учёта “не кот” вы получите bins, которые часто встречаются везде, и они пройдут порог.
Правильнее хотя бы хранить две гистограммы (positive/negative) или один массив, но обновлять по знаку:
-
вариант A (две гистограммы):
P[i]++ для positive
N[i]++ для negative затем решать по P[i]−N[i] или по отношению P[i]/(N[i]+1)
-
вариант B (одна “разностная”):
S[i]+=1 для positive
S[i]−=1 для negative затем пороговать S[i]
Иначе “компиляция порогом” превращается в очень шумную эвристику.
3) Огромная потеря информации: одинаковая реакция на все конфигурации с тем же dist
Для 64 бит число различных входов на фиксированном расстоянии d равно (d64). Например, при d=32 это гигантское количество. Вся эта масса будет иметь одинаковый выход, потому что вы используете только d.
Это даёт:
сильную инвариантность (иногда полезно),
но часто катастрофическое недообучение: модель не может выучить “важные позиции битов”, только “сколько не совпало”.
Классический нейрон с весами различает вклад каждого бита, а здесь вклад “позиции” уничтожен.
4) «Сжимаем A в одно 64‑битное целое» — ошибка (off-by-one)
Для 64-битного входа:
popcount(⋅)∈{0,1,2,…,64}
То есть индексов 65, нужно 65 бит, а не 64.
Можно сделать:
два 64-битных слова (например, 128 бит хранения);
или хранить 64 бита для 0..63 и отдельный бит для случая 64;
или поменять постановку (но popcount всё равно даёт 64).
5) «SHIFT на величину дистанции» ломается при dist = 64 (и часто — даже формально)
Даже если закрыть глаза на 65-й бит, есть ещё хуже:
На x86 сдвиг на 64 для 64-битного регистра маскируется (берутся младшие 6 бит счётчика), т.е. shift by 64 эквивалентен shift by 0. Это даст неверный результат для случая dist=64.
В C/C++ сдвиг на ширину типа и больше — вообще Undefined Behavior.
То есть нужно отдельное ветвление/маскирование под dist=64, что ломает “4 инструкции безусловно”.
6) Заявление “никаких чтений из оперативной памяти” неверно для реальной сети
Для одного нейрона в вакууме можно представить, что Mask и A уже в регистрах.
Но любая реальная модель — это много нейронов:
у каждого свой Mask и своё A;
их нужно загружать из памяти (а это обычно главный лимит производительности).
Итог: на практике вы упрётесь не в ALU, а в memory bandwidth / cache misses. POPCNT действительно быстрый, но “без памяти” — маркетинг.
7) “Фиксированный случайный Mask для изображений” — очень спорно
Для изображений важна локальность и структура (окна/патчи). Если вы просто берёте 64 случайных бита из изображения (или случайную маску поверх 64 бит), вы часто:
уничтожаете устойчивые признаки,
получаете “рандомный отпечаток”, который плохо переносится при сдвигах/шуме/освещении.
Чтобы это работало, обычно делают:
много таких нейронов (как random features),
продуманную схему выбора битов (патчи, каналы, локальные рецептивные поля),
нормализацию/устойчивое бинаризующее кодирование.
8) Где это может быть полезно (и почему идея вообще “не мусор”)
Это похоже на очень быстрый, дискретный “похожесть на прототип”:
быстрый фильтр “похоже/не похоже” для 64-битных отпечатков;
дедупликация/near-duplicate для бинарных хэшей;
локальные признаки/аномалия, если маски — прототипы;
как часть ансамбля (много масок) — может стать рабочим классификатором.
Но это не универсальная замена нейронов и не “бесплатная магия”.
9) Минимальные правки, чтобы концепт стал технически корректнее
Если хочется довести до “рабочего” состояния:
Исправить 65-й случай:
хранить A как 65 бит (например, два uint64), и отдельно обрабатывать dist==64.
Учить с негативами:
хранить P/N или разностный массив и пороговать по разделяющей статистике, а не по “частоте в позитиве”.
Не обещать “без памяти”:
честно считать, что inference будет memory-bound при большом числе нейронов.
Признать ограничение модели:
выход зависит только от расстояния, а не от структуры различий.

mozg4d Автор
13.02.2026 13:45тут сложно сказать что он в чем-то не прав, но для более полной картины, я еще бы спросил у gemini, как показывает практика он чуть более продвинутый, в том числе рекомендую вскормить ему ответ от GPT и этот мой ответ.
На 65-й бит жалко тратить UINT64) я пока не знаю как решить эту проблему, но 1 бит надо удалить)
да, обучение предполагается с негативом, об этом не сказано, т.к. я пытался сделать описание как можно короче и понятнее
Но вот выход зависит от структуры различий а не отлько от расстояния. тут он не прав или невнимателен.. нейрон то не один, разные нейроны смотрят на разные биты
Насчет памяти - исправлю, но всеже Хочется надеяться что нейронки построенные по reverse hash будут менее прожорливыми по памяти.
wataru
Без математического доказательства аналога Universal Approximation Theorem - это голословная демагогия. Вообще ниоткуда не следует, что ваш подход будет работать. Хоть бы тесты до публикации какие-нибудь погоняли. Хотя бы что-то тривиальное, вроде распознавания цифр.
Далее, с чего вы взяли, что это будет работать быстрее? У вас там индексация в массиве - это полный запрет на распараллеливание.
Ну и, вообще непонятно, как ваша модель работает с несколькими инпутами. Вон, в перцептронах идет суммирование по всем входам. У вас как? Типа, все входы - это разные биты input, а маска задает, какие входы инвертируются? Т.е. там фактически идет тупо сумма со множителями +-1 и потом берется пороговая функция активации. Т.е. у вас сильно урезанный перцептрон получается.
Обучение - вообще с потолка взято. Обратное распространение ошибки - это ведь градиентный спуск в пространстве параметров. У вас же какие-то счетчики. Даже не объяснено, как оно распространяется по сети. Плюс, если я правильно понял и ваша mask - это инвертация входов, то при распространении у каких-то нейронов надо наоборот счетчики уменьшать, если их выход берется инвертированным.
mozg4d Автор
все будет) руки пока не дошли.
Индексация в массиве - только в процессе обучения. Потом - это операция (A >> dist) & 1 (массив предращается в 64 битное число)
Входы - разные биты input - не уверен что будет работать. для распознавания символов планируется ранговое кодирование. Для друих задач - "heatmap" - больше число - больше единиц в байте
У нас массив A (LUT) позволяет задать любую нелинейную реакцию на расстояние (так что скорее продвинутый а не урезанный персептрон).
Для обучения планируем опираться на принципы Feedback Alignment и Target Propagation. В нашем Proof of Concept для MNIST планируем использовать логику близкую к Extreme Learning Machines (ELM): скрытые слои создают случайную проекцию признаков, а обучается только последний слой (статистически). Теоретически градиентный спуск тут не нужен, но покажут тесты..
Маски - это просто статические случайные "картинки" они не меняются в процессе обучения
wataru
А как тогда? Что вообще у вас за input? В нейросети в один перцептрон идет несколько входов от разных нейронов. Они за счет этого и работают. У вас же формула от одного Input.
mozg4d Автор
Зависит от задачи. В случае распознавания рукописных цифр думаю использовать ранговое кодирование -
Нейрону не важна абсолютная яркость. Ему важен перепад (градиент).
Мы берем 64 случайные пары точек изображения.
Если точка А ярче точки Б — пишем 1.
Иначе — 0.
Но это не значит что "тупо" брать случайные биты - не сработает. этого я не знаю
А в средних слоях инпуты - это аутпуты других нейронов. У нас у каждого нейрона 64 инпута, 1 аутпут (64 бита на вход, 1 на выход). Можно в принципе AVX512 использовать, тогда можно и больше бит на вход использовать, но 64 на современных процах наверно оптимум, батч (за такт сразу 4 нейрона считать например, заюзав 256 бит) при этом конечно можно использовать, но это уже тонкости
Выход нейронов первого слоя идет на рандомный вход нейронов второго слоя. сети не обязательно быть полносвязной
wataru
Как вы эти несколько выходов других нейронов передаете в этот один? Они составляют биты input, ведь так? Это ровно то, что я написал: "Типа, все входы - это разные биты input".
Сомнительное утверждение. У вас там все возможные функции: выдать 0 или 1 в зависимости от количества активных входов (некоторые фиксировано инвертированы). Вот это вот "количество активных входов" делает все входы равноправными и исключает любую возможность разделять сильные и слабые признаки.
Я вам сейчас вообще формально докажу, что ваша модель точно слабже обычных нейросетей.
Вообще, можно маски все у вас убрать, преобразовав вашу сеть: раздвоите каждый перцептрон, во второй копии инвертируйте таблицу A, соедините выход оригинального перцептрона с теми парами перцептронов, для которых в маске стоит 0. А выход инвертированной копии с теми, где в маске стоит 1. Получившаяся сеть может делать те же самые вычисления, что и изначальная, но может и больше, ведь в этой модели жесткая структура пар перцептронов, у которых инвертированны таблицы и входы и выходы аналогичны. Т.е. ваша модель точно не сильнее модели без масок.
Далее, можно и таблицу выкинуть, разбив каждый перцептрон на несколько новых. Первый будет тупо суммировать все входы и выдавать сумму/количество входов N. Эта сумма идет с весами +-1 (фиксированными от mask) в пару пороговых линейных перцептронов "сумма <= i" и "сумма >= i", которые выдают 0 или 1. Пара этих выходов с весами +1 идет в пороговый линейный перцептрон "input >= 2". Вот уже у нас есть N перцептронов, ровно один из которых активируется при заданной сумме входов. Это popcnt. Теперь осталось собрать из них выход, соответствующий таблице A[], так чтобы эти значения в A[] были весами. Ну, тупо ставим "input-0.5" перцептроны после каждого, потом все это с весами +-1 в зависимости от A[] соедняем с выходным перцептроном с пороговой активацией " сумма > -количество входов".
Итак, ваша изначальная сеть аналогична обычной нейросети, где абсолютно все веса -1|1 и все перцептроны пороговые, и очень фиксированная архитектура. Изменения в A[] в вашей модели приводят лишь к инвертации некоторых весов. Это сильно урезанный вариант нейросети. Очень большой вопрос, выполняется ли в нем теорема апроксимации. Без нее оно работать не будет.
mozg4d Автор
у нейрона 64 битовых входа, соответственно он может принять 64 входа от 64 нейронов. Если нейнонов в слое до текущего больше чем 64, что всегда будет верно, то нейрон текущего слоя примет входы от рандомных нейронов предыдущего слоя (берем количеством)
В принципе можно подумать о том чтобы нейрон имел вызод больше одного бита, но предварительно вычислительно выгоднее увеличитиь количество нейронов а не увеличивать битность выхода (требует проверки)
Действительно, XOR + POPCNT + LUT можно представить как ансамбль пороговых перцептронов с весами +-1, Но дьявол, в деталях реализации и нелинейности.
Вы пишете, что модель точно не сильнее обычной нейросети. Возможно. Но классический пример: функция XOR. Один классический перцептрон не может решить XOR. Ему нужна сеть.
Наш один LUT-нейрон решает XOR от 64 входов мгновенно. Для этого достаточно заполнить таблицу A[] чередующимися нулями и единицами.
Таким образом, один наш «урезанный» нейрон на 64 входа способен реализовать функции, для которых классическому перцептрону (даже с float весами) понадобится скрытый слой. LUT дает нам произвольную нелинейность от расстояния. Да. это частный случай, но он - показательный
Про «можно выкинуть маски» :
Математически — да, маска это просто перенос начала координат. Но инженерно — это LSH (Locality Sensitive Hashing).XOR + POPCNT — это способ вычислить «похожесть» входа на эталон (маску) за 2 такта процессора. Если мы выкинем маски и будем пытаться собрать это из пороговых перцептронов, как вы предложили в доказательстве, мы получим чудовищную конструкцию, которая будет работать в 100 раз медленнее. Наша цель — максимальный КПД на такт процессора.
Про теорему аппроксимации: Существует теорема для WiSARD (Weightless Neural Networks), которая доказывает их аппроксимационную способность. Наша модель — это WiSARD «на стероидах», где мы добавили топологическую связность через расстояние Хэмминга.
Мы не пытаемся сделать модель, которая «умнее» классической нейронки в бесконечном пространстве. Мы делаем модель, которая эффективнее классической в пространстве команд современного CPU. Классический нейрон тратит 64 умножения, чтобы разделить пространство одной плоскостью. Наш нейрон за 4 инструкции разделяет пространство 64-мя нелинейными «сферами».
Тесты на MNIST уже готовятся — они и станут финальным аргументом в споре о «слабости».
wataru
Ну вот их не один, а несколько, отлично xor считают. Я указал как. Но от количества нейронов класс сети не меняется. Мы же обсуждаем возможности класса сетей, который вы вводите.
Да, ваша формула считается быстрее аналогичной нейросети. Но, это неважно. Потому что вообще считать такие сети может и смысла нет. Максимум 64 входа в каждый перцептрон? В ллмках их десятки тысяч. Тут только какие-нибудь очень узкие применения в крошечных сетях. А там они итак работают мгновенно. Плюс веса в такой нейросети +-1 и при этом многие веса вообще нельзя менять. Может оно вообще не сходится ни к чему в обучении.
Давайте просто возьмем среднее арифметическое всех пикселей входной картинки. Это еще эффективнее ваших XORов и перцептронов. Сильно эффективнее! Но и возможности сильно меньше. Без возможностей эффективность бесполезна.
Потом, вы фокусируетесь на CPU. Для любого достаточно сложного применения у вас должны быть миллионы параметров, и там уже рулит GPU, TPU и прочие ускорители. А они уже под работу с float заточены.
Ну вот с этого и надо было начинать еще до статьи. А то вы как некоторые чиновники, которые постоянно анонсируют то базу на луне, то еще что-то. Были бы хоть какие-то твердые данные, было бы гораздо меньше вопросов.
mozg4d Автор
готово:
https://schoolscience.org/bit_nn/
не идеал, сделано "на коленке" но это-же proof of concept. Важем сам факт - работает)
На CPU - скорее можно попробовать, но сам концепт нейросети просто идеально ложится на асики, и проблем с использованием GPU тоже особо не видно
wataru
Что-то там точность весьма низкая. У меня оно только 1 нарисованное и отгадало правильно. Все остальные цифры - как попало:
Так, ваша формула получается ускорением 1-битных нейросеток, с пороговыми перцептронами и не более 64 входов для каждого нейрона. Какую-то точность оно даже даст. Право на жизнь идея имеет. Но я бы не называл это революцией. На собственно GPU эта формула ускоряется плохо, поэтому традиционные нейросетки все равно на практике будут и быстрее и точнее. Даже на мобильных процессорах, там везде встроенные iGPU же.
Еще, я так и не понял, как у вас идет обучение. Вот для выходного слоя понятно, какие нейроны должны быть активированны и "положительный" ли пример. Как оно распространяется назад?
mozg4d Автор
Битность и входа в выхода может быть любой, скажем проблем сделать нейрон с 10000 входов - нет и масштабируется линейно, то есть нет такого что "большущий" нейрон будет сильно медленнее считаться. Размер нейрона подбирается под задачу и железо.
Да, действительно, я выложил недостаточно протестирова) подправлю. На тестовом датасете точность в среднем 98%, какой-то косяк при анализе нарисованной картинки(
Я думаю о том чтобы найти большой интересный датасет и сделать на этом принципе нейронку побольше, в том числе как доказательство работоспособности и эффективности, но я это делаю по фану, и когда руки дойдут - не знаю.
Обучение в простейшем случае:
Когда мы показываем сети цифру «7»:
Нейрон вычисляет dist = popcount(Input ^ Mask).
Мы делаем A[dist]++ (если это правильный класс) или A[dist]-- (если неправильный).
wataru
Ну так там индексация тогда будет от суммы битов. Это медленно. Ассимптотически, да, все такая же линейная сложность, конечно, ну так и ваша оптимизация с XOR-ами - не ассимптотическая. Вы лишь в несколько раз будете быстрее из-за архитектурных особенностей вычислений.
Ну я понял, как для выходного нейрона это делать. Что происходит с нейронами в промежуточных слоях? У него нет класса, как ему понять, надо делать ++ или --?
mozg4d Автор
Конечно планы могут измениться, но базовый сценарий - статистическими методами. Очень упрощенно - берем текст и строим табличку - такая-то маска встречатеся с такой-то вероятностью и после нее идут символы с такой-то вероятностью - это данные чтобы собрать первый слой. Потом берем тот-же текст и "сжимаем его" через эту табличку, прогоняем получившееся и строим новую такую-же табличку - получаем второй слой. *этот концепт сейчас в процессе разработки и это лишь базовое описание идеи
wataru
Стоп, у вас там однослойная нейросеть, а как обучать многослойную вы еще не придумали?
mozg4d Автор
Да, но скорее не не придумал, а не проверил - будет ли работать. Сейчас работаю над этим.. Делаю мини чат бота на reverse hash нкйронах
wataru
Ну так расскажите же уже, наконец-то, как происходит обучение средних слоев? Как вы определяете, делать ++ или -- счетчика?
mozg4d Автор
пока у меня нет хорошего ответа на этот вопрос. но скоро появится) как будет готов - напишу
mozg4d Автор
пофиксил, теперь и ручками нарисованные символы нормально понимает) ссылка - не изменилась
ElWray
+5070 от Куртки.