Быстрое дифференцирование
Материал из MachineLearning.
| Строка 1: | Строка 1: | ||
| - | {{well|Статья написана с использованием LLM '''ChatGPT, GPT-5.6 Thinking''' и проверена участником [[Участник:Vadim Iamaletdinov|Vadim Iamaletdinov]] 18: | + | {{well|Статья написана с использованием LLM '''ChatGPT, GPT-5.6 Thinking''' и проверена участником [[Участник:Vadim Iamaletdinov|Vadim Iamaletdinov]] 18:13, 18 июля 2026 (MSD)}} |
{{TOCright}} | {{TOCright}} | ||
| Строка 17: | Строка 17: | ||
== Зачем нужно быстрое дифференцирование == | == Зачем нужно быстрое дифференцирование == | ||
| - | Во многих задачах машинного обучения параметры <tex>w\in | + | Во многих задачах машинного обучения параметры <tex>w\in\mathbb R^n</tex> выбираются минимизацией функции потерь |
| + | |||
| + | :: <tex>L(w)=\frac{1}{\ell}\sum_{i=1}^{\ell}\mathcal L\bigl(a(x_i,w),y_i\bigr)+\lambda R(w),</tex> | ||
| - | |||
где <tex>a(x_i,w)</tex> — прогноз модели, <tex>\mathcal L</tex> — функция потерь, <tex>R</tex> — регуляризатор. Градиентные методы требуют на каждой итерации вычислять | где <tex>a(x_i,w)</tex> — прогноз модели, <tex>\mathcal L</tex> — функция потерь, <tex>R</tex> — регуляризатор. Градиентные методы требуют на каждой итерации вычислять | ||
| - | + | :: <tex>\nabla_w L(w)=\left(\frac{\partial L}{\partial w_1},\ldots,\frac{\partial L}{\partial w_n}\right).</tex> | |
| + | |||
Если находить каждую частную производную независимо, большая часть промежуточных вычислений повторяется. Например, конечные разности требуют порядка <tex>n</tex> запусков функции для одного градиента: | Если находить каждую частную производную независимо, большая часть промежуточных вычислений повторяется. Например, конечные разности требуют порядка <tex>n</tex> запусков функции для одного градиента: | ||
| - | + | :: <tex>\frac{\partial L(w)}{\partial w_j}\approx | |
| + | \frac{L(w+h e_j)-L(w)}{h}.</tex> | ||
| + | |||
Кроме высокой стоимости, этот подход содержит ошибку усечения и чувствителен к округлению: слишком большое <tex>h</tex> даёт грубую аппроксимацию, а слишком малое приводит к вычитанию близких чисел. Символьное дифференцирование даёт точную формулу, но может многократно дублировать одинаковые подвыражения и порождать громоздкие выражения. | Кроме высокой стоимости, этот подход содержит ошибку усечения и чувствителен к округлению: слишком большое <tex>h</tex> даёт грубую аппроксимацию, а слишком малое приводит к вычитанию близких чисел. Символьное дифференцирование даёт точную формулу, но может многократно дублировать одинаковые подвыражения и порождать громоздкие выражения. | ||
| Строка 41: | Строка 45: | ||
Пусть программа вычисляет отображение | Пусть программа вычисляет отображение | ||
| - | + | :: <tex>F:\mathbb R^n\to\mathbb R^m,\qquad y=F(x).</tex> | |
| + | |||
Её выполнение представляется ориентированным ациклическим [[Вычислительный граф|вычислительным графом]]. Входные вершины содержат компоненты <tex>x_1,\ldots,x_n</tex>, а каждая последующая вершина вычисляет элементарную операцию | Её выполнение представляется ориентированным ациклическим [[Вычислительный граф|вычислительным графом]]. Входные вершины содержат компоненты <tex>x_1,\ldots,x_n</tex>, а каждая последующая вершина вычисляет элементарную операцию | ||
| - | + | :: <tex>v_i=\varphi_i(v_{p_1},\ldots,v_{p_k}).</tex> | |
| + | |||
Функциями <tex>\varphi_i</tex> могут быть сложение, умножение, матричное произведение, экспонента, логарифм, функция активации и другие операции, для которых задано локальное правило производной. Выходные вершины содержат <tex>y_1,\ldots,y_m</tex>. | Функциями <tex>\varphi_i</tex> могут быть сложение, умножение, матричное произведение, экспонента, логарифм, функция активации и другие операции, для которых задано локальное правило производной. Выходные вершины содержат <tex>y_1,\ldots,y_m</tex>. | ||
Полная производная <tex>F</tex> задаётся [[Матрица Якоби|матрицей Якоби]] | Полная производная <tex>F</tex> задаётся [[Матрица Якоби|матрицей Якоби]] | ||
| - | + | :: <tex>J_F(x)= | |
| - | Автоматическое дифференцирование обычно не строит эту матрицу явно. Вместо этого оно эффективно вычисляет произведения <tex>J_F r</tex> или <tex>J_F^ | + | \left[ |
| + | \frac{\partial y_i}{\partial x_j} | ||
| + | \right]_{i=1,\ldots,m;\;j=1,\ldots,n}.</tex> | ||
| + | |||
| + | Автоматическое дифференцирование обычно не строит эту матрицу явно. Вместо этого оно эффективно вычисляет произведения <tex>J_F r</tex> или <tex>J_F^{\mathsf T}q</tex>. Выбор одного из этих произведений приводит соответственно к прямому и обратному режимам. | ||
== Прямой режим == | == Прямой режим == | ||
| Строка 56: | Строка 66: | ||
В прямом режиме вместе с каждым обычным значением <tex>v_i</tex>, называемым ''основным'' или ''прямым'', вычисляется касательная величина | В прямом режиме вместе с каждым обычным значением <tex>v_i</tex>, называемым ''основным'' или ''прямым'', вычисляется касательная величина | ||
| - | + | :: <tex>\dot v_i=\frac{d v_i}{dt},</tex> | |
| + | |||
соответствующая возмущению входа <tex>x(t)=x+t r</tex>. Для вершины | соответствующая возмущению входа <tex>x(t)=x+t r</tex>. Для вершины | ||
| - | + | :: <tex>v_i=\varphi_i(v_{p_1},\ldots,v_{p_k})</tex> | |
| + | |||
касательная распространяется по правилу | касательная распространяется по правилу | ||
| - | + | :: <tex>\dot v_i= | |
| + | \sum_{s=1}^{k} | ||
| + | \frac{\partial\varphi_i}{\partial v_{p_s}}\dot v_{p_s}.</tex> | ||
| + | |||
Если положить <tex>\dot x=r</tex>, на выходе получится | Если положить <tex>\dot x=r</tex>, на выходе получится | ||
| - | + | :: <tex>\dot y=J_F(x)r.</tex> | |
| + | |||
Таким образом, один прямой проход вычисляет произведение Якобиана на произвольный вектор, или JVP (Jacobian–vector product). При <tex>r=e_j</tex> получается <tex>j</tex>-й столбец Якобиана. | Таким образом, один прямой проход вычисляет произведение Якобиана на произвольный вектор, или JVP (Jacobian–vector product). При <tex>r=e_j</tex> получается <tex>j</tex>-й столбец Якобиана. | ||
| - | Прямой режим особенно выгоден, когда входов мало, а выходов много, например для отображения <tex>F: | + | Прямой режим особенно выгоден, когда входов мало, а выходов много, например для отображения <tex>F:\mathbb R\to\mathbb R^m</tex>. Для построения полного Якобиана общего отображения требуется до <tex>n</tex> прямых проходов — по одному на каждый базисный вектор входного пространства.<ref name="Baydin2018"/> |
=== Двойственные числа === | === Двойственные числа === | ||
| Строка 74: | Строка 90: | ||
Одна из реализаций прямого режима основана на [[Двойственные числа|двойственных числах]] | Одна из реализаций прямого режима основана на [[Двойственные числа|двойственных числах]] | ||
| - | + | :: <tex>v+\dot v\varepsilon,\qquad \varepsilon^2=0.</tex> | |
| + | |||
Например, | Например, | ||
| - | + | :: <tex>(u+\dot u\varepsilon)(v+\dot v\varepsilon) | |
| + | =uv+(u\dot v+\dot u v)\varepsilon.</tex> | ||
| + | |||
Коэффициент при <tex>\varepsilon</tex> автоматически воспроизводит правило производной произведения. Перегрузив элементарные операции для двойственных чисел, можно выполнять исходную программу и одновременно получать направленную производную. | Коэффициент при <tex>\varepsilon</tex> автоматически воспроизводит правило производной произведения. Перегрузив элементарные операции для двойственных чисел, можно выполнять исходную программу и одновременно получать направленную производную. | ||
| Строка 86: | Строка 105: | ||
Для скалярного результата <tex>L</tex> каждой промежуточной переменной сопоставляется сопряжённая величина | Для скалярного результата <tex>L</tex> каждой промежуточной переменной сопоставляется сопряжённая величина | ||
| - | + | :: <tex>\bar v_i=\frac{\partial L}{\partial v_i}.</tex> | |
| + | |||
Алгоритм состоит из двух фаз. | Алгоритм состоит из двух фаз. | ||
# '''Прямой проход.''' Вычисляются все <tex>v_i</tex> и фиксируются зависимости между операциями. | # '''Прямой проход.''' Вычисляются все <tex>v_i</tex> и фиксируются зависимости между операциями. | ||
# '''Обратный проход.''' Устанавливается <tex>\bar L=1</tex>, после чего вершины обходятся в обратном топологическом порядке. Для каждого ребра <tex>v_j\to v_i</tex> выполняется накопление | # '''Обратный проход.''' Устанавливается <tex>\bar L=1</tex>, после чего вершины обходятся в обратном топологическом порядке. Для каждого ребра <tex>v_j\to v_i</tex> выполняется накопление | ||
| - | # | + | #:: <tex>\bar v_j\mathrel{+}= |
| + | \bar v_i\frac{\partial v_i}{\partial v_j}.</tex> | ||
| + | |||
Знак накопления принципиален: одна промежуточная переменная может влиять на результат несколькими путями, и её полная производная равна сумме вкладов всех этих путей. | Знак накопления принципиален: одна промежуточная переменная может влиять на результат несколькими путями, и её полная производная равна сумме вкладов всех этих путей. | ||
| - | Для векторного выхода и начального вектора <tex>q\in | + | Для векторного выхода и начального вектора <tex>q\in\mathbb R^m</tex> обратный проход вычисляет |
| + | |||
| + | :: <tex>J_F(x)^{\mathsf T}q,</tex> | ||
| - | |||
то есть VJP (vector–Jacobian product). Если <tex>m=1</tex> и <tex>q=1</tex>, результатом является полный градиент <tex>\nabla F(x)</tex> за один обратный проход.<ref name="Baydin2018"/> | то есть VJP (vector–Jacobian product). Если <tex>m=1</tex> и <tex>q=1</tex>, результатом является полный градиент <tex>\nabla F(x)</tex> за один обратный проход.<ref name="Baydin2018"/> | ||
| Строка 103: | Строка 126: | ||
Рассмотрим функцию | Рассмотрим функцию | ||
| - | + | :: <tex>f(x_1,x_2)=\ln x_1+x_1x_2-\sin x_2.</tex> | |
| + | |||
Представим её как последовательность элементарных операций: | Представим её как последовательность элементарных операций: | ||
| Строка 134: | Строка 158: | ||
Обратный проход начинается с <tex>\bar f=1</tex>. Локальные производные дают | Обратный проход начинается с <tex>\bar f=1</tex>. Локальные производные дают | ||
| - | + | :: <tex>\bar v_4=1,\qquad \bar v_3=-1,</tex> | |
| - | + | ||
| + | :: <tex>\bar v_1=\bar v_4=1,\qquad \bar v_2=\bar v_4=1.</tex> | ||
| + | |||
Далее накапливаются производные по входам: | Далее накапливаются производные по входам: | ||
| - | + | :: <tex>\bar x_1= | |
| - | + | \bar v_1\frac{1}{x_1}+\bar v_2x_2 | |
| + | =\frac{1}{2}+5=5{,}5,</tex> | ||
| + | |||
| + | :: <tex>\bar x_2= | ||
| + | \bar v_2x_1+\bar v_3\cos x_2 | ||
| + | =2-\cos 5\approx1{,}716.</tex> | ||
| + | |||
Итак, | Итак, | ||
| - | + | :: <tex>\nabla f(2,5)\approx(5{,}5,\;1{,}716).</tex> | |
| + | |||
Обе компоненты получены совместно: промежуточные значения прямого прохода были использованы повторно, а одинаковые участки вычислительного графа не дифференцировались заново. | Обе компоненты получены совместно: промежуточные значения прямого прохода были использованы повторно, а одинаковые участки вычислительного графа не дифференцировались заново. | ||
| Строка 149: | Строка 182: | ||
[[Метод обратного распространения ошибки|Обратное распространение ошибки]] — частный случай обратного режима для слоистой нейронной сети. Пусть | [[Метод обратного распространения ошибки|Обратное распространение ошибки]] — частный случай обратного режима для слоистой нейронной сети. Пусть | ||
| - | + | :: <tex>h^{(0)}=x,</tex> | |
| - | + | ||
| + | :: <tex>z^{(l)}=W^{(l)}h^{(l-1)}+b^{(l)},\qquad | ||
| + | h^{(l)}=\sigma_l\bigl(z^{(l)}\bigr),\quad l=1,\ldots,L,</tex> | ||
| + | |||
а скалярная функция потерь равна <tex>\mathcal L(h^{(L)},y)</tex>. Введём ошибки слоёв | а скалярная функция потерь равна <tex>\mathcal L(h^{(L)},y)</tex>. Введём ошибки слоёв | ||
| - | + | :: <tex>\delta^{(l)}=\frac{\partial\mathcal L}{\partial z^{(l)}}.</tex> | |
| + | |||
Для выходного слоя <tex>\delta^{(L)}</tex> определяется производной функции потерь и активации. Для внутренних слоёв правило цепочки даёт рекурсию | Для выходного слоя <tex>\delta^{(L)}</tex> определяется производной функции потерь и активации. Для внутренних слоёв правило цепочки даёт рекурсию | ||
| - | + | :: <tex>\delta^{(l)}= | |
| - | где <tex>\ | + | \left(W^{(l+1)}\right)^{\mathsf T}\delta^{(l+1)} |
| + | \odot\sigma_l'\bigl(z^{(l)}\bigr),</tex> | ||
| + | |||
| + | где <tex>\odot</tex> обозначает покомпонентное произведение. Градиенты параметров имеют вид | ||
| + | |||
| + | :: <tex>\frac{\partial\mathcal L}{\partial W^{(l)}}= | ||
| + | \delta^{(l)}\left(h^{(l-1)}\right)^{\mathsf T},\qquad | ||
| + | \frac{\partial\mathcal L}{\partial b^{(l)}}=\delta^{(l)}.</tex> | ||
| - | |||
Эти формулы не являются отдельным принципом дифференцирования: они получаются применением обратного режима к конкретному вычислительному графу. Поэтому автоматическое дифференцирование применимо не только к последовательным нейронным сетям, но и к сетям с ветвлениями, остаточными связями, свёртками, механизмами внимания и повторным использованием параметров. | Эти формулы не являются отдельным принципом дифференцирования: они получаются применением обратного режима к конкретному вычислительному графу. Поэтому автоматическое дифференцирование применимо не только к последовательным нейронным сетям, но и к сетям с ветвлениями, остаточными связями, свёртками, механизмами внимания и повторным использованием параметров. | ||
| Строка 176: | Строка 219: | ||
== Вычислительная сложность == | == Вычислительная сложность == | ||
| - | Пусть <tex> | + | Пусть <tex>\operatorname{ops}(F)</tex> — число элементарных арифметических операций, необходимых для вычисления <tex>F:\mathbb R^n\to\mathbb R^m</tex>. Один проход автоматического дифференцирования требует порядка |
| + | |||
| + | :: <tex>c\,\operatorname{ops}(F)</tex> | ||
| - | |||
операций, где <tex>c</tex> — небольшая константа. Для стандартных моделей вычисления приводится теоретическая граница <tex>c<6</tex>, а в типичных реализациях арифметическая работа часто возрастает примерно в 2—3 раза.<ref name="Baydin2018"/> | операций, где <tex>c</tex> — небольшая константа. Для стандартных моделей вычисления приводится теоретическая граница <tex>c<6</tex>, а в типичных реализациях арифметическая работа часто возрастает примерно в 2—3 раза.<ref name="Baydin2018"/> | ||
| Строка 192: | Строка 236: | ||
* при сопоставимых <tex>n</tex> и <tex>m</tex> выбор зависит от структуры вычислений, разреженности и доступной памяти. | * при сопоставимых <tex>n</tex> и <tex>m</tex> выбор зависит от структуры вычислений, разреженности и доступной памяти. | ||
| - | Для скалярной функции <tex>F: | + | Для скалярной функции <tex>F:\mathbb R^n\to\mathbb R</tex> обратный режим вычисляет весь градиент за один прямой и один обратный проход. Теорема Баура—Штрассена формализует фундаментальный факт: для функции, вычисляемой арифметической прямолинейной программой, все первые частные производные можно вычислить с арифметической сложностью, отличающейся от сложности исходной функции лишь постоянным множителем.<ref name="BaurStrassen1983">{{статья |
|автор = Baur W., Strassen V. | |автор = Baur W., Strassen V. | ||
|заглавие = The Complexity of Partial Derivatives | |заглавие = The Complexity of Partial Derivatives | ||
| Строка 264: | Строка 308: | ||
* повторное дифференцирование позволяет получать Гессианы и производные более высоких порядков. | * повторное дифференцирование позволяет получать Гессианы и производные более высоких порядков. | ||
| - | Для дважды дифференцируемой скалярной функции <tex>f: | + | Для дважды дифференцируемой скалярной функции <tex>f:\mathbb R^n\to\mathbb R</tex> и вектора <tex>r</tex> произведение |
| + | |||
| + | :: <tex>H_f(x)r=\nabla^2 f(x)r</tex> | ||
| - | |||
можно вычислить без формирования матрицы <tex>n\times n</tex>. Метод Пирлмуттера применяет направленное дифференцирование к программе обратного прохода и получает точное, с учётом машинной арифметики, произведение Гессиана на вектор со стоимостью того же порядка, что и вычисление градиента.<ref name="Pearlmutter1994">{{статья | можно вычислить без формирования матрицы <tex>n\times n</tex>. Метод Пирлмуттера применяет направленное дифференцирование к программе обратного прохода и получает точное, с учётом машинной арифметики, произведение Гессиана на вектор со стоимостью того же порядка, что и вычисление градиента.<ref name="Pearlmutter1994">{{статья | ||
|автор = Pearlmutter B. A. | |автор = Pearlmutter B. A. | ||
| Строка 295: | Строка 340: | ||
На практике используются гибридные системы. Например: | На практике используются гибридные системы. Например: | ||
| - | * | + | * `torch.autograd` в PyTorch записывает граф операций и реализует прежде всего обратный режим;<ref name="PyTorchAutograd">{{cite web |
|url = https://docs.pytorch.org/docs/stable/notes/autograd | |url = https://docs.pytorch.org/docs/stable/notes/autograd | ||
|title = Autograd mechanics | |title = Autograd mechanics | ||
| Строка 301: | Строка 346: | ||
|accessdate = 2026-07-18 | |accessdate = 2026-07-18 | ||
}}</ref> | }}</ref> | ||
| - | * JAX предоставляет преобразования | + | * JAX предоставляет преобразования `grad`, `jvp`, `vjp`, а также их композиции;<ref name="JAXAutodiff">{{cite web |
|url = https://docs.jax.dev/en/latest/jacobian-vector-products.html | |url = https://docs.jax.dev/en/latest/jacobian-vector-products.html | ||
|title = Forward- and reverse-mode autodiff in JAX | |title = Forward- and reverse-mode autodiff in JAX | ||
| Строка 307: | Строка 352: | ||
|accessdate = 2026-07-18 | |accessdate = 2026-07-18 | ||
}}</ref> | }}</ref> | ||
| - | * TensorFlow записывает операции в | + | * TensorFlow записывает операции в `tf.GradientTape` и затем проходит их в обратном порядке.<ref name="TensorFlowAutodiff">{{cite web |
|url = https://www.tensorflow.org/guide/autodiff | |url = https://www.tensorflow.org/guide/autodiff | ||
|title = Introduction to gradients and automatic differentiation | |title = Introduction to gradients and automatic differentiation | ||
| Строка 334: | Строка 379: | ||
=== Численная устойчивость === | === Численная устойчивость === | ||
| - | AD точно дифференцирует реализованный алгоритм, но сам алгоритм может быть численно неустойчив. Например, программная формула для | + | AD точно дифференцирует реализованный алгоритм, но сам алгоритм может быть численно неустойчив. Например, программная формула для `softmax` без вычитания максимума способна переполниться; её автоматически полученный градиент также будет испорчен. Полезный принцип: сначала выбрать устойчивый способ вычисления функции, затем дифференцировать его. |
=== Дифференцирование приближения === | === Дифференцирование приближения === | ||
| Строка 352: | Строка 397: | ||
* сравнивать несколько случайных направленных производных с центральными разностями; | * сравнивать несколько случайных направленных производных с центральными разностями; | ||
* проверять скалярное тождество сопряжённости | * проверять скалярное тождество сопряжённости | ||
| - | + | :: <tex>q^{\mathsf T}(J_Fr)=(J_F^{\mathsf T}q)^{\mathsf T}r;</tex> | |
* тестировать крайние значения и точки смены ветвей; | * тестировать крайние значения и точки смены ветвей; | ||
| - | * контролировать | + | * контролировать `NaN`, бесконечности и масштабы градиентов; |
* отдельно проверять пользовательские правила производных. | * отдельно проверять пользовательские правила производных. | ||
| Строка 418: | Строка 463: | ||
<references/> | <references/> | ||
| + | == Литература == | ||
| + | * {{книга | ||
| + | |автор = Griewank A., Walther A. | ||
| + | |заглавие = Evaluating Derivatives: Principles and Techniques of Algorithmic Differentiation | ||
| + | |издание = 2-е изд. | ||
| + | |место = Philadelphia | ||
| + | |издательство = SIAM | ||
| + | |год = 2008 | ||
| + | |страниц = 438 | ||
| + | |isbn = 978-0-89871-659-7 | ||
| + | |ссылка = https://doi.org/10.1137/1.9780898717761 | ||
| + | }} | ||
| + | * {{статья | ||
| + | |автор = Baydin A. G., Pearlmutter B. A., Radul A. A., Siskind J. M. | ||
| + | |заглавие = Automatic Differentiation in Machine Learning: a Survey | ||
| + | |ссылка = https://www.jmlr.org/papers/v18/17-468.html | ||
| + | |издание = Journal of Machine Learning Research | ||
| + | |год = 2018 | ||
| + | |том = 18 | ||
| + | |номер = 153 | ||
| + | |страницы = 1—43 | ||
| + | }} | ||
| + | * {{статья | ||
| + | |автор = Baur W., Strassen V. | ||
| + | |заглавие = The Complexity of Partial Derivatives | ||
| + | |ссылка = https://doi.org/10.1016/0304-3975(83)90110-X | ||
| + | |издание = Theoretical Computer Science | ||
| + | |год = 1983 | ||
| + | |том = 22 | ||
| + | |номер = 3 | ||
| + | |страницы = 317—330 | ||
| + | }} | ||
| + | * {{статья | ||
| + | |автор = Pearlmutter B. A. | ||
| + | |заглавие = Fast Exact Multiplication by the Hessian | ||
| + | |ссылка = https://doi.org/10.1162/neco.1994.6.1.147 | ||
| + | |издание = Neural Computation | ||
| + | |год = 1994 | ||
| + | |том = 6 | ||
| + | |номер = 1 | ||
| + | |страницы = 147—160 | ||
| + | }} | ||
| + | * {{книга | ||
| + | |автор = Евтушенко Ю. Г. | ||
| + | |заглавие = Оптимизация и быстрое автоматическое дифференцирование | ||
| + | |место = М. | ||
| + | |издательство = ВЦ им. А. А. Дородницына РАН | ||
| + | |год = 2013 | ||
| + | |страниц = 144 | ||
| + | }} | ||
[[Категория:Машинное обучение]] | [[Категория:Машинное обучение]] | ||
Версия 14:13, 18 июля 2026
| | Статья написана с использованием LLM ChatGPT, GPT-5.6 Thinking и проверена участником Vadim Iamaletdinov 18:13, 18 июля 2026 (MSD) |
Быстрое дифференцирование — совокупность алгоритмов вычисления производных сложной функции, заданной не одной формулой, а последовательностью элементарных операций. В современной терминологии основным воплощением этой идеи является автоматическое, или алгоритмическое, дифференцирование (automatic differentiation, AD). Для скалярной функции многих переменных его обратный режим позволяет вычислить весь градиент за один прямой и один обратный проход по вычислению функции. В нейронных сетях тот же алгоритмический принцип известен как метод обратного распространения ошибки (backpropagation).
Слово «быстрое» относится прежде всего к асимптотике: стоимость вычисления градиента скалярной функции имеет тот же порядок, что и стоимость вычисления самой функции, и отличается от неё лишь постоянным множителем. Это делает дифференцирование моделей с миллионами и миллиардами параметров практически возможным. Быстрое дифференцирование лежит в основе градиентного обучения, дифференцируемого программирования, оптимального управления, обратных задач и многих методов научных вычислений.[1]
Зачем нужно быстрое дифференцирование
Во многих задачах машинного обучения параметры выбираются минимизацией функции потерь
где — прогноз модели,
— функция потерь,
— регуляризатор. Градиентные методы требуют на каждой итерации вычислять
Если находить каждую частную производную независимо, большая часть промежуточных вычислений повторяется. Например, конечные разности требуют порядка запусков функции для одного градиента:
-
Кроме высокой стоимости, этот подход содержит ошибку усечения и чувствителен к округлению: слишком большое
даёт грубую аппроксимацию, а слишком малое приводит к вычитанию близких чисел. Символьное дифференцирование даёт точную формулу, но может многократно дублировать одинаковые подвыражения и порождать громоздкие выражения.
Быстрое дифференцирование использует третий путь:
- разбивает программу на элементарные операции с известными производными;
- сохраняет или восстанавливает нужные промежуточные значения;
- применяет правило дифференцирования сложной функции локально;
- повторно использует результаты общих подвычислений.
В результате вычисляется численное значение производной, а не развёрнутая символьная формула. Погрешность усечения отсутствует; остаются обычные ошибки машинной арифметики и ошибки, связанные с некорректно заданными локальными правилами дифференцирования.[1]
Формальная постановка
Пусть программа вычисляет отображение
Её выполнение представляется ориентированным ациклическим вычислительным графом. Входные вершины содержат компоненты
, а каждая последующая вершина вычисляет элементарную операцию
Функциями
могут быть сложение, умножение, матричное произведение, экспонента, логарифм, функция активации и другие операции, для которых задано локальное правило производной. Выходные вершины содержат
.
Полная производная
задаётся матрицей Якоби
-
Автоматическое дифференцирование обычно не строит эту матрицу явно. Вместо этого оно эффективно вычисляет произведения
или
. Выбор одного из этих произведений приводит соответственно к прямому и обратному режимам.
Прямой режим
В прямом режиме вместе с каждым обычным значением
, называемым основным или прямым, вычисляется касательная величина
соответствующая возмущению входа
. Для вершины
касательная распространяется по правилу
-
Если положить
, на выходе получится
Таким образом, один прямой проход вычисляет произведение Якобиана на произвольный вектор, или JVP (Jacobian–vector product). При
получается
-й столбец Якобиана.
Прямой режим особенно выгоден, когда входов мало, а выходов много, например для отображения
. Для построения полного Якобиана общего отображения требуется до
прямых проходов — по одному на каждый базисный вектор входного пространства.[1]
Двойственные числа
Одна из реализаций прямого режима основана на двойственных числах
Например,
-
Коэффициент при
автоматически воспроизводит правило производной произведения. Перегрузив элементарные операции для двойственных чисел, можно выполнять исходную программу и одновременно получать направленную производную.
Обратный режим
Обратный режим ориентирован на функцию с небольшим числом выходов и большим числом входов. Именно такой случай типичен для машинного обучения: миллионы параметров отображаются в одно скалярное значение функции потерь.
Для скалярного результата
каждой промежуточной переменной сопоставляется сопряжённая величина
Алгоритм состоит из двух фаз.
- Прямой проход. Вычисляются все
и фиксируются зависимости между операциями.
- Обратный проход. Устанавливается
, после чего вершины обходятся в обратном топологическом порядке. Для каждого ребра
выполняется накопление
-
Знак накопления принципиален: одна промежуточная переменная может влиять на результат несколькими путями, и её полная производная равна сумме вкладов всех этих путей.
Для векторного выхода и начального вектора
обратный проход вычисляет
то есть VJP (vector–Jacobian product). Если
и
, результатом является полный градиент
за один обратный проход.[1]
Пример вычисления
Рассмотрим функцию
Представим её как последовательность элементарных операций:
Номер Операция Значение при Обратный проход начинается с
. Локальные производные дают
Далее накапливаются производные по входам:
-
-
Итак,
Обе компоненты получены совместно: промежуточные значения прямого прохода были использованы повторно, а одинаковые участки вычислительного графа не дифференцировались заново.
Обратное распространение ошибки
Обратное распространение ошибки — частный случай обратного режима для слоистой нейронной сети. Пусть
-
а скалярная функция потерь равна
. Введём ошибки слоёв
Для выходного слоя
определяется производной функции потерь и активации. Для внутренних слоёв правило цепочки даёт рекурсию
-
где
обозначает покомпонентное произведение. Градиенты параметров имеют вид
-
Эти формулы не являются отдельным принципом дифференцирования: они получаются применением обратного режима к конкретному вычислительному графу. Поэтому автоматическое дифференцирование применимо не только к последовательным нейронным сетям, но и к сетям с ветвлениями, остаточными связями, свёртками, механизмами внимания и повторным использованием параметров.
Публикация Д. Румельхарта, Дж. Хинтона и Р. Уильямса 1986 года сделала backpropagation широко известным как метод обучения многослойных сетей,[1] однако обратный режим автоматического дифференцирования был сформулирован раньше и имеет более широкую область применения.
Вычислительная сложность
Пусть
— число элементарных арифметических операций, необходимых для вычисления
. Один проход автоматического дифференцирования требует порядка
операций, где
— небольшая константа. Для стандартных моделей вычисления приводится теоретическая граница
, а в типичных реализациях арифметическая работа часто возрастает примерно в 2—3 раза.[1]
Полный Якобиан можно получить:
- прямым режимом примерно за
проходов;
- обратным режимом примерно за
проходов.
Отсюда практическое правило:
- при
предпочтителен прямой режим;
- при
предпочтителен обратный режим;
- при сопоставимых
и
выбор зависит от структуры вычислений, разреженности и доступной памяти.
Для скалярной функции
обратный режим вычисляет весь градиент за один прямой и один обратный проход. Теорема Баура—Штрассена формализует фундаментальный факт: для функции, вычисляемой арифметической прямолинейной программой, все первые частные производные можно вычислить с арифметической сложностью, отличающейся от сложности исходной функции лишь постоянным множителем.[1]
Время и память
Прямой режим может вычислять значение и касательную одновременно, поэтому обычно требует умеренной дополнительной памяти. Обратному режиму нужны промежуточные значения прямого прохода. В худшем случае объём памяти растёт пропорционально числу операций вычислительного графа.
Основной способ уменьшения памяти — контрольные точки (checkpointing, rematerialization). Сохраняется лишь часть промежуточных состояний, а остальные повторно вычисляются во время обратного прохода. Это создаёт управляемый компромисс: меньше памяти ценой дополнительного времени. Современные системы также освобождают ненужные буферы, объединяют операции, используют статический анализ времени жизни и задаваемые пользователем правила повторного вычисления.
Сравнение способов вычисления производных
Метод Точность Стоимость градиента скалярной функции Основные ограничения Ручное дифференцирование С точностью машинной арифметики Может быть оптимальной Трудоёмкость, риск ошибок, сложность сопровождения Конечные разности Приближённая Обычно вычислений функции
Выбор шага, ошибки усечения и округления Символьное дифференцирование Формально точная Зависит от размера полученной формулы Разрастание выражений, трудности с обычным программным управлением Прямой режим AD С точностью машинной арифметики проходов для полного градиента
Невыгоден при очень большом числе входов Обратный режим AD С точностью машинной арифметики Один обратный проход Хранение или повторное вычисление промежуточных значений Автоматическое дифференцирование не следует называть ни численным, ни символьным в обычном смысле. Оно использует аналитические правила производных элементарных операций, но распространяет численные значения производных по фактически выполненной программе.[1]
Производные высших порядков
Режимы можно вкладывать друг в друга. Например:
- прямой режим поверх обратного вычисляет произведение матрицы Гессе на вектор;
- обратный режим поверх прямого может вычислять другие комбинации производных;
- повторное дифференцирование позволяет получать Гессианы и производные более высоких порядков.
Для дважды дифференцируемой скалярной функции
и вектора
произведение
можно вычислить без формирования матрицы
. Метод Пирлмуттера применяет направленное дифференцирование к программе обратного прохода и получает точное, с учётом машинной арифметики, произведение Гессиана на вектор со стоимостью того же порядка, что и вычисление градиента.[1]
Матрично-свободные произведения
применяются в методах Ньютона—Крылова, оценивании кривизны, анализе чувствительности и вычислении гиперградиентов. Полный Гессиан часто не строят, поскольку его хранение требует
памяти.
Реализация в программных системах
Существуют два основных подхода.
Перегрузка операций
Числовые объекты заменяются объектами, которые вместе со значением хранят касательную информацию или записывают операцию на ленту (tape). Программа исполняется обычным образом, а граф производной формируется динамически. Подход удобен для интерактивного программирования и сложного управления потоком.
Преобразование программы
Исходная программа или промежуточное представление компилятора преобразуется в новую программу, вычисляющую значения и производные. Такой подход позволяет заранее оптимизировать граф: удалять общие подвыражения, объединять операции, планировать память и компилировать вычисления для процессоров и ускорителей.
На практике используются гибридные системы. Например:
- `torch.autograd` в PyTorch записывает граф операций и реализует прежде всего обратный режим;[1]
- JAX предоставляет преобразования `grad`, `jvp`, `vjp`, а также их композиции;[1]
- TensorFlow записывает операции в `tf.GradientTape` и затем проходит их в обратном порядке.[1]
Для новой элементарной операции необходимо определить корректное локальное правило JVP или VJP. Пользовательские правила особенно важны для численно устойчивых реализаций, неявно заданных функций, итерационных решателей и операций, у которых дифференцирование внутренней реализации не соответствует нужной математической производной.
Ограничения и типичные ошибки
Негладкие функции
Для
, ReLU, максимума и других негладких операций классическая производная в некоторых точках не существует. Система обычно выбирает условное значение, например одну из односторонних производных или элемент субдифференциала. Это соглашение должно быть известно пользователю: автоматически полученное число не превращает негладкую функцию в дифференцируемую.
Ветвления и циклы
При динамическом графе дифференцируется фактически выполненная ветвь программы. Если малое изменение входа меняет ветвь, производная трассы может не описывать поведение всей кусочно заданной функции в точке переключения.
Циклы допустимы, если число итераций конечно, однако длинная развёртка увеличивает время и память. В рекуррентных сетях многократное умножение локальных Якобианов может приводить к исчезающим или взрывающимся градиентам. Это свойство модели и её Якобианов, а не ошибка алгоритма дифференцирования.
Дискретные операции
Выбор индекса, сравнение, случайная дискретная выборка и изменение структуры данных обычно не имеют обычной производной. Для обучения применяют непрерывные релаксации, стохастические оценки градиента, прямые оцениватели или специальные неявные формулы. Эти методы не следует смешивать с точным автоматическим дифференцированием гладкой программы.
Численная устойчивость
AD точно дифференцирует реализованный алгоритм, но сам алгоритм может быть численно неустойчив. Например, программная формула для `softmax` без вычитания максимума способна переполниться; её автоматически полученный градиент также будет испорчен. Полезный принцип: сначала выбрать устойчивый способ вычисления функции, затем дифференцировать его.
Дифференцирование приближения
Если точная функция заменена конечным числом итераций численного метода, AD вычислит производную именно конечной программы. Она не обязана совпадать с производной предельного решения. Возможны три разных объекта:
- производная усечённого алгоритма;
- производная точного решения неявной задачи;
- приближение нужной производной.
Различие существенно при оптимизации через численные решатели, дифференциальные уравнения и задачи с внутренним циклом оптимизации.
Проверка градиента
Даже при использовании AD полезно выполнять тесты:
- сравнивать несколько случайных направленных производных с центральными разностями;
- проверять скалярное тождество сопряжённости
- тестировать крайние значения и точки смены ветвей;
- контролировать `NaN`, бесконечности и масштабы градиентов;
- отдельно проверять пользовательские правила производных.
Конечные разности здесь используются не как основной способ обучения, а как независимая диагностическая проверка.
Применения
Быстрое дифференцирование применяется в следующих задачах:
- обучение нейронных сетей и других параметрических моделей;
- Градиентный спуск, квазиньютоновские и матрично-свободные методы второго порядка;
- Оптимальное управление и вычисление сопряжённых переменных;
- решение обратных коэффициентных задач;
- дифференцируемые физические симуляторы;
- вероятностное программирование и вариационный вывод;
- метаобучение, подбор гиперпараметров и гиперградиенты;
- анализ чувствительности и неопределённости;
- дифференцируемая визуализация, рендеринг и обработка сигналов.
В русскоязычной литературе выражение быстрое автоматическое дифференцирование также используется для методов вычисления точных градиентов многошаговых процессов, особенно в задачах оптимального управления и обратных задачах. Экспериментальные работы показывают, что преимущество определяется не магическим сокращением числа элементарных операций, а устранением повторных вычислений, повторным использованием промежуточных величин и систематическим построением сопряжённого прохода.[1]
История
Идеи автоматического вычисления производных развивались вместе с программированием численных алгоритмов. Р. Венгерт в 1964 году описал разложение функции на последовательность элементарных присваиваний и автоматическое распространение производных по этой последовательности.[1] С. Линнайнмаа сформулировал ранний вариант обратного накопления при анализе распространения ошибок округления.[1] В 1980-х годах теория сложности и программные реализации установили обратный режим как общий метод, а популяризация backpropagation связала его с обучением многослойных нейронных сетей.[1][1]
Современное понятие автоматического дифференцирования шире backpropagation. Backpropagation обычно обозначает применение обратного режима к функции потерь нейронной сети, тогда как AD охватывает прямой и обратный режимы, векторные функции, производные высших порядков и произвольные дифференцируемые программы.
См. также
- Автоматическое дифференцирование
- Метод обратного распространения ошибки
- Вычислительный граф
- Градиент
- Градиентный спуск
- Матрица Якоби
- Матрица Гессе
- Нейронная сеть
- Функция потерь
- Оптимальное управление
Примечания
Литература
- Griewank A., Walther A. Evaluating Derivatives: Principles and Techniques of Algorithmic Differentiation. — 2-е изд.. — Philadelphia: SIAM, 2008. — 438 с. — ISBN 978-0-89871-659-7
- Baydin A. G., Pearlmutter B. A., Radul A. A., Siskind J. M. Automatic Differentiation in Machine Learning: a Survey // Journal of Machine Learning Research. — 2018. — Т. 18. — № 153. — С. 1—43.
- Baur W., Strassen V. The Complexity of Partial Derivatives // Theoretical Computer Science. — 1983. — Т. 22. — № 3. — С. 317—330.
- Pearlmutter B. A. Fast Exact Multiplication by the Hessian // Neural Computation. — 1994. — Т. 6. — № 1. — С. 147—160.
- Евтушенко Ю. Г. Оптимизация и быстрое автоматическое дифференцирование. — М.: ВЦ им. А. А. Дородницына РАН, 2013. — 144 с.
-
-
-
-
-
-
-
-
-
-

