Хотел обучить нейросеть без backprop, а получил k‑means

Я решил проверить, как далеко можно зайти, если обучать скрытый слой нейросети вообще без обратного распространения ошибки. Только локальными правилами: каждый нейрон видит свой вход, свою активность и что делают соседи, и все. Никакого глобального сигнала ошибки и никаких меток.
Спойлер: в моей постановке такое обучение в итоге свелось к обычному k‑means. Оно обгоняет k‑means с той же архитектурой максимум на полпроцента и ни в чем не обгоняет backprop: ни по точности, ни при малом числе меток, ни по забыванию.
Но по дороге было много интересного. Сеть которая убивает собственные нейроны. Эволюция, которая обманывает саму себя. И несколько моих собственных ошибок, из‑за которых я чуть не написал совсем другую статью. Если вы смотрите в сторону хеббовского обучения, это может сэкономить вам время.
С чего все началось
Отправной точкой была статья Krotov & Hopfield (2019) “Unsupervised learning by competing hidden units”. Идея простая. На каждом примере нейроны соревнуются, у кого активность больше. Победитель подтягивает свои веса к входу (обычное правило Хебба), а нейрон, занявший k‑e место, наоборот, немного отталкивается (анти‑Хебб). Еще есть член из правила Ойи: он не дает весам разрастаться и держит их норму около единицы.
Правила я себе поставил жесткие:
веса скрытых слоев меняются только локально. Нейрону можно знать свой вход, свою активность, сигналы соседей и свое внутреннее состояние (порог, частоту побед)
никакого backprop, глобальной ошибки и меток при обучении скрытых слоев
метки видит только линейный классификатор (логистическая регрессия) поверх признаков relu(Wx)³. Он нужен только чтобы оценить, насколько признаки хороши.
И еще протокол, без которого половина выводов была бы неправильной. Каждый эксперимент меняет ровно один параметр относительно предыдущего. Каждый запуск идет на 5 сидах, и разницу я считаю реальной, только если она больше двух стандартных отклонений. Гиперпараметры подбирают только по validation, test открыл один раз в самом конце. И в каждом сравнении есть эталоны с тем же числом нейронов: сырые пиксели, случайные веса, обычный MLP на backprop и (как потом выяснилось, самое важное) k‑means.
Все написано на numpy, чтобы каждое обновление весов было на виду. Один прогон на MNIST занимает около 30 секунд на Apple M4 Pro.
Первый звоночек
Прототип гонялся на sklearn digits (маленькие картинки цифр 8×8, всего 1797 штук) и выдавал красивые числа: локальное правило 0,974, сырые пиксели 0,961, случайные веса 0,941. Выглядело так, будто правило выучило что‑то полезное сверх самих пикселей.
Я перенес код в нормальный проект, убедился, что он повторяет прототип, и начал менять разбиение данных.
что проверял | локальное правило | сырые пиксели | разница |
прототип: 1 сид, разбиение 70/30 | 0,974 | 0,961 | +0,013 |
то же разбиение, 5 сидов | 0,969±0,003 | 0,961 | +0,008 |
5 разных разбиений 60/20/20, по 5 сидов | -0,005±0,007 |
На других разбиениях преимущество над пикселями пропало: разница в пределах шума, причем скорее в минус. Устойчиво держалось только одно: правило лучше случайных весов на 2,6 п.п. Еще выяснилось, что разброс между разбиениями (около 1,2 п.п.) больше, чем между сидами (около 0,5 п.п.), а validation из 360 картинок различает методы буквально на 1–4 картинки.
Вывод, который потом пригодился еще не раз: один сид и одно разбиение на маленьком датасете ничего не доказывают.
Сеть, которая убивает свои нейроны
В прототипе 44% нейронов оказались мертвыми, то есть побежали меньше чем в 0,1% примеров. На картинке весов это сразу видно: живые нейроны выучили прототипы цифр, а мертвые так и остались случайным шумом. Их норма около 8, ровно как у случайного вектора N(0,1) в 64 измерениях. Значит, их ни разу не обновили. У живых норма ровно 1,000, как и обещает правило Ойи.

Заодно пришлось починить одну вещь, которая нарушала мои же правила. В прототипе (и у Krotov & Hopfield) шаг обновления делится на максимум |ΔW| по всем нейронам сразу. Получается, что каждый нейрон подстраивает свой шаг под то, что делают остальные, а это уже не локально. Если просто делить каждому на свой максимум, мертвых становится 76%: пропадает множитель δ, и отталкивание становится таким же сильным, как притяжение. Сработал вариант, где нейрон еще умножает шаг на свой сигнал конкуренции. Он полностью локальный и по всем метрикам не отличается от оригинала.
Коллапс
Если чуть увеличить шаг обучения (lr от 0,2) или усилить отталкивание (δ = 0,8), сеть коллапсирует. Один нейрон побеждает на всех примерах, точность падает до 0,14. А на MNIST те же параметры, что нормально работали на digits, коллапсируют на всех пяти сидах (точность 0,198), потому что шагов за эпоху там в 45 раз больше.
Вот так выглядят веса после коллапса. Один нейрон (красный) стал «средней цифрой», а почти все остальные превратились в ее негатив (синие).

Это ловушка в самом правиле. Если подставить w = ‑x/|x|, активность получается a = ‑|x|, и отталкивающая добавка ‑δ(x‑a·w) становится нулевой. Нейрон, который слишком часто отталкивали, уезжает туда, где его активность на любой вход отрицательная. ReLU там всегда выдает ноль, и выбраться оттуда он уже не может. То есть нейроны не просто «не рождаются» после инициализации, их убивает само правило.
Адаптивный порог
Лекарство известное (DeSieno, Földiák, Diehl & Cook). У каждого нейрона свой порог θ. Если нейрон побеждает чаще, чем 1/N, порог растет, если реже, падает. В конкуренции участвует a — θ. Это внутреннее состояние самого нейрона, так что с локальностью все в порядке.
Что дал порог: мертвых стало 0–0,2% вместо 43–47%; коллапс исчез во всем диапазоне lr и δ, который я проверял; параметры с digits заработали на MNIST без всякой подстройки, и все 100 нейронов стали чистыми прототипами цифр.

А вот точность почти не выросла. Если сравнивать со стабильным вариантом без порога (меньший lr, 13% мертвых), разница на MNIST -0,16 ± 0,20 п.п., то есть шум. Линейному классификатору хватает и 87 живых нейронов. Так что порог нужен для стабильности, а не для точности.
Неприятное открытие: это k‑means
Прототипы цифры на картинках выше подозрительно похожи на центроиды кластеров. Я добавил в эталоны обычный k‑means: те же N центроидов вместо весов, те же признаки relu(Wx)³, тот же классификатор. И он оказался почти таким же хорошим.
Кроме MNIST, я проверял все на Fashion‑MNIST. Если кто не сталкивался, это тот же формат (черно‑белые картинки 28×28, 10 классов, 70 тысяч штук), только вместо цифр там одежда и обувь: футболки, брюки, платья, кроссовки, сумки и так далее.
точность на test | MNIST | Fastion‑MNIST |
локальное правило, плотный слой (100) | 0,9462 | 0,7959 |
k‑means, плотный (100) | 0,9414 | 0,7956 |
разница (парно по 5 сидам) | +0,48 ± 0,08 п.п. | +0,03 ± 0,30 п.п. (шум) |
локальное правило, локальные поля (400) | 0,9707 | 0,8359 |
k‑means по тем же участкам (400) | 0,9695 | 0,8316 |
разница | +0,13 ± 0,07 п.п. | +0,43 ± 0,11 п.п. |
Откуда берется даже эта небольшая разница, хорошо видно из одного опыта на MNIST. Если убрать отталкивание (δ = 0), правило с порогом дает 0,9406 ± 0,0008, а k‑means 0,9410 ± 0,0008. Практически одно и то же. Вообще‑то этого стоило ожидать: конкурентное обучение «победитель забирает все» с нормировкой весов и есть классический онлайн k‑means, а порого просто помогает ему не терять кластеры. С δ = 0,4 получается 0,9457. Значит, весь выигрыш сверх кластеризации (+0,5 п.п., больше 3δ) дает отталкивание второго по силе нейрона.
Совет, если будете делать что‑то похожее: ставьте k‑means в эталоны с самого начала. Сравнение со случайными весами и сырыми пикселями выглядит красиво (на MNIST +13 и +3 п.п.), но почти весь этот выигрыш дает обычная кластеризация.
Карты и глубина: красиво, но точность не растет
Я разложил 100 нейронов по сетке 10×10 и попробовал два варианта. В первом соседи победителя тоже немного учатся, с весом exp(‑d²/2σ²), как в самоорганизующихся картах Кохонена. Карты получаются настоящие, с областями цифр и плавными переходами между ними. Корреляция между расстоянием на сетке и разницей весов +0,67 на digits и +0,80 на MNIST.

Только за карту приходится платить точностью: при σ = 1 минут 2,5 п.п. на digits и минус 5,8 п.п. на MNIST. Соседи стягиваются друг к другу, и из 100 нейронов остается примерно 21 по‑настоящему разный прототип. Линейному классификатору все равно, где нейрон стоит на сетке, а вот потерю разнообразия он чувствует сразу. Если уменьшать σ к концу обучения, точность возвращается, но на MNIST карта при этом исчезает полностью.
Во втором варианте нейрон соревнуется только с соседями в радиусе r. Карты так не получается, наоборот, соседи становятся непохожими, потому что торможение их расталкивает. Точность при r = 1 падает до 0,865 и на digits, и на MNIST. На каждом примере побеждает около 20 нейронов сразу, каждый отвечает примерно за пятую часть данных и учит размытую смесь цифр. На digits из 100 нейронов остается всего 5 разных прототипов.
Второй слой имеет смысл только поверх «деталей»
Поверх плотного слоя прототипов второй слой с тем же правилом бесполезен. Сам по себе он хуже первого. Вместе с перым дает +1 п.п. на MNIST, но ровно столько же дает один слой из 200 нейронов или первой слов плюс случайный второй. Причина в том, что код первого слоя плотный: все прототипы неотрицательные и хоть немного откликаются почти на любую цифру. Второй слой кластеризует эти плотные векторы и получает что‑то вроде среднего по 64 прототипам.
Все меняется, если первый слой смотрит не на всю картинку, а на кусочек. Я сделал 16 участков 10×10 с шагов 6, по 25 нейронов на участков. Веса у участков не общие (это locally connected слой, а не свертка), и соревнуются нейроны только внутри своего участка. Слой сам выучил детали цифр: штрихи, дуги, углы. А второй слой собирает из них силуэты цифр.

В итоге: слой из 400 таких «деталей» дает 0,969 на validation MNIST против 0,946 у плотного слоя из 100 нейронов. Это самый большой прирост за все исследование. При одинаковой ширине (400 плотных прототипов дают 0,965) выигрыш скромнее, +0,4 п.п. И дала его архитектура, а не само правило.
100 нейронов второго слоя поверх деталей лучше 100 прототипов на пикселях (+0,4 п.п.), а вместе с первым слоем добавляют то, чего нет у случайного второго слоя. Но k‑means поверх тех же деталей дает ровно столько же.
Эволюция правила, или как поиск обманывает сам себя
Раз ручное правило по сути k‑means, логично поискать правило получше автоматически. Я записал обобщенное хеббовское правило с коэффициентами и стал подбирать их через CMA‑ES. Здесь g это сигнал конкуренции нейрона (1 у победителя, −δ у k‑го, 0 у остальных), a его активность. Член с E держит норму весов, без него такое правило просто разлетается. Мое ручное правило соответствует A = 1, E = 1, B = C = D = 0.
Первая попытка: подгон под сиды. Фитнесом была точность на validation digits при N = 25 нейронах, усредненная по трем фиксированным сидам. Поиск нашел правило лучше ручного на 1,5 п.п., а дальше все рассыпалось. На новых сидах и других разбиениях при тех же N = 25 разница с ручным правилом вышла +0,15 ± 0,19 п.п., то есть ноль. При N = 100 точность падает с 0,967 до 0,90 и 0,80, мертвых нейронов 44–70%. На MNIST одно из найденных правил вообще разлетается.
Виноватыми оказались члены B и D. Если в ручное правило подставить одно только B = -0,04, мертвых становится 95%. Эти члены меняют веса всех нейронов на каждом шаге, а уравновешивает их только хеббовский член, который срабатывает ли у победителя, то есть примерно в 1/N случаев. Баланс, подобранный при N = 25, при N = 100 съезжает в 4 раза, и нейроны уходят в ту же ловушку, что и при коллапсе. Сначала я думал, что дело в пороге (у него целевая частота тоже 1/N), но это не подтвердилось: когда я усиливал порог, становилось только хуже.
Вторая попытка: нашлось тормозное окружение. Во второй раз я убрал члены B и D, считал фитнес сразу по трем задачам (digits при N = 25 и N = 100 плюс кусок MNIST из его обучающей части) и брал новые сиды в каждом поколении.
Два независимых запуска пришли к одному и тому же типу правила. Во‑первых, C < 0: победитель на каждом шаге вычитает константу из всех своих весов, и у фильтра появляется тормозное окружение, что‑то вроде «чернила здесь, но не вокруг» (отрицательных весов 62% вместо 10%). Во‑вторых, сильное отталкивание (δ около 0,8), которое без порога вызывало коллапс. И в‑третьих, очень быстрый порог (подстраивается в 9–15 раз быстрее, чем в ручном правиле), который это отталкивание удерживает.
правило | MNIST test | Fashion‑MNIST |
ручное | 0,9462 | 0,7959 |
найденное, запуск 1 | 0,9544 (+0,82 ± 0,25 п.п.) | разлетается на 5 сидах из 5 |
найденное, запуск 2 | разлетается на 3 сидах из 5 | не проверял |
На MNIST найденное правило действительно лучше ручного, причем на test, который поиск не видел. А вот с переносом проблемы. На Fashion‑MNIST картинки ярче (средняя норма входа 12,1 против 9,2 у MNIST), и правило без нормировки шага разлетается уже на первой эпохе. Второе найденное правило разлетается даже на полном MNIST: в фитнесе обучение было в 20 раз короче, и неустойчивость просто не успевала проявиться.
Полезная находка есть (тормозное окружение), но фитнес на фиксированных сидах и одном размере сети оптимизирует шум. А устойчивость на полной длине обучения и на других данных надо проверять прямо в фитнесе.
Итоговые цифры
Лучшая локальная модель у меня получилась такая: слой с локальными рецептивными полями плюс второй слой. На MNIST 0,9735, на Fashion‑MNIST 0,8576. Это на 5,6 и 2,5 п.п. выше сырых пикселей и на 0,9 и 3,4 п.п. ниже backprop. Test (стандартные 10 тысяч картинок) я открывал один раз, все усреднено по 5 сидам. На Fashion‑MNIST все варианты запускались впервые, без подстройки.
модель | нейронов | MNIST | Fashion‑MNIST |
локальные поля + второй слой | 400+100 | 0,9735 | 0,8576 |
локальные поля | 400 | 0,9707 | 0,8359 |
плотный слой, найденное правило | 100 | 0,9544 | разлетается |
плотный слой, ручное правило | 100 | 0,9462 | 0,7959 |
k‑means по участкам | 400 | 0,9695 | 0,8316 |
k‑means плотный | 100 | 0,9414 | 0,7956 |
сырые пиксели | 784 | 0,9175 | 0,8323 |
случайные веса | 100 | 0,8190 | 0,7725 |
backprop MLP | 100 | 0,9758 | 0,8831 |
backprop MLP | 400+100 | 0,9829 | 0,8918 |
Разброс по сидам 0,1–0,8 п.п. На MNIST test совпал с validation у всех моделей, так что подбор параметров по validation результаты не завысил.
По таблице видны две вещи. На Fashion‑MNIST плотный слой прототипов хуже сырых пикселей: прототип целой вещи плохо отличает, скажем, рубашку от футболки, там все решают детали. Локальные поля это исправляют. И каждой локальной модели соответствует k‑means с той же архитектурой, и разница между ними всегда меньше полпроцента.
Где локальность могла бы выиграть
Точность линейного классификатора на полной разметке как раз та метрика, на которой кластеризация выглядит хорошо. Поэтому я проверил еще две вещи, ради которых локальное обучение обычно и предлагают. Оба раза мой прогноз не сбылся.
Мало меток
Признаки учатся без меток на всех 50 тысячах картинок, а классификатор и backprop MLP получают только 100 или 1000 размеченных. Я ожидал, что на 100 метках MLP переобучится, а признаки без учителя выиграют. Чтобы сравнение было честным, обеим сторонам дал лучшую регуляризацию из небольшой сетки.
разница c MLP той же ширины | 100 меток | 1000 меток |
MNIST, плотный локальный слой | +0,4 ± 0,8 п.п. | +0,2 ± 0,3 п.п. |
MNIST, локальные поля + второй слой | +0,2 ± 1,5 п.п. | +0,2 ± 0,3 п.п. |
Fashion‑MNIST, плотный локальный слой | −7,3 п.п. | −8,6 п.п. |
Fashion‑MNIST, локальные поля + второй слой | −5,1 п.п. | −3,7 п.п. |
На MNIST вышла ничья, на Fashion‑MNIST MLP заметно впереди. Кстати, с регуляризацией по умолчанию локальный слой на MNIST выигрывал +2 п.п. при 100 метках, но только потому, что MLP был плохо регуляризован. Хороший пример того, как легко получить «преимущество» за счет слабого эталона.
Забывание
Модель сначала учится на MNIST, потом продолжает учиться на Fashion‑MNIST. Забывание я мерил по признакам: насколько падает точность свежего классификатора на MNIST после обучения на одежде.
модель | забывание MNIST | насколько выучен Fashion |
плотный локальный слой (100) | −7,1 п.п. | 0,782 |
локальные поля (400) | −1,7 п.п. | 0,822 |
backprop MLP 100 | −3,1 п.п. | 0,886 |
backprop MLP 400 | −1,0 п.п. | 0,885 |
Локальные модели забывают не меньше backprop и при этом хуже учат новую задачу. Хуже всех плотный слой, и виноват тот самый порог, который спасал от мертвых нейронов. он требует, чтобы каждый нейрон побеждал примерно в 1/N примеров, уже на новых данных. Нейрон, который был «семеркой», на кроссовках почти не побеждает. Его порог падает, он начинает выигрывать, и каждая победа тянет его веса к кроссовку. В итоге под цифры не остается ни одного нейрона.
Забавно, что без порога часть нейронов‑цифр просто не побеждала бы на одежде и сохранилась бы. То есть те самые «мертвые» нейроны, с которыми я боролся в начале, тут работали бы как память. Локальные поля держатся лучше, потому что штрихи и края частично общие у цифр и одежды.
А знаменитое катастрофическое забывание у MLP почти целиком сидит в выходном слое. Старый выходной слой после Fashion дает на MNIST 0,36–0,47, а признаки скрытого слоя сохраняют 0,94–0,97.
Где я сам ошибался
Ранняя остановка в sklearn. Когда дообучаешь MLP с warm_start, sklearn не сбрасывает лучший результат с прошлой задачи. Обучение на Fashion‑MNIST сравнивалось с лучшим результатом на MNIST и останавливалось через 11 эпох. В итоге MLP почти не учил новую задачу и поэтому «почти не забывал». После исправления его забывание выросло в 2,5–3 раза.
«Мертвые нейроны мешают». По средним так и выглядело, но парная разница по сидам оказалась +0,5 ± 0,7 п.п., то есть шум.
Фитнес эволюции при одном N и на трех сидах дал +1,5 п.п. чистой подгонки.
«Преимущество при 100 метках» исчезло, как только MLP получил нормальную регуляризацию.
И про границы выводов, их важно не раздувать. Масштаб маленький: 100–500 нейронов, один‑два слоя, без сверток и пулинга. Датасеты простые, CIFAR я не трогал. Качество признаков мерил только линейным классификатором. И не проверял то, что к точности не сводится: обучение за один проход, энергопотребление, нейроморфное железо.
Известно, что хеббовские сети со свертками, пулингом и мягкой конкуренцией заходят заметно дальше. Например, SoftHebb (Journé и соавторы, 2023) показывает порядка 80% на CIFAR-10. Так что честнее сказать так: в моей постановке простое конкурентное правило сводится к кластеризации. Это не приговор хеббовскому обучению вообще.
Что в итоге
Я проверил четыре возможных преимущества локального обучения: точность, работу при малом числе меток, забывание и перенос найденного правила. Относительно backprop не подтвердилось ни одно, k‑means выигрыш не больше полпроцента.
Что реально работает:
Адаптивный порог убивает мертвые нейроны и коллапс, обучение перестает зависеть от lr. Обратная сторона: быстрое забывание.
Локальные рецептивные поля дали самый большой прирост, и только поверх них есть смысл ставить второй слой.
Тормозное окружение и отталкивание — единственное в самом правиле, что дает что‑то сверх k‑means, и то около полпроцента.
А вот глубина и топографические карты сами по себе точность не поднимают.
Если копать дальше, то уже в другой постановке: тысячи нейронов, свертки с пулингом, мягкая конкуренция, обучение за один проход. И с k‑means в эталонах с самого начала.
Литература
Krotov D., Hopfield J. J. Unsupervised learning by competing hidden units // PNAS. 2019. Vol. 116, no. 16. P. 7723–7731.
Oja E. A simplified neuron model as a principal component analyzer // Journal of Mathematical Biology. 1982. Vol. 15. P. 267–273.
Bienenstock E. L., Cooper L. N., Munro P. W. Theory for the development of neuron selectivity: orientation specificity and binocular interaction in visual cortex // Journal of Neuroscience. 1982. Vol. 2, no. 1. P. 32–48.
DeSieno D. Adding a conscience to competitive learning // IEEE International Conference on Neural Networks. 1988. Vol. 1. P. 117–124.
Földiák P. Forming sparse representations by local anti‑Hebbian learning // Biological Cybernetics. 1990. Vol. 64. P. 165–170.
Diehl P. U., Cook M. Unsupervised learning of digit recognition using spike‑timing‑dependent plasticity // Frontiers in Computational Neuroscience. 2015. Vol. 9. Art. 99.
Kohonen T. Self‑organized formation of topologically correct feature maps // Biological Cybernetics. 1982. Vol. 43. P. 59–69.
Coates A., Lee H., Ng A. Y. An analysis of single‑layer networks in unsupervised feature learning // AISTATS. 2011. PMLR 15. P. 215–223.
Xiao H., Rasul K., Vollgraf R. Fashion‑MNIST: a novel image dataset for benchmarking machine learning algorithms. arXiv:1708.07747. 2017.
Hansen N., Ostermeier A. Completely derandomized self‑adaptation in evolution strategies // Evolutionary Computation. 2001. Vol. 9, no. 2. P. 159–195.
Najarro E., Risi S. Meta‑learning through Hebbian plasticity in random networks // NeurIPS. 2020.
Moraitis T., Toichkin D., Journé A., Chua Y., Guo Q. SoftHebb: Bayesian inference in unsupervised Hebbian soft winner‑take‑all networks // Neuromorphic Computing and Engineering. 2022. Vol. 2, no. 4. 044017.
Journé A., Garcia Rodriguez H., Guo Q., Moraitis T. Hebbian deep learning without feedback // ICLR. 2023.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.