Pull to refresh

Comments 9

Тоже недавно разбирал NoProp. По сути это метод исправления локальной ошибки, подобный Predict Coding, но немного иначе и для каждого слоя (только данного критерия недостаточно). Он не будет работать в ряде случаев, а так же имеет ряд ограничений:

1) Требует ручного задания или подбора локальных loss-функций.
Это прям один из главных недостатков. Для каждого слоя нужно специально определить, какой сигнал считать «целью» для этого слоя, иначе обучение не работает.

Например, для выходного слоя — это просто ground truth, а для скрытого — нужно вручную придумать target (или использовать какой-то эвристический метод, типа autoencoding, reconstruction loss и т.д.)

2) Ограниченная передача информации между слоями.
Из-за отсутствия global backpropagation информация об ошибке из верхних слоёв не распространяется вниз. Поэтому глубокие архитектуры обучаются хуже, если не придумать специальные механизмы согласования между слоями. Это очень хорошо заметно когда мы сделаем 50-100 слоев (для контраста).

3) Нестабильность при использовании finite-difference.
Метод использует оценку градиента по конечным разностям, а это шумно и зависит от выбора шага (epsilon). Особенно проблема будет возникать при обучении, когда сигнале практически нет явного шума и мы работаем уже с абстракциями. Например, LLM.

4) Зависимость от архитектуры.
Некоторые архитектурные решения требуют специальной адаптации, та же структура с skip-соединениями или BatchNorm может нарушать локальную целевую функцию, я уже не говорю про трансформеры и SSM модели.

В целом метод был протестирован на простых задачах (MNIST, CIFAR-10). Но когда мы берем более сложные, где в основе уже не шумный сигнал, а содержит абстракции, то только данного подхода будет недостаточно.

Если хотите пробовать развивать эту тему, то рекомендую подумать о том, чтобы расширить этот подход.

Тест обучение классификатору sentiment140 на 20 блоках, где каждый блок состоит из FFN (для простоты)
Тест обучение классификатору sentiment140 на 20 блоках, где каждый блок состоит из FFN (для простоты)

Я сейчас тестирую подход. где совестил как раз локальную ошибку Predict Coding (это как и метод выше убирает шум, корректируя локальную ошибку) и глобальную ошибку (через глобальную цель). Выше как раз результат такого подхода, где локальная - это как описал, а глобальная это классический метод обратного распространения.

Accuracy of Local Model: 81.2500%
Accuracy of Global Model: 15.6250%

В этом случае на тестах локальный метод выигрывает у классического. Так как классический не учитывает индивидуальные изменения.

Так что смотрите в эту сторону. Так как данный подход позволяет распараллелить вычисления нейронных сетей каждого блока на разных машинах. Например те же LLM и блоков трансформеров, где каждый его блок можно вычислять параллельно на другой машине. Что в теории должно ускорить обучение в N раз (при N блоках), конечно меньше так как затраты на обмен данными тоже съедает время.

Большое спасибо за такой развернутый и вдумчивый комментарий! Очень интересно узнать, что вы тоже разбирались с NoProp и проводите эксперименты в схожем направлении. Ваши наблюдения и критика во многом перекликаются с тем опытом, который мы получили в процессе нашей попытки реализации и отладки.

С чем мы согласны:

  1. Важность Локальной Цели/Потерь: Вы абсолютно правы, указывая на критическую важность правильного определения локальной цели или функции потерь для каждого блока. Хотя в NoProp цель (uy​ или ϵ) вытекает из общей диффузионной схемы, а не задается вручную разной для каждого слоя, наш опыт показал, что выбор этой цели — ключевой момент. Оригинальная цель (предсказание uy​) в нашей реализации оказалась нестабильной и неэффективной, и только переход к предсказанию шума ϵ (как в DDPM/CARD) позволил добиться существенного прогресса. Так что проблема "правильной" локальной цели действительно стоит очень остро.

  2. Ограничения Глубины и Передачи Информации: Согласен, что масштабирование чисто локальных методов на очень глубокие сети (50-100 слоев, как вы предлагаете) и обеспечение эффективной координации между слоями без глобального сигнала обратной связи — это известный и серьезный вызов. Наш эксперимент с T=10 уже потребовал стабилизации, и для гораздо более глубоких архитектур проблемы, вероятно, усугубятся.

  3. Зависимость от Архитектуры и Сложности Задач: Вполне вероятно, что сложные архитектурные элементы (skip-соединения, attention и т.д.) потребуют адаптации локальных целей. И да, текущие тесты (в статье и у нас) проводились на относительно простых задачах. Смогут ли такие подходы эффективно работать с высокоуровневыми абстракциями (как в LLM) без глобальной ошибки — большой и открытый вопрос.

  • Пункт 3 (Нестабильность Finite-Difference): Здесь, кажется, произошло небольшое недопонимание. NoProp, как он описан в статье и реализован нами, не использует оценку градиента по конечным разностям (finite differences). Обучение каждого блока основано на минимизации своей аналитической функции потерь (MSE от предсказания uy​ или ϵ). Градиенты для этой локальной функции вычисляются стандартным методом автоматического дифференцирования (autodiff) в PyTorch. Термин "gradient-free" в контексте NoProp относится к отсутствию необходимости вычислять и распространять градиент сквозь всю нейронную сеть (end-to-end backpropagation), а не к отсутствию градиентов для локального шага оптимизации вообще. Поэтому проблем, связанных с шумностью и выбором шага эпсилон для конечных разностей, здесь быть не должно.

  • Ваше предложение комбинировать локальную и глобальную ошибки — это очень интересное и логичное направление развития, которое активно исследуется. Спасибо, что поделились результатами вашего эксперимента (81% у локальной модели против 15% у глобальной на sentiment140) — это сильный аргумент в пользу потенциала локальных или гибридных методов в некоторых задачах (хотя результат глобальной модели кажется неожиданно низким, возможно, были свои нюансы). Интересно, что наш финальный работающий вариант тоже имел локальную компоненту (MSE по шуму) и "глобальную" (CE Loss, влияющий на эмбеддинги и классификатор), хоть и обновлялись они, возможно, иначе, чем в вашем подходе.

  • Параллелизация: Совершенно согласен, возможность распараллелить обучение блоков по разным устройствам — это огромный потенциальный плюс для таких архитектур, как LLM.

Исходя из нашего опыта и вашего отзыва, интересными направлениями видятся:

  1. Исследование оптимальных локальных целей (шум ϵ vs чистое предсказание uy​ vs предсказание среднего постериорного распределения) и стабильных схем взвешивания потерь для диффузионных/NoProp-подобных моделей.

  2. Разработка механизмов координации между локально обучаемыми блоками для улучшения работы глубоких сетей.

  3. Тестирование на более сложных задачах и архитектурах (включая трансформеры).

  4. Систематическое сравнение чисто локальных, чисто глобальных (backprop) и гибридных подходов на разных задачах.

Еще раз спасибо за ваш комментарий и пищу для размышлений! Всегда интересно обсудить такие темы с людьми, кто глубоко погружался в вопрос.

шестислойная модель неокортекса
шестислойная модель неокортекса

В качестве размышления, вам стоит рассмотреть как работает неокортекс. Он состоит из множества областей шестислойной модели. В некоторых областях какие-то части отсутствуют, например в ассоциативных зонах нет шума и поэтому нет блока "фильтрации и обобщения" (слой 4). По сути все сводиться к цепочке - "Обобщение сигнала на входе и фильтрация" - "выделение признаков из шума - устойчивого сигнала" - "обобщение найденных признаков". Все - это весь неокортекс. К этому добавляется коррекция ошибки для фильтрации шума (слой 6) и коррекция сигнала другими областями (модуляция через слой 1 и напрямую через селективность).

Исследование NoProp и другие подобные без классического метода обратного распространения учитывают только по сути коррекцию локального сигнала на уровне шума (как на схеме выше). Это присуще областям в мозге, которые получают сенсорные сигналы, так как те содержат шумы. Но в дальних областях уже данный механизм редуцирован (так как сигнал очищен от шума уже, и уже идет упор на выделение признаков и связей между ними).

Когда выше я объединил коррекцию локальной и глобальной ошибки, то это было как раз Predict Coding который в обратном порядке (подобно Forward Gradient ) обновляет градиенты внутри блока. И коррекция глобальной ошибки, через передачу блоку глобальной цели, где он корректирует ошибку как классический метод обратного распространения, но только в рамках данного блока. По сути локальная ошибка убирает шум, а глобальная цель (ошибка) корректирует конечный результат через связь. Важным моментом тут является добавление чувствительности блоков, подобно рецепторам дофамина. Поэтому каждый блок подстраивается, на сколько сильно глобальная ошибка влияет на него (это очень важный момент).

Вот что дало введение чувствительности блоков к глобальной цели. Без нее, подход с глобальной целью всегда проигрывает классическому (в качестве примера исследование Blockwise Self-Supervised Learning at Scale на сетке ResNet-50, где нет чувствительности и результат ошибки на 1,1% хуже классического).

По сути локальная ошибка убирает шум, а коррекция глобальной ошибки - позволяет корректировать конечную (глобальную) цель.

Блоки связаны друг с другом. Но связь эта происходит через глобальную цель (глобальный результат). Именно так мозг и обучается. Если проводить параллели - это уровни детализации. Современные LLM делают тоже самое, на одном уровне мелкие признаки, далее более крупные и так далее. В LocalModal как в мозге каждый блок обучается детализировать свой уровень иерархии признаков и их связей. Но каждый блок связан между собой, так как передает сигнал дальше в следующий блок, который работает с признаками более высокого уровня. Чтобы было понятно, приведем абстрактный пример. Допустим мы строители и у нас толпа людей это сигнал.

Первый блок: наш сигнал (толпа людей) формирует кирпичи из глины и передает их во второй блок.

Второй блок: наш сигнал (толпа людей) работает с кирпичами (а не с глиной) и строит из них дома и передает в третий блок.

Третий блок: работает с домами и строит города.

Четвертый блок: работает с городами и строит страну.

Пятый блок: работает со странами с строит регионы (европа, азия, южная американский и так далее).
и так далее.

Вот тоже самое делает каждый блок LLM или каждая область мозга. Сами по себе они преследуют одну и туже цель строительства, но работают каждый на своем уровне детализации и сравнивания с итоговым глобальным результатом.

Собственно выше как раз и была показана его реализации. И вас стоит рассмотреть это направление, если хотите улучшить показатели обучения.

Тест на 1000 блоков.
Тест на 1000 блоков.

Дополню, роль локальной ошибки видна выше. Она позволяет быстро адаптироваться. Всплеск далее был вызван тем, что архитектура специально выла выбрана не удачной: linear-relu-linear. Что из за отсутствия нормализации приводит к всплеску, но все равно данный подход быстро с ним справляется. Если добавляет нормализацию, то этого всплеска не будет.

Тут главное другое, то что LocalModel модель быстрее адаптируется при большом кол-ве элементов.

LocalModel: тут каждый блок стремиться к глобальной цели. Состоящая из коррекции локальной и глобальной ошибке

GlobalModel: классический подходом коррекции ошибки методом обратного распространением.

Это происходит из за учета локальной ошибки, которая убирает шум из сигнала. В классическом методе обратного распространения, это происходит медленнее, так как пока градиенты нормализуют веса первых слоев, пройдет больше времени. Тогда как локальная ошибка, как раз позволяет системе быстро адаптироваться. И дальше уже главную роль играет глобальная ошибка.

Это был просто пример различная генерация данных для каждой операции. Есть два числа и делаем либо +, -, *, / . Числа генерируются случайно. Поэтому система нашла уровень шума этого случайного процесса и вышла на плато.
Это был просто пример различная генерация данных для каждой операции. Есть два числа и делаем либо +, -, *, / . Числа генерируются случайно. Поэтому система нашла уровень шума этого случайного процесса и вышла на плато.

Выше можно как раз наблюдать, как при обучении меняется локальная ошибка. То что она растет и выходит на плато, говорит о том. что система выявила шум и дальше уже стабилизировалась. В данном случае локальную ошибку, это по сути уровень шума который мы должны учитывать. Так как Predict Coding как раз и пытается определить шум в сигнале, чтобы затем оставить только сам сигнал.

Я к тому, что локальная ошибка на уровне шума играет роль до определенного момента, а потом стабилизирует значение и мы работаем уже с очищенным сигналом. где главное это уже признаки, которые и стремятся к конечной цели. А признаки без глобальной ошибки (глобальной цели) определить нельзя. Да, у произвольных наборов это график будет не таким ровным (выше - если мы искусственно добавляем случайный шум). Но все равно он уже будет колебаться в определенном интервале.

Модель состоит из двух блоков с Linear-ReLU-Linear. Каждый из которых имеет свою обучаемую чувствительность к глобальной ошибке.
Модель состоит из двух блоков с Linear-ReLU-Linear. Каждый из которых имеет свою обучаемую чувствительность к глобальной ошибке.

Что касается чувствительности, то тут пример для двух блоков. Можно видеть как во время обучения меняется чувствительность блоков к глобальной ошибке. Видно, что она не одинаковая. Каждый блок на самом деле по разному реагирует на глобальную ошибку (цель). Именно так работают рецепторы дофамина, которые в каждой области адаптируются свою чувствительность к глобальной ошибке (выбросу дофамина).

Надеюсь, что данная информация поможет вам в ваших дальнейших исследованиях.

Хорошо, вот проект ответа на этот очень содержательный и глубокий отзыв. Его можно использовать как комментарий на Habr или как основу для дополнения статьи.

Здравствуйте!

Огромное спасибо за такой невероятно подробный и глубокий комментарий! Ваши размышления об аналогии с неокортексом, разделении ролей локальной и глобальной ошибки, а также описание вашего гибридного подхода и экспериментальные результаты — это чрезвычайно ценный вклад в дискуссию. Очень приятно видеть, что кто-то еще так же глубоко копает в этом направлении!

Во многом ваши тезисы перекликаются с нашим опытом и теми выводами, к которым мы пришли:

  1. Локальная vs Глобальная Ошибка и Неокортекс: Ваша аналогия с обработкой информации в неокортексе — от фильтрации шума в сенсорных областях до выделения абстрактных признаков и связей в ассоциативных — прекрасно иллюстрирует, почему чисто локальных механизмов коррекции ошибки (как в оригинальном NoProp или базовом Predictive Coding) может быть недостаточно. Для построения сложных иерархических представлений действительно необходим механизм, ориентированный на глобальную цель. Наш опыт, когда оригинальный NoProp не заработал, а модифицированный (с добавлением CE Loss, влияющего на эмбеддинги и классификатор) показал результат, это подтверждает.

  2. Роль Локальной Ошибки: Полностью согласен с вашей интерпретацией, что локальная коррекция помогает системе быстро адаптироваться к начальному сигналу, эффективно убирая шум. Это объясняет, почему ваш LocalModel показал такое быстрое падение Loss вначале по сравнению с GlobalModel. Идея о том, что локальная ошибка "выходит на плато", когда базовый шум идентифицирован, и дальше в игру вступает глобальная коррекция для обучения признакам, выглядит очень логично.

  3. Ваш Гибридный Подход и "Чувствительность": Ваш метод, сочетающий PC-подобную локальную коррекцию с BP-подобной глобальной коррекцией внутри блока, — это очень интересно! Особенно интригует идея адаптивной чувствительности каждого блока к глобальной ошибке, по аналогии с дофаминовыми рецепторами. График (image_64595c.jpg), показывающий разную динамику этой чувствительности, впечатляет. Это выглядит как ключевой недостающий элемент во многих локальных или "почти локальных" подходах (включая наш финальный вариант NoProp, где влияние глобальной CE Loss на промежуточные блоки было очень опосредованным). Ваше замечание про сравнение с "Blockwise Self-Supervised Learning", где отсутствие чувствительности ухудшило результат, — сильный аргумент в пользу этого механизма. Спасибо, что поделились!

  4. Иерархия и Связь Блоков: Аналогия со строителями отлично передает идею иерархической обработки и того, что, хотя блоки работают на разных уровнях детализации, они должны быть связаны общей глобальной целью.

Связь с Нашим Результатом:

Наш финальный рабочий вариант (предсказание ϵ + классификатор на u^T​) по сути тоже стал неким упрощенным гибридом. У нас была локальная задача для блоков (минимизация MSE по шуму) и была глобальная задача (минимизация CE Loss), которая влияла на эмбеддинги и финальный классификатор (и, через реконструкцию u^T​, косвенно задавала цель для последнего блока). Однако, в отличие от вашего подхода, у нас не было механизма прямой передачи глобальной ошибки в промежуточные блоки и, тем более, не было адаптивной чувствительности к ней. Возможно, именно поэтому нам потребовалось так много итераций отладки и стабилизации, и точность хоть и достигла 99%, но могла бы быть еще выше или стабильнее с более продвинутым гибридным механизмом вроде вашего.

Будущие Направления:

Ваш комментарий и результаты убедительно показывают, что наиболее перспективное направление для развития идей вроде NoProp — это создание гибридных архитектур, которые эффективно сочетают преимущества локальной коррекции (быстрая адаптация, борьба с шумом, параллелизация) с механизмами глобальной координации (обучение признаков, релевантных для конечной цели). И внедрение адаптивной чувствительности блоков к глобальному сигналу выглядит как очень многообещающая идея для реализации такой координации.

Еще раз огромное спасибо за столь ценную обратную связь, детальное описание вашего подхода и экспериментальные данные! Это очень помогает в дальнейших размышлениях и исследованиях.

И снова огромное спасибо за такой развернутый фидбек, глубокие аналогии с неокортексом и, особенно, за детальное описание вашего гибридного подхода с Predictive Coding, глобальной коррекцией и адаптивной чувствительностью. Это невероятно ценные мысли и направления для размышлений!

Мы во многом согласны с вашим анализом ограничений чисто локальных методов:

  • Важность локальной цели: Полностью поддерживаем ваш тезис о критической важности правильного выбора локальной цели. Наш опыт это ярко продемонстрировал: оригинальная цель NoProp (предсказание uy​) не позволила модели обучиться, и только переход к предсказанию шума ϵ (в духе DDPM/CARD) дал стабильный результат в 99.01%.

  • Масштабирование и координация: Согласны, что обеспечение координации между десятками слоев и обучение сложным абстракциям без глобального сигнала — главные вызовы для таких подходов. Наш эксперимент с T=10 уже требовал внимания к стабильности.

  • Зависимость от архитектуры/задачи: Также согласны, что применимость к сложным архитектурам (Трансформеры) и задачам (LLM) требует дальнейшего изучения.

(Небольшое уточнение по пункту 3 из вашего первого отзыва: в нашей реализации (и как описано в статье NoProp) не использовались конечные разности для градиентов, а применялось стандартное автодифференцирование PyTorch для локальной функции потерь. "Gradient-free" относилось к отсутствию сквозного backprop.)

О гибридном подходе (локальная + глобальная ошибка):

Ваша идея о необходимости комбинировать локальную коррекцию (для шума/адаптации) и глобальную (для признаков/цели) кажется очень сильной и логичной. Ваш эксперимент с sentiment140 (Local 81% vs Global 15%) — интересное тому подтверждение.

Наш эксперимент с гибридизацией: Вдохновившись вашим отзывом, мы попробовали реализовать упрощенный гибридный вариант на основе нашей работающей модели (CNN/MLP блоки, предсказывающие ϵ): мы добавили вторую компоненту потерь к обновлению каждого блока t, а именно MSE(u^t​,uy​), где u^t​ — это чистый эмбеддинг, реконструированный из zt−1​ и предсказанного блоком шума ϵ^. Идея была в том, чтобы явно "тянуть" промежуточные предсказания к финальной цели uy​.

Результат: К сожалению, этот простой гибрид не сработал. Обучение стало нестабильным, и точность на тесте катастрофически упала (до ~2-10%) по сравнению с версией, где блоки обучались только на предсказание шума ϵ. Похоже, что эти две цели (точно предсказать ϵ и одновременно сделать реконструированный u^t​ похожим на uy​) создали конфликтующие градиенты для параметров блока.

Важность "Чувствительности": Этот негативный результат нашего эксперимента с простым гибридом еще сильнее подчеркивает важность предложенного вами механизма адаптивной чувствительности (λt​). Возможно, именно он позволяет правильно сбалансировать и применить градиенты от локальной и глобальной ошибки, избегая конфликтов и нестабильности. Ваше сравнение с Blockwise Self-Supervised Learning, где отсутствие чувствительности ухудшило результат, — очень релевантно. Похоже, это действительно может быть ключевым элементом для успешных гибридных архитектур.

Выводы и Будущее:

Ваш отзыв и наши эксперименты указывают, что:

  1. Чисто локальный подход NoProp (предсказание uy​) нестабилен.

  2. Модифицированный локальный подход (предсказание ϵ) может работать очень хорошо (99% на MNIST) при правильной настройке и стабилизации.

  3. Простая попытка добавить глобальную цель (MSE(u^t​,uy​)) к локальному обучению блока привела к ухудшению.

  4. Более сложные гибридные методы, как ваш (сочетающий разные типы локальной/глобальной коррекции внутри блока и, возможно, адаптивной чувствительностью), выглядят как наиболее перспективное направление для дальнейших исследований методов обучения без сквозного backpropagation, которые могли бы сочетать локальную эффективность и глобальную координацию.

Огромное спасибо вам за то, что поделились своим опытом, идеями и результатами — это было невероятно полезно и дало много пищи для размышлений!

Я планировал написать посты и статью о том, как добавить чувствительность в глобальную ошибку. Поделюсь тогда пока так.

В нем ниже нет локальной ошибки. Глобальная ошибка связана с блоками тоже упрощенно - это одна из первых версий. Тут так же нет рецепторов, архитектура блока должна иметь одинаковый размер тензора для глобальной цели и блока (это решается введением двух выходов, один для обучения и один для связи между блоками).

Но сама идея думаю будет понятна.

тут в forward есть такое место

self.last_output = x.

Каждый следующий блок разрывает связь с прежним в начале, поэтому граф будет разорван по всем блокам. У каждого блока - свой граф. Но последний будет связан со слоем classifier верхней модели. Если хотите, что и его связь была разорвана то делайте

self.last_output = x.detach().clone().requires_grad_()

но результат будет хуже, модель дольше будет сходиться, так как глобальная связь хуже работает на уровне одного слоя (если оставляем одинокий classifier как отдельный блок), а не блока. Вы можете запихнуть в блок хоть трансформер вместо обычных слоев (делал на отдельных тестах).

Надеюсь этот код будет вам отправной точкой. Дальше смотрите в сторону дополнительных выходов, чувствительность на уровне матрицы а не параметра, связь локальной и глобальной потребует самостоятельного обновления градиентов (там вообще уже на Loss не стоит полагаться так как пока не ясно как правильно интерпретировать это смешенное значение и следует смотреть на тестирование обучение, а не Loss).

import random
import torch
import torch.nn as nn
import matplotlib.pyplot as plt

# --- Архитектура блока ---
class LocalBlock(nn.Module):
    def __init__(self, input_dim, hidden_dim):
        super().__init__()
        self.linear1 = nn.Linear(input_dim, hidden_dim)
        self.relu = nn.ReLU()
        self.linear2 = nn.Linear(hidden_dim, input_dim)
        self.sensitivity = nn.Parameter(torch.ones(1))
        self.last_input = None
        self.last_output = None

    def forward(self, x):
        self.last_input = x.detach().clone().requires_grad_()
        x = self.linear1(self.last_input)
        x = self.relu(x)
        self.last_output = self.linear2(x)
        return self.last_output

    def local_backward(self, loss_fn, target):
        loss = loss_fn(self.last_output, target) * self.sensitivity
        loss.backward(retain_graph=True)

        return loss

# --- Модель с локальным обучением ---
class LocalModel(nn.Module):
    def __init__(self, input_dim, hidden_dim, num_blocks=20):
        super().__init__()
        self.blocks = nn.ModuleList([LocalBlock(input_dim, hidden_dim) for _ in range(num_blocks)])
        self.classifier = nn.Linear(input_dim, 1)

    def forward(self, x):
        for block in self.blocks:
            x = block(x)
        return self.classifier(x)

# --- Модель с глобальным обучением ---
class GlobalModel(nn.Module):
    def __init__(self, input_dim, hidden_dim, num_blocks=20):
        super().__init__()
        layers = []
        # Каждые три слоя: Linear(input_dim, hidden_dim) -> ReLU -> Linear(hidden_dim, input_dim)
        for _ in range(num_blocks):
            layers.append(nn.Linear(input_dim, hidden_dim))
            layers.append(nn.ReLU())
            layers.append(nn.Linear(hidden_dim, input_dim))
        self.blocks = nn.Sequential(*layers)
        self.classifier = nn.Linear(input_dim, 1)

    def forward(self, x):
        x = self.blocks(x)
        return self.classifier(x)

# Генерация данных с операциями
def generate_classification_data(batch_size, input_dim=16):
    X = torch.randn(batch_size, input_dim)  # Признаки (входные данные)
    y = torch.zeros(batch_size, 1)  # Метки для классификации

    for i in range(batch_size):
        # Выбираем случайную операцию
        operation = random.choice(['+', '-', '*', '/'])

        # Генерируем числа для операции
        num1 = random.uniform(1, 10)
        num2 = random.uniform(1, 10)

        # Различная генерация данных для каждой операции
        if operation == '+':
            X[i] = torch.tensor([num1 + num2] + [random.uniform(-0.5, 0.5) for _ in range(input_dim - 1)])
            y[i] = torch.tensor([0])  # Операция сложения
        elif operation == '-':
            X[i] = torch.tensor([num1 - num2] + [random.uniform(-0.5, 0.5) for _ in range(input_dim - 1)])
            y[i] = torch.tensor([1])  # Операция вычитания
        elif operation == '*':
            X[i] = torch.tensor([num1 * num2] + [random.uniform(-0.5, 0.5) for _ in range(input_dim - 1)])
            y[i] = torch.tensor([2])  # Операция умножения
        elif operation == '/':
            X[i] = torch.tensor([num1 / num2] + [random.uniform(-0.5, 0.5) for _ in range(input_dim - 1)])
            y[i] = torch.tensor([3])  # Операция деления

    return X, y

# --- Обучение локальной модели ---
def train_local_model(model, optimizer, criterion, x, y):
    optimizer.zero_grad()
    out = model(x)
    loss = criterion(out, y)

    for block in model.blocks[::-1]:
        # Обратный порядок для backward (от последнего блока к первому)
        block.local_backward(criterion, y)

    loss.backward()
    optimizer.step()
    return loss.item()

# --- Обучение глобальной модели ---
def train_global_model(model, optimizer, criterion, x, y):
    optimizer.zero_grad()
    out = model(x)
    loss = criterion(out, y)
    loss.backward()
    optimizer.step()
    return loss.item()

# --- Настройки ---
input_dim = 16
hidden_dim = 32
epochs = 1000
lr = 1e-3
batch_size = 32

# --- Инициализация моделей ---
torch.manual_seed(42)
local_model = LocalModel(input_dim, hidden_dim)
global_model = GlobalModel(input_dim, hidden_dim)

# --- Оптимизаторы ---
optimizer_local = torch.optim.Adam(local_model.parameters(), lr=lr)
optimizer_global = torch.optim.Adam(global_model.parameters(), lr=lr)

# --- Критерий ---
criterion = nn.MSELoss()

# --- Хранение результатов ---
losses_local = []
losses_global = []

# --- Обучение ---
for epoch in range(epochs):
    x, y = generate_classification_data(batch_size, input_dim)
    loss_l = train_local_model(local_model, optimizer_local, criterion, x.clone(), y.clone())
    loss_g = train_global_model(global_model, optimizer_global, criterion, x.clone(), y.clone())

    losses_local.append(loss_l)
    losses_global.append(loss_g)

# --- Визуализация ---
plt.figure(figsize=(10, 5))
plt.plot(losses_local, label='Локальное обучение (по блокам)')
plt.plot(losses_global, label='Глобальное обучение')
plt.title('Сравнение глобального Loss при разных подходах')
plt.xlabel('Эпоха')
plt.ylabel('Loss (MSE)')
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.show()

losses_sensitivity = []
# Значения обучаемых параметров чувствительности:
for block in local_model.blocks[::-1]:
    losses_sensitivity.append(block.sensitivity.item())
    print("Block sensitivity (LocalModel):", block.sensitivity.item())

# --- Чувствительность ---
plt.figure(figsize=(10, 5))
plt.plot(losses_sensitivity, label='LocalModel')
plt.title('Сравнение чувствительности по блокам')
plt.xlabel('Блок')
plt.ylabel('Чувствительность')
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.show()

На самом деле. когда понимаешь что надо сделать. для решения понадобиться несколько минут. Поэтому рекомендую подробнее изучить работу базальных ганглий в мозге, дофамин, дофаминовые рецепторы, шестислойную модель неокортекса и связь дофамина с этой моделью. Тогда картина будет более понятной. Выше код - который можно сравнить с аналогией:

  • базальные ганглии определяют конечную цель - это наш criterion, который решает сколько дофамина надо выделить. Решает соответствие глобальной цели, и корректирует глобальную ошибку. Базальные ганглии помогают понять на сколько сигнал "непредсказуем" (высокий Loss), тем больше дофамина будет выработано (больше коррекция глобальной ошибки у градиентов через Loss)

  • затем дофамин (он является аналогией индикатора глобальной ошибки) поступает в каждую область шестислойной модели (блоки архитектуре)

  • каждая область по разному реагирует на дофамин (на глобальную ошибку), поэтому рецепторы по разному настроены на уровень влияния дофамина в каждой области (вводим чувствительность у блоков к глобальной ошибке)

Разница тут в том, что в отличии от классического метода обратного распространения ошибки (который слабо связан с реальной работой мозга), мы биологически корректно корректируем ошибку в каждом блоке (области мозга).

Огромное спасибо за такой развернутый и вдумчивый комментарий! Ваши размышления об аналогии с неокортексом, разделении ролей локальной и глобальной ошибки, а также описание вашего гибридного подхода и экспериментальные результаты — это чрезвычайно ценный вклад в дискуссию.

О гибридном подходе (локальная + глобальная ошибка):

Ваша идея о необходимости комбинировать локальные и глобальные сигналы очень сильна. Ваш эксперимент с sentiment140 — интересное тому подтверждение.

Наша реализация: Наш финальный рабочий вариант (предсказание ϵ + гибридная потеря для блока + классификация по u^T​) по сути тоже стал гибридом, но более простым, чем ваш. Влияние глобальной ошибки на промежуточные блоки у нас было опосредованным (через MSE(u^t​,uy​) и общий градиент от classify_loss, влияющий на эмбеддинги и классификатор). Эту реализацию (ту, что достигла 99.4%) можно найти здесь: https://github.com/vpuhoff/noprop-dt-mnist-pytorch/tree/feature/hybrid.

Ваш подход: Ваш пример кода, пусть и упрощенный, демонстрирует интересный механизм прямой передачи глобальной цели y для расчета ошибки внутри каждого блока (local_backward). Но особенно нас заинтересовало ваше текстовое описание полноценного гибрида с идеей адаптивной чувствительности λt​ (аналогия с дофамином). Это выглядит как ключевой недостающий элемент во многих локальных или гибридных подходах (включая наш), который может быть необходим для стабильности и эффективности по-настоящему глубоких сетей.

Дальнейшие шаги: Ваш пример кода и особенно текстовое описание полноценного гибрида с адаптивной чувствительностью λt​ очень нас вдохновили. Мы определенно попробуем поэкспериментировать с добавлением этого механизма (и, возможно, вашей схемы локального/глобального обновления внутри блока с использованием local_backward и финальной цели y) в нашу реализацию, чтобы проверить, удастся ли еще улучшить результат и стабильность, приблизившись к биологически правдоподобным механизмам, которые вы описали.

Еще раз огромное спасибо за то, что поделились кодом, графиками, своим видением и такими ценными идеями! Это очень помогает и мотивирует для дальнейших исследований.

Я выложил полностью новый механизм обучения.

Вы можете посмотреть его в папке backward

https://t.me/greenruff/2257

Он применим к любой архитектуре. Там есть все, от примера выше до реализации обучения по блокам глобальной ошибке с учётом обучения локальной ошибке

Sign up to leave a comment.

Articles