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

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

Обучение нейронной сети

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

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

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

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

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

    \[\delta = y_0 - y\]

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

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

    \[\Delta w_{jk} = \eta*\delta_k*x_j\]

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

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

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

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

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

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

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

    \[ \delta_j = f^{'}(net_j)*\sum_{k}{}{\delta_k*w_{kj} \]

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

    \[ f^{'}(net_j) = f(net_j) * (1 - f(net_j)) \]

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

    \[ \delta_j = f(net_j) * (1 - f(net_j))*\sum_{k}{}{\delta_k*w_{kj} \]

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

  • \delta_j — ошибка элемента с индексом j
  • k — индекс, соответствующий слою, который посылает ошибку «обратно»
  • net_j — комбинированный ввод элемента
  • f(net_j) — активность элемента

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

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

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

Нейронная сеть

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

    \[f(x) = \frac{1}{1 + e^{-x}}\]

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

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

    \[net_2 = (-1) * 0.2 + 2 * 0.5 = 0.8\]

    \[net_3 = 1 * 0.2 + 1 * 0.5 = 0.7\]

    \[net_4 = (-2) * 0.5 + 3 * 0.2 = -0.4\]

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

    \[f(net_2) = 0.69\]

    \[f(net_3) = 0.67\]

    \[f(net_4) = 0.40\]

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

    \[net_5 = 0.69 + 0.67 * 2 + 0.4 * 4 = 3.63\]

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

    \[f(net_5) = 0.974\]

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

    \[\delta_5 = (0.4 - 0.974)) * f(net_5) * (1 - f(net_5))\]

    \[\delta_5 = (0.4 - 0.974) * 0.974* (1 - 0.974)\]

    \[\delta_5 = -0.014\]

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

    \[ \delta_j = \sum_{k}{}{\delta_k*w_{kj} * f(net_j) * (1 - f(net_j)) \]

    \[\delta_2 = -0.014 * 0.69 * (1 - 0.69) = -0.0029\]

    \[\delta_3 = -0.028 * 0.67 * (1 - 0.67) = -0.0061\]

    \[\delta_4 = -0.056 * 0.40 * (1 - 0.40) = -0.0134\]

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

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

    \[\Delta w_{02} = 0.2 * -0.0029 * 0.85\]

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

    \[\Delta w_{03} = 0.2 * -0.0061 * 0.85\]

    \[\Delta w_{04} = 0.2 * -0.0134 * 0.85\]

    \[\Delta w_{12} = 0.5 * -0.0029 * 0.85\]

    \[\Delta w_{13} = 0.5 * -0.0061 * 0.85\]

    \[\Delta w_{14} = 0.5 * -0.0134 * 0.85\]

    \[\Delta w_{25} = 0.69 * -0.014 * 0.85\]

    \[\Delta w_{35} = 0.67 * -0.014 * 0.85\]

    \[\Delta w_{45} = 0.40 * -0.014 * 0.85\]

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

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

Собственно, на этом на сегодня мы закончим, до скорых встреч!

Понравилась статья? Поделись с друзьями!

Обучение нейронной сети. Алгоритм обратного распространения ошибок.: 23 комментария
  1. Спасибо! Следим! Ну и ждем интересный пример из собственных задач и программную реализацию на QT 🙂

  2. Теория и математика очень круто 🙂 А как использовать на практике в Embedded? например на STM32 или BeagleBone, Black Raspberry Pi

    • В основном задачи классификации, распознавания образцов. Кроме того, задачи прогнозирования.

  3. пожалуй,единственная статья(+предыдущая),которая мне действительно помогла в понимании нейросетей.
    спасибо автор!
    сейчас борюсь с дипломом…много вопросов…

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

  5. Может я ошибаюсь, но комбинированный ввод элементов 2,3 и 4 рассчитаны в примере как 0.8 , 0.7 и -0.4 соответственно, и при этом их активности указаны как 0.31, 0.33 и 0.60 однако если в функцию активации подставить значения 0.8 , 0.7 и -0.4 то результаты будут 0.69, 0.67 и 0.40 соответственно. Вопрос как появились значения 0.31, 0,33 и 0,60 ?
    P.S. Очевидно что 0.31 = 1- 0.69 и т.д

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

  6. по моему та же ошибка осталась для 5-го элемента:
    при 3.63 функция активации должна быть 0,974

  7. Статья очень понравилась, единственное не могу до сих пор вкурить что обозначает (вычисляет) данная формула
    f'(net_j)=f(net_j)*(1-f(net_j))
    Объясните пожалуйста.

    • Это производная функции f(net_j), если рассчитать производную, то получится выражение, которое можно выразить через исходную функцию.

  8. >Здесь значения 0.009, 0.018 и 0.036 получаются в результате прохода ошибки выходного элемента 0.009 по взвешенным связям в направлении к элементам 2, 3 и 4 соответственно.

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

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

    не вижу только в статье, откуда взялась формула для вычисления ошибки на выходном слое δ5 как

    δ5 = (y0-y)*f(net5)(1-f(net5)) [1]

    после которой получилось δ5=-0.014

    если следовать формуле для вычисления δj по δk
    δj=f(net-j)*(1-f(net-j))*Σwk*δk [2]

    то для того, чтобы для δ5 получилась формула [1] из формулы [2], нужно взять j=5, k=6: получается, что типа добавляем фиктивный выход 6 с входным нейроном 5 с весом w6=1 и ошибкой δ6=y0-y

    другой вариант — просто постулировать формулу [1] для вычисления ошибки выходного слоя

    третий вариант — брать ошибку для выходного слоя δ5=(y0-y), как показано в начале статьи, но в таком случае вычисления в статье не верны

    • Добрый день!

      В формуле [2] часть, которая находится под знаком суммы представляет из себя ошибку на выходе нейрона j, а для выходного нейрона получается, что эту ошибку мы знаем уже из самого условия задачи, ведь стоит цель получить определенное значение на выходе сети. Поэтому, по большому, счету можно считать, что мы добавляем «виртуальный» элемент с весом 1, он и даст как раз такую же ошибку выходного нейрона.

      • Написал реализацию сетки с обучением на Java, взял δ5 = (y0-y), все работает, думаю, нюанс не принципиальный, пойдут оба варианта.

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

        Пробую учить простому XOR, два входа, два скрытых нейрона, один выход.

        Сам алгоритм распространения ошибки, понятно, работает, но можно ли с ним как-то обучать сетку воспринимать одновременно множественные варианты входных параметров?

        • (y0-y) — это ошибка именно выхода 5 элемента, а нам нужно ее как бы провести через 5 элемент обратно, поэтому нужно умножать на другие множители.

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

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

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

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *