Обучение сети - обратное распространение. Обучение нейронной сети. Алгоритм обратного распространения ошибок

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

В 6 части перевода выкладываю сразу 4 раздела книги. Все они посвящены одной из самых важных тем в области нейросетей - методу обратного распространения ошибки. Вы научитесь рассчитывать погрешность всех нейронов нейросети основываясь только на итоговой погрешности сети и весах связей.

Материал сложный, так что смело задавайте свои вопросы на форуме.

Вы можете перевода.

Приятного чтения!

1 Глава. Как они работают.

1.10 Калибровка весов нескольких связей

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

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

Но как нам калибровать веса связей, когда на получаемый результат, а значит и на погрешность, влияют сразу несколько нейронов? Рисунок ниже демонстрирует проблему:

Очень легко работать с погрешностью, когда вход у нейрона всего один. Но сейчас уже два нейрона подают сигналы на два входа рассматриваемого нейрона. Что же делать с погрешностью?

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

Конечно, существует очень маленькая вероятность того, что только один вес внес погрешность, а второй был идеально откалиброван. Но даже если мы немного поменяем вес, который и так не вносит погрешность, то в процессе дальнейшего обучения сети он все равно придет в норму, так что ничего страшного.

Можно попытаться разделить погрешность одинаково на все нейроны:

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

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

На рисунке изображены два нейрона, которые подают сигналы третьему, выходному нейрону. Веса связей: ​\(3 \) ​ и ​\(1 \) ​. Согласно нашей идее о переносе погрешности на нейроны мы используем ​\(\frac{3}{4} \) ​ погрешности на корректировку первого (большего) веса и ​\(\frac{1}{4} \) ​ на корректировку второго (меньшего) веса.

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

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

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

1.11 Обратное распространение ошибки от выходных нейронов

На диаграмме ниже изображена нейросеть с двумя нейронами во входном слое, как и в предыдущем примере. Но теперь у сети имеется два нейрона и в выходном слое.

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

То, что сейчас у нас больше одного выходного нейрона по сути ни на что не влияет. Мы просто используем наш метод дважды: для первого и второго нейронов. Почему так просто? Потому что связи с конкретным выходным нейроном никак не влияют на остальные выходные нейроны. Их изменение повлияет только на конкретный выходной нейрон. На диаграмме выше изменение весов ​\(w_{1,2} \) ​ и ​\(w_{2,2} \) ​ не повлияет на результат ​\(o_1 \) ​.

Погрешность первого нейрона выходного слоя мы обозначили за ​\(e_1 \) ​. Погрешность равна разнице между желаемым выходом нейрона ​\(t_1 \) ​, который мы имеем в обучающей выборке и полученным реальным результатом ​\(o_1 \) ​.

\[ e_1 = t_1 — o_1 \]

Погрешность второго нейрона выходного слоя равна ​\(e_2 \) ​.

На диаграмме выше погрешность ​\(e_1 \) ​ разделяется на веса ​\(w_{1,1} \) ​ и ​\(w_{2,1} \) ​ соответственно их вкладу в эту погрешность. Аналогично, погрешность ​\(e_2 \) ​ разделяется на веса ​\(w_{1,2} \) ​ и ​\(w_{2,2} \) ​.

Теперь надо определить, какой вес оказал большее влияние на выход нейрона. Например, мы можем определить, какая часть ошибки ​\(e_1 \) ​ пойдет на исправление веса ​\(w_{1,1} \) ​:

А вот так находится часть ​\(e_1 \) ​, которая пойдет на корректировку веса ​\(w_{2,1} \) ​:

\[ \frac{w_{2,1}}{w_{1,1} + w_{2,1}} \]

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

А как нам понять величину веса относительно всех остальных весов? Для этого мы должны сравнить какой-то конкретный вес (например ​\(w_{1,1} \) ​) с абстрактной «общей» суммой всех весов, повлиявших на выход нейрона. На выход нейрона повлияли два веса: ​\(w_{1,1} \) ​ и ​\(w_{2,1} \) ​. Мы складываем их и смотрим, какая часть от общего вклада приходится на ​\(w_{1,1} \) ​ с помощью деления этого веса на полученную ранее общую сумму:

\[ \frac{w_{1,1}}{w_{1,1}+w_{2,1}} \]

Пусть ​\(w_{1,1} \) ​ в два раза больше, чем ​\(w_{2,1} \) ​: ​\(w_{1,1} = 6 \) ​ и ​\(w_{2,1} = 3 \) ​. Тогда имеем ​\(6/(6+3) = 6/9 = 2/3 \) ​, а значит ​\(2/3 \) ​ погрешности ​\(e_1 \) ​ пойдет на корректировку ​\(w_{1,1} \) ​, а ​\(1/3 \) ​ на корректировку ​\(w_{2,1} \) ​.

В случае, когда оба веса равны, то каждому достанется по половине погрешности. Пусть ​\(w_{1,1} = 4 \) ​ и ​\(w_{2,1}=4 \) ​. Тогда имеем ​\(4/(4+4)=4/8=1/2 \) ​, а значит на каждый вес пойдет ​\(1/2 \) ​ погрешности ​\(e_1 \) ​.

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

Но есть еще одна проблема. Сейчас мы знаем, что делать с весами связей слое, который находится прямо перед выходным слоем сети. А что если наша нейросеть имеет больше 2 слоев? Что делать с весами связей в слоях, которые находятся за предпоследним слоем сети?

1.12 Обратное распространение ошибки на множество слоев

На диаграмме ниже изображена простая трехслойная нейросеть с входным, скрытым и выходным слоями.

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

Для простоты обозначения были обобщены. Погрешности нейронов выходного слоя мы в целом назвали ​\(e_{\text{out}} \) ​, а все веса связей между скрытым и выходным слоем обозначили за ​\(w_{\text{ho}} \) ​.

Еще раз повторю, что для корректировки весов ​\(w_{\text{ho}} \) ​ мы распределяем погрешность нейрона выходного слоя по всем весам в зависимости от их вклада в выход нейрона.

Как видно из диаграммы ниже, для корректировки весов связей между входным и скрытым слоем нам надо повторить ту же операцию еще раз. Мы берем погрешности нейронов скрытого слоя ​\(e_{\text{hi}} \) ​ и распределяем их по весам связей между входным и скрытым слоем ​\(w_{\text{ih}} \) ​:

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

Для корректировки связей между предпоследним и выходным слоем мы использовали погрешность выходов сети ​\(e_{\text{out}} \) ​. А чему же равна погрешность выходов нейронов скрытых слоев ​\(e_{\text{hi}} \) ​? Это отличный вопрос потому что сходу на этот вопрос ответить трудно. Когда сигнал распространяется по сети от входного к выходному слою мы точно знаем значения выходных нейронов скрытых слоев. Мы получали эти значения с помощью функции активации, у которой в качестве аргумента использовалась сумма взвешенных сигналов, поступивших на вход нейрона. Но как из выходного значения нейрона скрытого слоя получить его погрешность?

У нас нет никаких ожидаемых или заранее подготовленных правильных ответов для выходов нейронов скрытого слоя. У нас есть готовые правильные ответы только для выходов нейронов выходного слоя. Эти выходы мы сравниваем с заранее правильными ответами из обучающей выборки и получаем погрешность. Давайте вновь проанализируем диаграмму выше.

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

Уже из рисунка можно понять, что делать дальше. Но давайте все-таки еще раз пройдемся по всему алгоритму. Нам нужно получить погрешность выхода нейрона скрытого слоя для того, чтобы скорректировать веса связей между текущим и предыдущим слоями. Назовем эту погрешность ​\(e_{\text{hi}} \) ​. Но мы не можем получить значение погрешности напрямую. Погрешность равна разности между ожидаемым и полученным значениями, но проблема заключается в том, что у нас есть ожидаемые значения только для нейронов выходного слоя нейросети.

В невозможности прямого нахождения погрешности нейронов скрытого слоя и заключается основная сложность.

Но выход есть. Мы умеем распределять погрешность нейронов выходного слоя по весам связей. Значит на каждый вес связи идет часть погрешности. Поэтому мы складываем части погрешностей, которые относятся к весам связей, исходящих из данного скрытого нейрона. Полученная сумма и будем считать за погрешность выхода данного нейрона. На диаграмме выше часть погрешности ​\(e_{\text{out 1}} \) ​ идет на вес ​\(w_{1,1} \) ​, а часть погрешности ​\(e_{\text{out 2}} \) ​ идет на вес ​\(w_{1,2} \) ​. Оба этих веса относятся к связям, исходящим из первого нейрона скрытого слоя. А значит мы можем найти его погрешность:

\[ e_{\text{hi 1}} = \text{сумма частей погрешностей для весов } w_{1,1} \text{ и } w_{1,2} \]

\[ e_{\text{hi 1}} = \left(e_{\text{out 1}}\cdot\frac{w_{1,1}}{w_{1,1} + w_{2,1}}\right) + \left(e_{\text{out 2}}\cdot\frac{w_{1,2}}{w_{1,2} + w_{2,2}}\right) \]

Рассмотрим алгоритм на реальной трехслойной нейросети с двумя нейронами в каждом слое:

Давайте отследим обратное распространение одной ошибки/погрешности. Погрешность второго выходного нейрона равна ​\(0.5 \) ​ и она распределяется на два веса. На вес ​\(w_{12} \) ​ идет погрешность ​\(0.1 \) ​, а на вес ​\(w_{22} \) ​ идет погрешность ​\(0.4 \) ​. Дальше у нас идет второй нейрон скрытого слоя. От него отходят две связи с весами ​\(w_{21} \) ​ и ​\(w_{22} \) ​. На эти веса связей также распределяется погрешность как от ​\(e_1 \) ​, так и от ​\(e_2 \) ​. На вес ​\(w_{21} \) ​ идет погрешность ​\(0.9 \) ​, а на вес ​\(w_{22} \) ​ идет погрешность ​\(0.4 \) ​. Сумма этих погрешностей и дает нам погрешность выхода второго нейрона скрытого слоя: ​\(0.4 + 0.9 = 1.3 \) ​.

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

Ключевые моменты

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

1.13 Обратное распространение ошибки и произведение матриц

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

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

Начинаем мы с самого конца нейросети - с матрицы погрешностей ее выходов. В примере выше у нас имеется две погрешности сети: ​\(e_1 \) ​ и ​\(e_2 \) ​.

\[ \mathbf{E}_{\text{out}} = \left(\begin{matrix}e_1 \\ e_2\end{matrix}\right) \]

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

Сначала рассматриваем первый нейрон скрытого слоя. Как было показано в предыдущем разделе, погрешность этого нейрона высчитывается так:

\[ e_{\text{hi 1}} = \left(e_1\cdot\frac{w_{11}}{w_{11} + w_{21}}\right) + \left(e_2\cdot\frac{w_{12}}{w_{12} + w_{22}}\right) \]

Погрешность второго нейрона скрытого слоя высчитывается так:

\[ e_{\text{hi 2}} = \left(e_1\cdot\frac{w_{21}}{w_{11} + w_{21}}\right) + \left(e_2\cdot\frac{w_{22}}{w_{12} + w_{22}}\right) \]

Получаем матрицу погрешностей скрытого слоя:

\[ \mathbf{E}_{\text{hid}} = \left(\begin{matrix}e_{\text{hid 1}} \\ e_{\text{hid 2}}\end{matrix}\right) = \left(\begin{matrix} e_1\cdot\dfrac{w_{11}}{w_{11} + w_{21}} \hspace{5pt} + \hspace{5pt} e_2\cdot\dfrac{w_{12}}{w_{12} + w_{22}} \\ e_1\cdot\dfrac{w_{21}}{w_{11} + w_{21}} \hspace{5pt} + \hspace{5pt} e_2\cdot\dfrac{w_{22}}{w_{12} + w_{22}} \end{matrix}\right) \]

Многие из вас уже заметили произведение матриц:

\[ \mathbf{E}_{\text{hid}} = \left(\begin{matrix} \dfrac{w_{11}}{w_{11} + w_{21}} & \dfrac{w_{12}}{w_{12} + w_{22}} \\ \dfrac{w_{21}}{w_{11} + w_{21}} & \dfrac{w_{22}}{w_{12} + w_{22}} \end{matrix}\right)\times \left(\begin{matrix} e_1 \\ e_2 \end{matrix}\right) \]

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

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

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

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

Но нам очень нужно получить простую формулу для удобного и быстрого расчета матрицы погрешностей.

Пора немного пошалить!

Вновь обратим взор на формулу выше. Можно заметить, что самым главным является умножение погрешности выходного нейрона ​\(e_n \) ​ на вес связи ​\(w_{ij} \) ​, которая к этому выходному нейрону подсоединена. Чем больше вес, тем большую погрешность получит нейрон скрытого слоя. Эту важную деталь мы сохраняем. А вот знаменатели дробей служат лишь нормализующим фактором. Если их убрать, то мы лишимся масштабирования ошибки на предыдущие слои, что не так уж и страшно. Таким образом, мы можем избавиться от знаменателей:

\[ e_1\cdot \frac{w_{11}}{w_{11} + w_{21}} \hspace{5pt} \longrightarrow \hspace{5pt} e_1 \cdot w_{11} \]

Запишем теперь формулу для получения матрицы погрешностей, но без знаменателей в левой матрице:

\[ \mathbf{E}_{\text{hid}} = \left(\begin{matrix} w_{11} & w_{12} \\ w_{21} & w_{22} \end{matrix}\right)\times \left(\begin{matrix} e_1 \\ e_2 \end{matrix}\right) \]

Так гораздо лучше!

В разделе по использованию матриц при расчетах прохода сигнала по сети мы использовали следующую матрицу весов:

\[ \left(\begin{matrix} w_{11} & w_{21} \\ w_{12} & w_{22} \end{matrix}\right) \]

Сейчас, для расчета матрицы погрешностей мы используем такую матрицу:

\[ \left(\begin{matrix} w_{11} & w_{12} \\ w_{21} & w_{22} \end{matrix}\right) \]

Можно заметить, что во второй матрице элементы как бы отражены относительно диагонали матрицы, идущей от левого верхнего до правого нижнего края матрицы: ​\(w_{21} \) ​ и ​\(w_{12} \) ​ поменялись местами. Такая операция над матрицами существует и называется она \textbf{транспонированием} матрицы. Ранее мы использовали матрицу весов ​\(\mathbf{W} \) ​. Транспонированные матрицы имеют специальный значок справа сверху: ​\(^\intercal \) ​. В расчете матрицы погрешностей мы используем транспонированную матрицу весов: ​\(\mathbf{W}^\intercal \) ​.

Вот два примера для иллюстрации транспонирования матриц. Заметьте, что данную операцию можно выполнять даже для матриц, в которых число столбцов и строк различно.

\[ \left(\begin{matrix} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \end{matrix}\right)^\intercal = \left(\begin{matrix} 1 & 4 & 7 \\ 2 & 5 & 8 \\ 3 & 6 & 9 \end{matrix}\right) \]

\[ \left(\begin{matrix} 1 & 2 & 3 \\ 4 & 5 & 6 \end{matrix}\right)^\intercal = \left(\begin{matrix} 1 & 4 \\ 2 & 5 \\ 3 & 6 \end{matrix}\right) \]

Мы получили то, что хотели - простую формулу для расчета матрицы погрешностей нейронов скрытого слоя:

\[ \mathbf{E}_{\text{hid}} = \mathbf{W}^\intercal \times \mathbf{E}_{\text{out}} \]

Это все конечно отлично, но правильно ли мы поступили, просто проигнорировав знаменатели? Да.

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

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

Убрав знаменатели, мы сохранили общую суть обратного распространения ошибки - чем больше вес, тем больше его надо скорректировать. Мы просто убрали смягчающий фактор.

Ключевые моменты

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

Сделайте перерыв. Вы его заслужили. Следующие несколько разделов будут финальными и очень крутыми. Но их надо проходить на свежую голову.

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

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

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

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

Дельта правило (правило Видроу-Хоффа).

Определим ошибку :

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

Дельта-правило заключается в следующем – изменение величины весового коэффициента должно быть равно:

Где – норма обучения. Это число мы сами задаем перед началом обучения. – это сигнал, приходящий к элементу k от элемента j . А – ошибка элемента k .

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

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

Алгоритм обратного распространения ошибок.

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

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

Я, пожалуй, не буду приводить математические выводы и расчеты (несмотря на мою любовь к математике 🙂), чтобы не перегружать статью, ограничимся только итоговыми результатами:

Функция – это функция активности элемента. Давайте использовать логистическую функцию, для нее:

Подставляем в предыдущую формулу и получаем величину ошибки:

В этой формуле:

Наверняка сейчас еще все это кажется не совсем понятным, но не переживайте, при рассмотрении практического примера все встанет на свои места 😉

Собственно, давайте к нему и перейдем.

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

Рассмотрим нейронную сеть и вручную проведем расчеты для прямого и обратного “потоков” в сети.

На вход мы должны подать образец, пусть это будет (0.2, 0.5) . Ожидаемый выход сети – 0.4 . Норма обучения пусть будет равна 0.85 . Давайте проведем все расчеты поэтапно. Кстати, совсем забыл, в качестве функции активности мы будем использовать логистическую функцию:

Итак, приступаем…

Вычислим комбинированный ввод элементов 2 , 3 и 4 :

Активность этих элементов равна:

Комбинированный ввод пятого элемента:

Активность пятого элемента и в то же время вывод нейронной сети равен:

С прямым “потоком” разобрались, теперь перейдем к обратному “потоку”. Все расчеты будем производить в соответствии с формулами, которые мы уже обсудили. Итак, вычислим ошибку выходного элемента:

Тогда ошибки для элементов 2 , 3 и 4 равны соответственно:

Здесь значения -0.014, -0.028 и -0.056 получаются в результате прохода ошибки выходного элемента –0.014 по взвешенным связям в направлении к элементам 2 , 3 и 4 соответственно.

И, наконец-то, рассчитываем величину, на которую необходимо изменить значения весовых коэффициентов. Например, величина корректировки для связи между элементами 0 и 2 равна произведению величины сигнала, приходящего в элементу 2 от элемента 0 , ошибки элемента 2 и нормы обучения (все по дельта-правилу, которое мы обсудили в начале статьи):

Аналогичным образом производим расчеты и для остальных элементов:

Теперь новые весовые коэффициенты будут равны сумме предыдущего значения и величины поправки.

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

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

Итак, добавляем в нашу сеть два новых элемента (X и Y), которые теперь будут выполнять роль входных. На вход также подаем образец (0.2, 0.5) . Рассмотрим алгоритм в данном случае:

1. Прямой проход сети. Здесь все точно также как и для сети с одним скрытым слоем. Результатом будет значение .

2. Вычисляем ошибку выходного элемента:

3. Теперь нам нужно вычислить ошибки элементов 2, 3 и 4.

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

Обновление правила цепочки

Можно рассматривать как длинный ряд вложенных уравнений. Если вы так думаете о прямом распространении, то обратное распространение — это просто приложение правила цепочки (дифференцирования сложной функции) для поиска производных потерь по любой переменной во вложенном уравнении. С учётом функции прямого распространения:

F(x)=A(B(C(x)))

A, B, и C — на различных слоях. Пользуясь правилом цепочки, мы легко вычисляем производную f(x) по x:

F′(x)=f′(A)⋅A′(B)⋅B′(C)⋅C′(x)

Что насчёт производной относительно B ? Чтобы найти производную по B , вы можете сделать вид, что B (C(x)) является константой, заменить ее переменной-заполнителем B , и продолжить поиск производной по B стандартно.

F′(B)=f′(A)⋅A′(B)

Этот простой метод распространяется на любую переменную внутри функции, и позволяет нам в точности определить влияние каждой переменной на общий результат.

Применение правила цепочки

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

Учитывая сеть, состоящую из одного нейрона, общая потеря нейросети может быть рассчитана как:

Cost=C(R(Z(XW)))

Используя правило цепочки, мы легко можем найти производную потери относительно веса W.

C′(W)=C′(R)⋅R′(Z)⋅Z′(W)=(y^−y)⋅R′(Z)⋅X

Теперь, когда у нас есть уравнение для вычисления производной потери по любому весу, давайте обратимся к примеру с нейронной сетью:

Какова производная от потери по Wo ?

C′(WO)=C′(y^)⋅y^′(ZO)⋅Z′O(WO)=(y^−y)⋅R′(ZO)⋅H

А что насчет Wh ? Чтобы узнать это, мы просто продолжаем возвращаться в нашу функцию, рекурсивно применяя правило цепочки, пока не доберемся до функции, которая имеет элемент Wh .

C′(Wh)=C′(y^)⋅O′(Zo)⋅Z′o(H)⋅H′(Zh)⋅Z′h(Wh)=(y^−y)⋅R′(Zo)⋅Wo⋅R′(Zh)⋅X

И просто забавы ради, что, если в нашей сети было бы 10 скрытых слоев. Что такое производная потери для первого веса w1?

C(w1)=(dC/dy^)⋅(dy^/dZ11)⋅(dZ11/dH10)⋅(dH10/dZ10)⋅(dZ10/dH9)⋅(dH9/dZ9)⋅(dZ9/dH8)⋅(dH8/dZ8)⋅(dZ8/dH7)⋅(dH7/dZ7)⋅(dZ7/dH6)⋅(dH6/dZ6)⋅(dZ6/dH5)⋅(dH5/dZ5)⋅(dZ5/dH4)⋅(dH4/dZ4)⋅(dZ4/dH3)⋅(dH3/dZ3)⋅(dZ3/dH2)⋅(dH2/dZ2)⋅(dZ2/dH1)⋅(dH1/dZ1)⋅(dZ1/dW1)

Заметили закономерность? Количество вычислений, необходимых для расчёта производных потерь, увеличивается по мере углубления нашей сети. Также обратите внимание на избыточность в наших расчетах производных . Производная потерь каждого слоя добавляет два новых элемента к элементам, которые уже были вычислены слоями над ним. Что, если бы был какой-то способ сохранить нашу работу и избежать этих повторяющихся вычислений?

Сохранение работы с мемоизацией

Мемоизация — это термин в информатике, имеющий простое значение: не пересчитывать одно и то же снова и снова . В мемоизации мы сохраняем ранее вычисленные результаты, чтобы избежать пересчета одной и той же функции. Это удобно для ускорения рекурсивных функций, одной из которых является обратное распространение. Обратите внимание на закономерность в уравнениях производных приведённых ниже.

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

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

Примечание: термин ошибка слоя относится к производной потерь по входу в слой. Он отвечает на вопрос: как изменяется выход функции потерь при изменении входа в этот слой?

Ошибка выходного слоя

Для расчета ошибки выходного слоя необходимо найти производную потерь по входу выходному слою, Zo . Это отвечает на вопрос: как веса последнего слоя влияют на общую ошибку в сети? Тогда производная такова:

C′(Zo)=(y^−y)⋅R′(Zo)

Чтобы упростить запись, практикующие МО обычно заменяют последовательность (y^−y)∗R"(Zo) термином Eo . Итак, наша формула для ошибки выходного слоя равна:

Eo=(y^−y)⋅R′(Zo)

Ошибка скрытого слоя

Для вычисления ошибки скрытого слоя нужно найти производную потерь по входу скрытого слоя, Zh .

Eh=Eo⋅Wo⋅R′(Zh)

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

Производная потерь по любому весу

Вернемся к нашей формуле для производной потерь по весу выходного слоя Wo .

C′(WO)=(y^−y)⋅R′(ZO)⋅H

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

C′(Wo)=Eo⋅H

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

C′(w)=CurrentLayerError⋅CurrentLayerInput

Примечание: вход относится к активации с предыдущего слоя, а не к взвешенному входу, Z.

Подводя итог

Вот последние 3 уравнения, которые вместе образуют основу обратного распространения.

Вот процесс, визуализированный с использованием нашего примера нейронной сети выше:

Обратное распространение: пример кода

def relu_prime(z): if z > 0: return 1 return 0 def cost(yHat, y): return 0.5 * (yHat - y)**2 def cost_prime(yHat, y): return yHat - y def backprop(x, y, Wh, Wo, lr): yHat = feed_forward(x, Wh, Wo) # Layer Error Eo = (yHat - y) * relu_prime(Zo) Eh = Eo * Wo * relu_prime(Zh) # Cost derivative for weights dWo = Eo * H dWh = Eh * x # Update weights Wh -= lr * dWh Wo -= lr * dWo

Метод обратного распространения ошибки

Метод обратного распространения ошибки - метод обучения многослойного персептрона, один из вариантов обучения с учителем. Впервые метод был описан Полом Дж. Вербосом. Далее существенно развит в 1986 г. Дэвидом И. Румельхартом, Дж. Е. Хинтоном и Рональдом Дж. Вильямсом. Это итеративный градиентный алгоритм, который используется с целью минимизации ошибки работы многослойного перцептрона и получения желаемого выхода.

Основная идея этого метода состоит в распространении сигналов ошибки от выходов сети к её входам, в направлении, обратном прямому распространению сигналов в обычном режиме работы. Барцев и Охонин предложили сразу общий метод («принцип двойственности»), приложимый к более широкому классу систем, включая системы с запаздыванием, распределённые системы, и т.п.

Метод является модификацией классического метода градиентного спуска.

Алгоритм метода обратного распространения ошибки

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

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

где реальный выход N-го выходного слоя сети для p-го нейрона на j-м обучающем примере, желаемый выход. То есть, минимизировав такой функционал, мы получим решение по методу наименьших квадратов.

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

где весовой коэффициент j-го нейрона n-го слоя для связи с i-м нейроном (n-1)-го слоя.

Параметр называется параметром скорости обучения.

Таким образом, требуется определить частные производные целевой функции E по всем весовым коэффициентам сети. Согласно правилам дифференцирования сложной функции

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

Третий сомножитель / есть ни что иное, как выход i-го нейрона (n-1)-го слоя, то есть

Частные производные целевой функции по весам нейронов выходного слоя теперь можно легко вычислить. Производя дифференцирование (1) по и учитывая (3) и (5) будем иметь

Введем обозначение

Тогда для нейронов выходного слоя

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

Заметим, что в этой формуле первые два сомножителя есть не что иное, как. Таким образом, с помощью (9) можно выражать величины для нейронов n-го слоя черездля нейронов (n+1)-го. Поскольку для последнего слоя легко вычисляется по (8), то можно с помощью рекурсивной формулы

получить значения для вех нейронов всех слоев.

Окончательно формулу (2) для модификации весовых коэффициентов можно записать в виде

Таким образом, полный алгоритм обучения нейронной сети с помощью алгоритма обратного распространения строиться следующим образом.

Присваиваем всем весовым коэффициентам сети случайные начальные значения. При этом сеть будет осуществлять какое-то случайное преобразование входных сигналов и значения целевой функции (1) будут велики.

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

Скорректировать веса сети:

Оценка работы сети

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

где - требуемое значение выходного сигнала.

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

Недостатки алгоритма

Несмотря на многочисленные успешные применения обратного распространения, оно не является панацеей. Больше всего неприятностей приносит неопределённо долгий процесс обучения. В сложных задачах для обучения сети могут потребоваться дни или даже недели, она может и вообще не обучиться. Причиной может быть одна из описанных ниже.

Паралич сети

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

Локальные минимумы

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

Размер шага

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

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

Среда разработки и причины ее выбора

Разработка приложения будет осуществляться на языке программирования C# с фреймворком.NETFramework4.0 в среде разработки MicrosoftVisualStudio 2010. Фрагменты кода, требующие массивных вычислений, разработаны на языке C++. MSVisualStudio 2010 включает в себя полный набор новых и улучшенных функций, упрощающих все этапы процесса разработки от проектирования до развертывания.

MicrosoftVisualStudio 2010 Ultimate - интегрированная среда инструментальных средств и серверная инфраструктура, упрощающая процесс разработки приложения в целом. Для создания бизнес-приложений используются эффективные, предсказуемые, настраиваемые процессы. Детальная аналитика повышает прозрачность и прослеживаемость всего жизненного цикла приложения. Как при создании новых решений, так и при доработке существующих, доступна разработка с помощью мощных инструментов создания прототипов, проектирования архитектуры и разработки, которые позволяют разрабатывать приложения для всевозможных платформ и технологий, таких как обработка данных в облаке и параллельная обработка данных. Расширенные возможности координирования совместной деятельности наряду с интегрированными инновационными инструментами тестирования и отладки обеспечат повышение производительности группы и создание высококачественных и недорогих решений.

Разработка приложений в MicrosoftVisualStudio2010 Ultimate на языке C# с фреймворком.NETFramework4.0 осуществляется с применением объектно-ориентированного программирования и визуального программирования.

Алгоритм обратного распространения ошибки является одним из методов обучения многослойных нейронных сетей прямого распространения, называемых также многослойными персептронами. Многослойные персептроны успешно применяются для решения многих сложных задач.

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

Рассмотрим работу алгоритма подробней. Допустим необходимо обучить следующую нейронную сеть, применив алгоритм обратного распространения ошибки:

На приведенном рисунке использованы следующие условные обозначения:

В качестве активационной функции в многослойных персептронах, как правило, используется сигмоидальная активационная функция, в частности логистическая:

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

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

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

Следующий:

  1. Инициализировать синаптические веса маленькими случайными значениями.
  2. Выбрать очередную обучающую пару из обучающего множества; подать входной вектор на вход сети.
  3. Вычислить выход сети.
  4. Вычислить разность между выходом сети и требуемым выходом (целевым вектором обучающей пары).
  5. Подкорректировать веса сети для минимизации ошибки (как см. ниже).
  6. Повторять шаги с 2 по 5 для каждого вектора обучающего множества до тех пор, пока ошибка на всем множестве не достигнет приемлемого уровня.

Операции, выполняемые шагами 2 и 3, сходны с теми, которые выполняются при функционировании уже обученной сети, т.е. подается входной вектор и вычисляется получающийся выход. Вычисления выполняются послойно. На рис. 1 сначала вычисляются выходы нейронов слоя (слой входной, а значит никаких вычислений в нем не происходит), затем они используются в качестве входов слоя , вычисляются выходы нейронов слоя , которые и образуют выходной вектор сети . Шаги 2 и 3 образуют так называемый «проход вперед», так как сигнал распространяется по сети от входа к выходу.

Шаги 4 и 5 составляют «обратный проход», здесь вычисляемый сигнал ошибки распространяется обратно по сети и используется для подстройки весов.

Рассмотрим подробней 5 шаг – корректировка весов сети. Здесь следует выделить два нижеописанных случая.

Случай 1. Корректировка синаптических весов выходного слоя

Например, для модели нейронной сети на рис. 1, это будут веса имеющие следующие обозначения: и . Определимся, что индексом будем обозначать нейрон, из которого выходит синаптический вес, а – нейрон в который входит:

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

Тогда, веса выходного слоя после коррекции будут равны:

Приведем пример вычислений для синаптического веса :

Случай 2. Корректировка синаптических весов скрытого слоя

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

2024 med103.ru. Я самая красивая. Мода и стиль. Разные хитрости. Уход за лицом.