Автоматическое дифференцирование
Градиентные методы настройки нейросетей основаны на вычислении градиента от функции потерь. Для эффективного обучения нейросетей, вычисление градиента должно производиться
-
точно
-
и вычислительно эффективно.
Для этого используются методы автоматического дифференцирования - forward mode и backward mode. Второй метод также называется методом обратного распространения ошибки (backpropagation) и является основным методом настройки нейросетей.
Вначале же для полноты картины рассмотрим какие другие подходы мы могли бы применить для вычисления градиента функции.
Дифференцирование напрямую
Градиент можно вычислять вручную и программно реализовывать расчёт найденных производных. Этот подход можно автоматизировать, воспользовавшись библиотеками символьного дифференцирования, такими как SymPy [1] и SymEngine [2]. Это быстрее и избавит нас от потенциальных ошибок при вычислении производных.
В результате мы получим точное значение градиента. Недостатком подхода является сильное разрастание формул, по которым будет вычисляться градиент.
Рассмотрим нахождение производной по скаляру от функции .
Производная будет:
в которой потребуется многократное повторное вычисление функций , что неэффективно.
Если подобные операции возникают на каждом слое нейросети, то это приведёт к ещё большему разрастанию объёма повторяемых неэффективных вычислений!
Численная аппроксимация
Градиент можно находить, используя численное приближение производных:
В формуле вектор содержит малую константу на -й позиции. Этот подход избавлен от ошибок дифференцирования, но даст лишь приближённое значение производных. Также он вычислительно неэффективен, поскольку требует прохода по нейросети:
-
один проход для вычисления
-
проходов для вычислений
Поскольку стоимость одного прохода вперёд по сети имеет порядок , то общая сложность вычисления всего градиента - , что весьма много для современных нейросетей, имеющих миллионы настраиваемых параметров.
Автоматическое дифференцирование
Идея
Вычислить производные можно точно и эффективно как по объёму вычислений, так и по памяти, использ уя методы автоматического дифференцирования (automatic differentiation [3]). Существуют библиотеки, эффективно реализующие эти методы, используя процессор и видеокарты - это PyTorch [4], Tensorflow [5] и JAX [6].
Цель автоматического дифференцирования - не вывести общую функциональную формулу производной, а уметь быстро вычислять её значение в заданной точке, обладая программным кодом для вычисления дифференцируемой функции.
Расчёт функции потерь сопряжён с вычислением суперпозиции большого числа математических преобразований, вызванных как расчётом нейросетевого прогноза, так и вычислением самой функции потерь от него.
Производная сложной функции
Автоматическое дифференцирование основано на формуле расчёта производной сложной функции [7], которое для скаляра будет
Для функции
произв одная по будет
Поскольку нейросети включают в себя суперпозицию большого числа преобразований, необходимо вычислять производную от большого числа вложенных функций.
Например, для суперпозиции нескольких функций от скаляра :
Последовательное применение формулы дифференцирования сложной функции даст
Функция потерь представляет собой суперпозицию большого числа нелинейных преобразований, отвечающих промежуточным вычислениям внутри нейросети, а также вычислению самой функции потерь. Поэтому вычисление функции потерь представляется в виде графа вычислений (computational graph), в котором узлами являются промежуточные переменные, необходимые для расчёта, а связи указывают, какие промежуточные перем енные от каких зависят. Каждая переменная в графе вычислений - это некоторая простая операция от ранее посчитанных переменных, такая как сумма, разность, тригонометрическая или другая стандартная функция.
Главное требование - чтобы для этой функции была аналитически известна функция расчёта градиента.
Пример
Рассмотрим простейшую нейросеть - линейную регрессию, настраиваемую с регуляризацией:
Тогда вычисление можно представить в виде следующего вычислительного графа:

Все переменные делятся на три типа:
-
входные переменные: ;
-
финальная выходная переменная: ;
-
промежуточные переменные: .
Нас интересует расчёт градиента .
Вычислять его можно двумя способами:
-
в режиме прохода вперёд (forward-mode).
-
в режиме прохода назад (backward-mode).
Forward-mode
Рассмотрим эффективный расчёт градиента функции потерь методом forward-mode.
Он основан только на проходе вперёд (forward pass) по графу, при котором вычисляются не только значения промежуточных переменных графа, но и производные каждой промежуточной переменной по вектору весов сети . В каждую найденную производную подставляются значения промежуточных переменных графа, от которых она зависит, в результате чего сразу получаем промежуточное значение производной. Значение производной по финальной переменной и есть целевой градиент.
Пример использования forward-mode
Рассмотрим вычисление потерь
Построим по ней вычислительный граф:

Пусть нам нужно вычислить при
При проходе вперёд по графу вычислений получаем следующие значения промежуточных переменных:
Но одновременно при проходе вперёд вычисляются и производные по целевой переменной (вектору весов ):