Введение

Численные вычисления, связанные с вычислением производных.
В математике и компьютерной алгебре автоматическое дифференцирование (автодифференцирование, автодиф, или AD), также известное как алгоритмическое дифференцирование или вычислительное дифференцирование, представляет собой набор методов для вычисления частных производных функции, заданной компьютерной программой. Автоматическое дифференцирование использует тот факт, что любая компьютерная операция, независимо от её сложности, выполняется как последовательность элементарных арифметических операций (сложение, вычитание, умножение, деление и т.д.) и элементарных функций (exp, log, sin, cos и т.д.). Многократно применяя правило цепочки к этим операциям, можно автоматически вычислять частные производные любого порядка с точностью до машинной точности, используя при этом не более чем на небольшую постоянную величину большее количество арифметических операций, чем в исходной программе.

Отличие от других методов дифференцирования

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

Приложения

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

За пределами передового и обратного накопления

Прямое и обратное накопление — это лишь два (крайних) способа применения правила цепочки. Задача вычисления полного Якобиана функции f: ℝⁿ → ℝᵐ с минимальным количеством арифметических операций известна как задача оптимального Якобианского накопления (OJA), которая является NP-полной. Ключевым моментом в доказательстве является идея о том, что между локальными частными производными, обозначающими рёбра графа, могут существовать алгебраические зависимости. В частности, две или более этикеток рёбер могут оказаться равными. Сложность задачи остаётся открытой, если предположить, что все этикетки рёбер уникальны и алгебраически независимы.

Реализация

Приводится пример реализации на основе подхода с использованием дуальных чисел.

Аргументы и функции вектора

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

Высокий порядок и много переменных

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

Реализация

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

Трансформация исходного кода (SCT)

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

Перегрузка оператора (OO)

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

Перегрузка оператора и трансформация исходного кода

Для извлечения графа вычислений можно использовать перегруженные операторы, после чего происходит автоматическая генерация AD-версии исходной функции во время выполнения. В отличие от классического объектно-ориентированного автоматического дифференцирования (OO AAD), такая AD-функция не изменяется от итерации к итерации. Следовательно, отсутствует накладной расход времени выполнения, связанный с объектно-ориентированной интерпретацией или интерпретацией ленты, для каждого образца Xi. Поскольку AD-функция генерируется во время выполнения, её можно оптимизировать с учетом текущего состояния программы и предварительно вычислить определенные значения. Кроме того, её можно генерировать таким образом, чтобы последовательно использовать аппаратную векторизацию процессора для обработки блоков из 4(8) чисел двойной точности пользовательских данных (AVX2\AVX512 обеспечивает ускорение в 4 и 8 раз соответственно). При добавлении многопоточности такой подход может привести к конечному ускорению порядка 8 × #Ядер по сравнению с традиционными инструментами автоматического дифференцирования. Эталонная реализация доступна на GitHub.