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

Обратное распространениечерезфункцию потерь

Это всего лишь частная производная функции потерь E по выходным данным модели.

Здесь, кстати, видно, как сокращаются ½ и 2, и становится понятно, почему мы сначала прибавили ½ к формуле.

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

Обратное распространение через ReLU

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

Обратное распространение через сигмоид

Обратное распространение через Softmax (ссылка1 и ссылка2)

Эти расчеты мне показались немного сложнее, так как функция softmax для i-го вывода зависит не только от его xˡᵢ, но и от всех остальных xˡⱼ ∀ i,j ∈ (0, …, n), сумма которых стоит в знаменателе формулы прямого прохода по сети. Следовательно, формула для обратного распространения «расщепляется» на две части: частную производную от xˡᵢ и xˡⱼ:

И частная производная от xˡⱼ:

Исходя из формулы выше, есть нюанс с тем, что функция должна возвращаться при обратном распространении ошибки для ∂yˡ/ с softmax, так как в этом случай, когда все xˡᵢ используются для вычисления одного yˡᵢ, или, другими словами, каждый xˡᵢ влияет на все yˡᵢ:

Таким образом, сумма появляется в бэкпропе для softmax:

При этом значения ∂E/∂yˡₖ для всех k у нас уже есть: это бэкпроп через функцию потерь. Остается только найти ∂yˡₖ /∂xˡᵢ для всех k и всех i — то есть это матрица. Ниже приведено умножение матриц в «расширенном» виде, чтобы было лучше понятно, почему ∂yˡₖ /∂xˡᵢ является матрицей и откуда берется умножение матриц.

Речь шла как раз об этой последней матрице в разложении — ∂yˡ /∂xˡ. Посмотрите, как при перемножении матриц ∂E/∂yˡ и ∂yˡ/∂xˡ мы получаем ∂E/∂xˡ. Итак, выходом функции обратного распространения для softmax должна быть матрица ∂yˡ /∂xˡ. Это будет умножено на ∂E/∂yˡ , и мы получим ∂E/∂xˡ

Backprop через полносвязный слой

Формула обратного распространения для обновления

А вот как это будет выглядеть в матричном виде:

Ниже я привожу матрицы в «развернутом» виде, чтобы расчеты казались нагляднее.

Формула обратного распространения для обновления

Для смещения все расчеты очень похожи на предыдущий раздел:

Формула обратного распространения через ¹

В приведенной ниже формуле сумма i возникает из-за того, что каждый yₖˡ⁻ ¹ связан с каждым xˡᵢ (помните, что слой называется «полностью подключен»):

Раскладываем числитель и видим, что все частные производные равны нулю, кроме случая, когда k՛=k:

Далее матрицы находятся в «расширенном» виде. Замечу, что я намеренно оставил индексы самой последней матрицы в том виде, в котором они были до перестановки, чтобы было лучше видно, куда делись элементы после перестановки.

Обратное распространение через слой Maxpooling

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

Вот демонстрация maxpooling в python: demo_of_maxpooling.ipynb
Вторая матрица, которую возвращает функция, содержит координаты выбранных максимальных значений на прямом проходе maxpooling.

Обратное распространение через сверточный слой

Backprop для обновления ядра свертки

Все частные производные в числителе будут равны нулю, кроме тех, для которых a՛=a и b՛=b. Также,

Все вышесказанное относится к свертке. Формула обратного распространения для взаимной корреляции выглядит аналогично, за исключением изменения знака в a и b:

Важно понимать, что само ядро ​​свертки в итоговой формуле не участвует. Есть своего рода операция свертки, но с участием ∂E/∂xˡᵢⱼ и yˡ⁻ ¹, и ∂E/∂xˡᵢⱼ действует как ядро, но все равно не напоминает свертку, особенно когда значение шага больше единицы: тогда ∂E/∂xˡᵢⱼ затухает на yˡ⁻ ¹, что совершенно перестает походить на обычную сверток. Это затухание происходит из-за того, что параметры i и j повторяются внутри цикла формулы. Чтобы увидеть, как все это выглядит, вы можете использовать демо-код: demo_of_conv_backprop_through_kernel

Backprop для обновления смещения свертки

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

То есть, если мы разложим сумму по всем i и j, мы увидим, что все частные производные по ∂bˡ будут равны единице :

Для одной карты признаков существует только одно смещение, связанное со всеми элементами этой карты. Соответственно, при обновлении значения смещения следует учитывать все значения из карты признаков, полученные при обратном распространении. Как вариант, можно взять для отдельной карты признаков столько смещений, сколько элементов в этой карте, но в этом случае параметров смещения будет слишком много — больше, чем параметров самих ядер свертки. Для второго случая также легко вычислить производную — тогда каждый ∂E/∂bˡᵢⱼ (обратите внимание, что смещение уже имеет индексы i, j) будет равно каждый ∂E/∂xˡᵢⱼ.

Обратное распространение через сверточный слой

Здесь все аналогично предыдущим выводам:

Разложив сумму в числителе на a и b, получим, что все частные производные равны нулю, кроме случая, когда i՛s - a=i и j՛s - b=j и, соответственно, a=i՛s - i, b=j՛s - j. Это справедливо только для свертки, для взаимной корреляции должно быть i՛s+a=i и j՛s+b=j и, соответственно, a=ii՛s и b=jj՛s. И тогда итоговая формула в случае кросс-корреляции будет выглядеть так:

Результирующие выражения представляют собой ту же операцию свертки, но знакомый действует как ядро. Но, впрочем, все аналогично обычной свертке, только если шаг равен единице, в случаях другого шага получается совсем другое (аналогично случаю backprop для обновления ядра свертки): матрица начинает ломаться по всей матрице ∂E/∂, захватывая разные ее части (опять же, потому что индексы и для повторяются внутри цикла формул). Здесь вы можете увидеть демо-код: demo_of_conv_backprop_through_input.ipynb

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

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

Следующая часть цикла: CNN на Python, часть 3. Обучение модели