Показаны сообщения с ярлыком нейронные сети. Показать все сообщения
Показаны сообщения с ярлыком нейронные сети. Показать все сообщения

среда, 15 июля 2026 г.

Обратное распространение ошибки и градиентный спуск

 Продолжим изучение темы нейронных сетей. На прошлом уроке мы рассмотрели простейший двухслойный персептрон с двумя входами и двумя выходами состоящий из весовых коэффициентов и не имеющий коэффициентов смещения, с сигмоидой в качестве функции активации для всех нейронов. В том уроке мы рассмотрели прямой проход т.е. алгоритм использования нейронной сети но он также имеется в составе алгоритма её обучения. Обучать нейронную сеть можно разными путями. Например можно случайным образом немного менять весовые коэффициенты, смотреть что получаем а выходе, сохранять наиболее лучшие результаты а также комбирировать между собой и так повторять до тех пор пока результат не станет удовлетворительным. Это называется "генетический" алгоритм. Очень интересный способ, однако он практически не применяется т.к. требует большого объема памяти и очень медленно работает. Помимо "генетического" алгоритма существует также алгоритм обучения на основе градиентного спуска. У этого алгоритма есть множество современных эффективных разновидностей. Но мы, для начала для простоты, рассмотрим классический градиентный спуск. Идея заключается в том чтобы во время обратного прохода, от выходов ко входам, вычислять ошибки создаваемые нейронами и корректировать веса таким образом чтобы немного минимизировать эти ошибки с каким то небольшим шагом за один проход путем изменения весовых коэффициентов в сторону противоположную градиентам ошибок.

Проще всего будет показать на примере как это делается. Возьмем нашу двухслойный персептрон с двумя входами и двумя выходами. Допустим мы уже сделали прямой проход и получили результат на выходе. Теперь у нас есть числа которые мы хотим иметь на выходе.  Чтобы сделать до конца первую итерацию обучения персептрона, нам нужно выполнить обратный проход. На первом этапе мы вычислим ошибки каждого нейрона. Начиная с выходного слоя и заканчивая входным. Чтобы вычислить ошибку нейрона выходного слоя нам нужно из полученного на выходе значения этого нейрона вычесть требуемое значение которое мы хотим чтобы было на выходе этого нейрона. Результат этого вычитания умножить на производную функции активации (в данном случае сигмоида) в точке взвешенной суммы. Т.е. производная сигмоиды просто выражается через саму сигмоиду а выходы нам известны то можно подставить выход в формулу производной сигмоиды и умножить это на разницу т.е. отклонение от выхода.  Далее проделать так для всех остальных нейронов выходного слоя. Теперь нужно рассчитать ошибки нейронов слоев предшествующих выходному. Сделать это уже сложнее но к счастью метод всё таки есть. На данном этапе ошибка как бы распространяется назад. Поэтому метод и называется обратным распространением ошибки. Ошибка распространяется назад и перемножается с весовыми коэффициентами а взвешенная сумма умножается на производную функции активации в точке взвешенной суммы. Таким образом. Можно рассчитать ошибки нейронов скрытых слоев. Возможно принцип работы данного метода, на первый взгляд, не очевиден но главное что он работает если просто примнеять формулы.  После того как ошибки найдены можно приступать к корректировке весов. Чтобы скорректировать вес методом градиентного спуска, из этого веса нужно вычесть произведение этого веса на градиент умноженный на шаг обучения который обычно является небольшим числом например 0.01 или что то вроде того. Выбор шага это большая отдельная тема, пока остановимся на том что это просто небольшое число. Градиент рассчитывается как произведение входа нейрона на ошибку. Можно было бы выделить расчет градиентов в отдельный этап но для реализации на языке программирования лучше объединить этап расчета градиентов с этапом корректировки весов чтобы не занимать лишнюю память т.к. на весовые коэффициенты её и так много приходиться + другие модификации градиентного спуска требуют ещё больше память поэтому очень важно стараться её экономить. Итак мы скорректировали веса. Теперь мы переходим в начало цикла обучения, выполняем прямой поход. Получаем новые значения выходов, можем оценить ошибку на данном этапе. После повторяем обратный проход и так до тех пор пока сеть не обучиться или не застрянет если например её структура такова что сеть не может быть обучена до требуемого уровня теми методами с теми данными и стартовыми условиями которые мы имеем.

Наглядно весь процесс обучения можно увидеть на картинке:

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

Или же можно поиграться с этой нейросетью непосредственно на данной странице

обучающая выборка
x0=,x1=,
out0=,out1=
шаг обучения h =

можно заметить что каждые 50 шагов приближают результат всё ближе и ближе к истине и т.о. нейронная сеть обучается всё точнее и точнее. Чем ближе к результату тем медленнее обучается сеть но это просто особенность класического градиентного спуска. Чтобы ускорить обучение можно использовать другие оптимизаторы например моментум который также помогает преодолевать локальные минимум. Также в данной простой сети нет коэффициентов смешения для того чтобы можно было принимать больше вариантов входных данных, например данные с нулями. И обучающая выборка состоит всего из двух векторов, входного и выходного, но на практике их обычно гораздо больше. Ещё можно пробовать менять функции активации. Например использовать Relu на скрытых слоях что гораздо лучше для глубокого обучения или гиперболический тангенс чтобы использовать область отрицательных значений. В общем сеть ещё можно улучшать но это уже отдельные темы.

На Ютубе можно посмотреть можно на видео данного урока:

Номер биткоин кошелька для поддержки блога - bc1qlhrmmkh77x2lzhqe4lt9qwkglswj64tsqt2l5g



воскресенье, 8 марта 2026 г.

Немного о нейронных сетях

 Предлагаю немного погрузиться в тему нейронных сетей. т.к. теперь это весьма популярная тема и на то есть причины. Одна из причин, вероятно, заключается в том что они обучаются на подобии того как это происходит с мозгом живых существ а не программируются обычным для электронных устройств способом. Это значительно упрощает процесс создания какого либо полезного цифрового продукта. Ну например, для того чтобы создать программу переводчик обычным способом, придется учитывать все правила языков между которыми происходит перевод и составлять длинные словари всех возможных слов, словосочетаний и всё подобное. Для создания переводчика на основе нейронной сети, понадобятся обучающие выборки и процесс тренировки сети который будет происходить автоматически. Обычно под нейронными сетями понимаются их эмуляторы т.е. такие программы которые используют не нейросетевую архитектуру какого либо устройства для выполнения алгоритмов которые функционируют по принципам нейронных сетей. Например нейросети могут эмулироватся на обычном процессоре. И это не очень эффективно т.к. требует много времени. Также немаловажным является колличество памяти, как постоянной так и оперативной, поскольку работа с нейронными сетями это работа с огромными массивами данных. Долгое время это ограничивало возможности искусственных нейронных сетей. Но когда для их эмуляции стали использовать графические, тензорные или специальные процессоры то это увеличило их возможности.Также сами алгоритмы нейронных улучшались. И теперь они, можно сказать, из разряда игрушек перешли в разряд серьезных вещей которые сильно влияют на всю развитую часть человечества. В частности сильно влияют на рынок труда но вероятно что это далеко не предел. Основой большинства нейронных сетей является многослойный персептрон. Обычный однослойный персептрон изначально был придуман как модель для изучения естественных нейронных сетей из которых состоят мозги живых существ. Один слой состоит из некоторого количества нейронов которые работают параллельно и расположены как бы в одном слое. На вход этого слоя приходят какие то сигналы. Слой их обрабатывает и выдает какие то сигналы на выход. Обычно естественные нейронные сети состоят из более чем одного слоя. Т.е. сначала сигналы со входов обрабатываются одним слоем который выдает какие то сигналы на выход. После, эти выходные сигналы идут на вход следующего слоя который также обрабатывает сигналы  и пойдёт их на выход следующего слоя или просто на выход. Каждый нейрон, если он натуральный, то это живая клетка которая устроена очень сложно но в данном случае это не важно. У нейрона имеются отростки. Отростки бывают двух типов. Это аксоны т.е. выходы. У одного нейрона обычно один аксон. И дендриты т.е. входы. Дендритов у нейрона может быть много.

Место соединения акстона и дендрита или ещё входов с дендритами, называется синапсом или по простому связью.

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

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

Входные и выходные совокупности сигналов для настройки сети называются обучающими выборками. Их чаще всего больше одной. И чем больше обучающих выборок тем точнее можно настроить нейронную сеть для своих задач. Процесс расчета выходных сигналов по входным, называется "прямое распространение" или forward propagation. Этот процесс нужен для использования нейронной сети. И он относительно прост. Задача же обучения нейронной сети гораздо сложнее и не описывается простыми формулами. Существуют разные методы обучения с разными вариациями. Но пока что на них останавливаться не будем. Т.к. это большая отдельная тема. Предположим что у нас имеется двухслойный персептрон с четырьмя нейронами, двумя входами и двумя выходами.

Попробуем реализовать код прямого распространения. На каком нибудь языке программирования. Обычно подобные вещи делают на питоне со специальными библиотеками для матричных операций. Так получается всё очень просто и быстро. Но о том как реализовать этот алгоритм на чем нибудь си подобном, информации не так много. Поэтому попробуем что нибудь си подобное. Наиболее простой вариант это на мой взгляд javascript. При желании код можно адаптировать под си. Что может быть полезно для применения в микроконтроллерах. 

Данный код реализует прямое распространение двухслойного персептрона с двумя нейронами в каждом слое и возможностью увеличить это количество, т.е. масштабирования путем изменения соответствующих переменных. Весовые коэффициенты каждого синапса инициализируются случайными значениями в диапазоне от -1 до +1.  На вход сети подается массив входных значений а на выходе получается массив выходных. Для того чтобы запустить программу её нужно сохранить в формате html и открыть каким нибудь браузером, например google chrome. После загрузки произойдет прямой проход и результаты появятся во всплывающем окне а также их можно посмотреть в специальном поле если нажать F12. Есть конечно ограничения. Нельзя сделать слои с разными колличествами нейронов. Т.е. этим ограничена архитектура но зато такая архитектура очень красиво и удобно помещается в трехмерный массив и обрабатывается вложенными циклами что мне очень нравиться 😄

Посмотреть и послушать про нейронные сети также можно на видео:

Следующий урок 2 (обучение сети, обратное распространение, градиентный спуск) 

Адрес биткоин кошелька для поддержки блога - bc1qlhrmmkh77x2lzhqe4lt9qwkglswj64tsqt2l5g