Автоматическая дифференциация инструментов глубокого обучения (2)

машинное обучение глубокое обучение

0x00 сводка

эта статья и вышеAutomatic Differentiation in Machine Learning: a SurveyИсходя из этого, проводится пошаговый анализ автоматического дифференцирования, основного инструмента машинного обучения.

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

  • Автоматическое вычисление и обновление градиента во время обратного распространения, также известное как автоматическое дифференцирование.
  • Вычислить с помощью графического процессора.

Эта статья подробно расскажет, что такое автоматическая дифференциация.

см. вышеАвтоматическая дифференциация инструментов глубокого обучения (1)

0x01 Обзор предыдущей ситуации

Выше мы ввели некоторые основные понятия, общие методы дифференцирования и математические основы автоматического дифференцирования.

В математике и вычислительной алгебре автоматическое дифференцирование или автоматический вывод (Automatic Differentiation, AD) также называют дифференциальным алгоритмом или числовым дифференцированием. Это способ численного расчета, его функция заключается в вычислении производной, градиента, значения матрицы Гессе и т. д. сложной функции (многослойной составной функции) в определенной точке.

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

Некоторые из наиболее часто используемых методов дифференциации заключаются в следующем:

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

Подробности следующие:

Далее мы подробно изучим автоматическое дифференцирование.

0x02 Автоматическое дифференцирование

2.1 Расчет разложения

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

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

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

Возьмем следующий пример, который является исходной формулой

y=f(g(h(x)))=f(g(h(w0)))=f(g(w1))=f(w2)=w3y=f(g(h(x)))=f(g(h(w_0)))=f(g(w_1))=f(w_2)=w_3 \\

Автоматическое дифференцирование основано на цепном правиле, сортировке некоторых частей формулы на некоторые новые переменные, а затем замене формулы в целом этими новыми переменными, поэтому мы получаем:

image.png

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

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

2.2 Расчетный режим

Как правило, существует два различных режима автоматической дифференциации:

  • Прямое автоматическое дифференцирование (также называемое тангенциальным режимом AD) или прямое накопление градиентов (прямой режим).
  • Обратное автоматическое дифференцирование (также называемое сопряженным режимом AD) или обратный кумулятивный градиент (обратный режим).

Оба режима автоматического дифференцирования находят dy/dx рекурсивно, но в разных формах в соответствии с цепным правилом.

image.png

Давайте еще немного разберемся:

Прямое автоматическое дифференцирование (тангенциальный режим AD) и обратное автоматическое дифференцирование (сопряженный режим AD) вычисляют один столбец и одну строку матрицы Якоби соответственно. (Цитируется по [Hascoet2013])

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

image.png

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

2.3 Примеры

image.png


Или добавьте символы, чтобы нам было легче понять.

После преобразования в приведенную выше структуру DAG (направленный ациклический график) мы можем легко рассчитать значение шага функции на шаг и получить производное значение каждого шага. Затем мы используем правило цепи для выражения процесса деривата DF / DX_1 в следующую форму:

Видно, что весь вывод можно разложить на ряд комбинаций дифференциальных операторов. Расчеты здесь можно разделить на два типа:

  • Подсчет этого выражения спереди назад называется прямым режимом.
  • Подсчет этого выражения сзади вперед называется обратным режимом.

2.4 Прямой режим

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

2.4.1 Процесс расчета

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

Мы даем производный анализ, и расчет значения узла выглядит следующим образом:

image.png

В этот момент мы получаем значения всех узлов графа. И при вычислении значения узла мы также вычисляем производную вместе, предполагая, что мы находим \frac{∂y}{∂x_{1}}, которую мы также вычисляем из входных данных.

image.png

Пожалуйста, обрати внимание:

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

3.4.2 Продвижение

image.png

Видно, что один столбец данных матрицы Якоби можно получить одним опережающим вычислением. Например, ​, соответствующий 3-му столбцу.

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

Но если мы хотим найти для всех входных данных, нам нужно вычислить n раз, чтобы найти все столбцы.

image.png

3.4.3 Проблемы

Преимущество прямого шаблона в том, что он прост в реализации [Revels2016] и не требует большого объема дополнительной памяти.

Проблема с прямым режимом:

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

Итак, в 1986 году Хинтон предложил обучать нейронную сеть методом обратного распространения [Rumelhart1986], который представляет собой сопряженный режим AD, о котором будет сказано далее.

3.5 Реверсивный режим

3.5.1 Идеи

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

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

Алгоритм обратного распространения нейронной сети представляет собой особую форму автоматической дифференциации в обратном режиме.

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

3.5.2 Процесс расчета

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

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


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

При расчете значений производной в обратном порядке последовательность следующая:

image.png

image.png

3.5.3 Продвижение

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

image.png

Получение матрицы Якоби:

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

3.5.4 Проблемы с памятью и операции на месте

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

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

image.png

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

Inplace относится к прямому изменению значения переменной без изменения адреса памяти переменной.

# 情景 1,不是 inplace,类似 Python 中的 `i=i+1`
a = a.exp()
# 情景 2,是 inplace 操作,类似 `i+=1`
a[0] = 10

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

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

3.6 Сравнение

Как показано на рисунке ниже, прямое автоматическое дифференцирование (тангенциальный режим AD) и обратное автоматическое дифференцирование (сопряженный режим AD) вычисляют соответственно один столбец и одну строку матрицы Якоби (цитата из [Hascoet2013]).

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

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

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

0x04 PyTorch

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

4.1 Принцип

Расчет обратного распространения PyTorch в основном реализует функцию автоматического дифференцирования через класс autograd, и в основе autograd лежат:

  • Математическая основа: составные функции, цепное правило вывода и матрица Якоби;
  • Инженерная основа: вычислительный граф, состоящий из тензора (DAG-ориентированный ациклический граф);

4.1.1 Матрица Якоби

Математически, если у вас есть вектор-функция, то градиент градиента по отношению к является матрицей Якоби J, которая содержит все следующие комбинации частных производных:

или:

Приведенная выше матрица представляет собой градиент f(X) по отношению к X. Примечание. Матрица Якоби реализует отображение n-мерных векторов в m-мерные векторы.

image.png

Давайте посмотрим на идеи PyTorch ниже.

обратная функция

На самом деле PyTorch использует обратную функцию для обратного вывода.

def backward(self, gradient=None,...)

Обратный метод в конечном итоге вызывает класс torch.autograd. В ПиТорч,torch.autogradС математической точки зрения класс представляет собой механизм вычисления векторного произведения Якоби.

факел.автоград класс
  • Если градиент является скаляром, мы можем напрямую вычислить полный якобиан. По умолчанию используется значение по умолчанию 1, то есть назад (torch.tensor (1.)).
  • Если градиент является вектором векторов. PyTorch не строит всю матрицу Якоби (фактический градиент) явно, а вычисляет ее напрямую.продукт Якоби(якобианское векторное произведение), которое обычно проще и эффективнее и требует много места для хранения реального градиента.

Обратный метод имеет 2 возможности в зависимости от параметра градиента:

image.png

4.1.2 Вычислительный граф

Как было сказано ранее, обучение выполняется в несколько этапов:

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

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

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

img

В рамках глубокого обучения базовой структурой является вычислительный граф, состоящий из тензоров.Конечно, в реальном процессе прямого распространения PyTorch явно не строит вычислительный граф, но его вычислительный путь действительно проходит по пути вычислительного графа. , в то время как обратный граф автоматически и динамически создается классом autograd во время прямого прохода.

4.1.3 Цепное правило в нейронных сетях

Давайте рассмотрим пример применения цепного правила вывода в простой модели нейронной сети, выдержки изblog.paper space.com/py torch-101…:

image.png

Тогда график прямого расчета его состава выглядит следующим образом:

Правило его вывода:

image.png

Соответствует следующему рисунку:

В pytorch процесс расчета обратного распространения аналогичен приведенному выше рисунку.

Например, если вычисляется частная производная L по w2, путь графа вычисляется следующим образом:

image.png

В процессе обратного распространения обычно используется для передачи градиента из восходящего потока, чтобы реализовать правило цепочки.

Я могу представить функцию потерь, y представляет результат, а x представляет промежуточные данные или параметры веса. Средний слой можно сравнить, каждый слой можно рассматривать как матрицу Якоби, а v может быть матрицей. Производная потерь по любому параметру может быть получена путем итерации.

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

4.2 Функции PyTorch

PyTorch имеет два метода получения.

4.2.1 Метод 1 назад

back является производной через torch.autograd.backward(). Конкретные определения следующие:

    def backward(self, gradient=None, retain_graph=None, create_graph=False, inputs=None):
        r"""
        Args:
            gradient (Tensor or None): Gradient w.r.t. the
                tensor. If it is a tensor, it will be automatically converted
                to a Tensor that does not require grad unless ``create_graph`` is True.
                None values can be specified for scalar Tensors or ones that
                don't require grad. If a None value would be acceptable then
                this argument is optional.
            retain_graph (bool, optional): If ``False``, the graph used to compute
                the grads will be freed. Note that in nearly all cases setting
                this option to True is not needed and often can be worked around
                in a much more efficient way. Defaults to the value of
                ``create_graph``.
            create_graph (bool, optional): If ``True``, graph of the derivative will
                be constructed, allowing to compute higher order derivative
                products. Defaults to ``False``.
            inputs (sequence of Tensor): Inputs w.r.t. which the gradient will be
                accumulated into ``.grad``. All other Tensors will be ignored. If not
                provided, the gradient is accumulated into all the leaf Tensors that were
                used to compute the attr::tensors. All the provided inputs must be leaf
                Tensors.
        """
        if has_torch_function_unary(self):
            return handle_torch_function(
                Tensor.backward,
                (self,),
                self,
                gradient=gradient,
                retain_graph=retain_graph,
                create_graph=create_graph,
                inputs=inputs)
        torch.autograd.backward(self, gradient, retain_graph, create_graph, inputs=inputs)


Его параметры:

  • gradient: если тензор нескалярный (т. е. его данные содержат несколько элементов) и требуется градиент, обратная функция также должна указать «градиент». Это должен быть тензор соответствующего типа и положения.
  • retain_graph: если установленоFalse, график, используемый для вычисления градиента, будет освобожден. Обычно после обратного вызова график вычислений будет автоматически уничтожен.Если вы хотите повторно вызывать обратную переменную, вам нужно установить этот параметр в значениеTrue.
  • create_graph: когда установленоTrueможет использоваться для вычисления градиентов более высокого порядка.
  • inputs: тензор, для которого необходимо вычислить градиент. Если он не указан, градиенты накапливаются по всем листовым тензорам.

Следует отметить, что эта функция предоставляет только функцию деривации и не возвращает значение.Возвращаемое значение всегда равно None.

Простой автоматический вывод

Если класс Tensor представляет скаляр (то есть содержит одноэлементный тензор), вам не нужно указывать какие-либо аргументы для reverse().

a = torch.tensor(1.0, requires_grad=True)
b = torch.tensor(2.0, requires_grad=True)
z = x**3+b
z.backward()
print(z, a.grad, b.grad)

вывод

tensor([ 8., 10., 12.])

tensor([147., 192., 243.])

Если вы используете параметр inputs, например:

torch.autograd.backward([z], inputs=[a])

Тогда он будет накапливаться только на a, а на b градиент рассчитываться не будет.

сложный автоматический вывод

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

import torch
​
x = torch.randn(2, 2, dtype=torch.double, requires_grad=True)
y = torch.randn(2, 2, dtype=torch.double, requires_grad=True)
​
def fn():
    return x ** 2 + y * x + y ** 2
​
gradient = torch.ones(2, 2)
​
torch.autograd.backward(fn(), gradient, inputs=[x, y])
​
print(x.grad)
print(y.grad)

вывод

\

tensor([[-1.0397, -2.4018],
        [-0.5114,  0.2455]], dtype=torch.float64)
tensor([[-0.9240, -2.5764],
        [-1.4938,  1.2254]], dtype=torch.float64)

4.2.2 Метод 2 степени

Второй способ — получить вывод через torch.autograd.grad(). Функция автоматически завершает процесс вывода и автоматически возвращает результат вывода для каждого аргумента. Это отличается от обратного.

Конкретные определения следующие:

def grad(
    outputs: _TensorOrTensors,
    inputs: _TensorOrTensors,
    grad_outputs: Optional[_TensorOrTensors] = None,
    retain_graph: Optional[bool] = None,
    create_graph: bool = False,
    only_inputs: bool = True,
    allow_unused: bool = False
) -> Tuple[torch.Tensor, ...]:
    r"""Computes and returns the sum of gradients of outputs with respect to
    the inputs.

    ``grad_outputs`` should be a sequence of length matching ``output``
    containing the "vector" in Jacobian-vector product, usually the pre-computed
    gradients w.r.t. each of the outputs. If an output doesn't require_grad,
    then the gradient can be ``None``).

    If ``only_inputs`` is ``True``, the function will only return a list of gradients
    w.r.t the specified inputs. If it's ``False``, then gradient w.r.t. all remaining
    leaves will still be computed, and will be accumulated into their ``.grad``
    attribute.

    .. note::

        If you run any forward ops, create ``grad_outputs``, and/or call ``grad``
        in a user-specified CUDA stream context, see
        :ref:`Stream semantics of backward passes<bwd-cuda-stream-semantics>`.

    Args:
        outputs (sequence of Tensor): outputs of the differentiated function.
        inputs (sequence of Tensor): Inputs w.r.t. which the gradient will be
            returned (and not accumulated into ``.grad``).
        grad_outputs (sequence of Tensor): The "vector" in the Jacobian-vector product.
            Usually gradients w.r.t. each output. None values can be specified for scalar
            Tensors or ones that don't require grad. If a None value would be acceptable
            for all grad_tensors, then this argument is optional. Default: None.
        retain_graph (bool, optional): If ``False``, the graph used to compute the grad
            will be freed. Note that in nearly all cases setting this option to ``True``
            is not needed and often can be worked around in a much more efficient
            way. Defaults to the value of ``create_graph``.
        create_graph (bool, optional): If ``True``, graph of the derivative will
            be constructed, allowing to compute higher order derivative products.
            Default: ``False``.
        allow_unused (bool, optional): If ``False``, specifying inputs that were not
            used when computing outputs (and therefore their grad is always zero)
            is an error. Defaults to ``False``.
    """
    outputs = (outputs,) if isinstance(outputs, torch.Tensor) else tuple(outputs)
    inputs = (inputs,) if isinstance(inputs, torch.Tensor) else tuple(inputs)
    overridable_args = outputs + inputs
    if has_torch_function(overridable_args):
        return handle_torch_function(
            grad,
            overridable_args,
            outputs,
            inputs,
            grad_outputs=grad_outputs,
            retain_graph=retain_graph,
            create_graph=create_graph,
            only_inputs=only_inputs,
            allow_unused=allow_unused,
        )

    if not only_inputs:
        warnings.warn("only_inputs argument is deprecated and is ignored now "
                      "(defaults to True). To accumulate gradient for other "
                      "parts of the graph, please use torch.autograd.backward.")

    grad_outputs_ = _tensor_or_tensors_to_tuple(grad_outputs, len(outputs))
    grad_outputs_ = _make_grads(outputs, grad_outputs_)

    if retain_graph is None:
        retain_graph = create_graph

    return Variable._execution_engine.run_backward(
        outputs, grad_outputs_, retain_graph, create_graph,
        inputs, allow_unused, accumulate_grad=False)

Параметры следующие:

  • outputs: Узел результата, вывод дифференциальной функции, то есть функция, которую необходимо дифференцировать.
  • inputs: Листовой узел, то есть возвращаемый градиент, то есть независимая переменная функции.
  • grad_outputs: Вектор в якобиан-векторном произведении.
  • retain_graph: если установленоFalse, график, используемый для вычисления градиента, будет освобожден. Обычно после обратного вызова график вычислений будет автоматически уничтожен.Если вы хотите повторно вызывать обратную переменную, вам нужно установить этот параметр в значениеTrue.
  • create_graph: когда установленоTrueможет использоваться для вычисления градиентов более высокого порядка.
  • allow_unused: по умолчаниюFalse, то есть необходимо указатьinput, если не указано, будет сообщено об ошибке.

Примеры следующие:

import torch

x = torch.randn(2, 2, requires_grad=True)
y = torch.randn(2, 2, requires_grad=True)
z = x ** 2 + y * x + y ** 2
z.backward(torch.ones(2, 2), create_graph=True)

x_grad = 2 * x + y
y_grad = x + 2 * y

grad_sum = 2 * x.grad + y.grad
x_hv = torch.autograd.grad(
    outputs=[grad_sum], grad_outputs=[torch.ones(2, 2)],
    inputs=[x], create_graph=True)

print(x.grad)
print(y.grad)

вывод

tensor([[ 2.3553, -1.9640],
        [ 0.5467, -3.1051]], grad_fn=<CopyBackwards>)
tensor([[ 1.8319, -2.8185],
        [ 0.5835, -2.5158]], grad_fn=<CopyBackwards>)

4.3 Проверка моделирования

Затем мы используем код PyTorch, чтобы грубо смоделировать процесс автоматической дифференциации.

4.3.1 Исходная версия

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

import torch
x1 = torch.tensor(2., requires_grad=True)
x2 = torch.tensor(5., requires_grad=True)
​
y = torch.log(x1) + x1 * x2 - torch.sin(x2)
grads = torch.autograd.grad(y, [x1, x2])
​
print('y is :', y)
print('grad is : ', grads[0],grads[1]) # 

Результат выглядит следующим образом, где grads[0],grads[1] — это градиенты y относительно x1 и x2 соответственно.

y is : tensor(11.6521, grad_fn=<SubBackward0>)
grad is :  tensor(5.5000) tensor(1.7163)

4.3.2 Прямой режим

Давайте взглянем на приближенное моделирование прямого режима.Представим промежуточный расчет в виде переменной, где dv1~dv4 — градиенты.

Вы можете увидеть значения переменных и значения градиента в прямом процессе, и вы можете проверить это с помощью полученного ранее процесса расчета.

import torch
x1 = torch.tensor(2., requires_grad=True)
x2 = torch.tensor(5., requires_grad=True)
​
v1 = torch.log(x1)
v2 = x1 * x2
v3 = torch.sin(x2)
v4 = v1 + v2
y = v4 - v3
​
dv1 = torch.autograd.grad(v1, [x1], retain_graph=True)
dv2 = torch.autograd.grad(v2, [x1], retain_graph=True)
dv3 = torch.autograd.grad(v3, [x2], retain_graph=True)
dv4 = torch.autograd.grad(v4, [x1, x2], retain_graph=True)
​
grads = torch.autograd.grad(y, [x1, x2])
​
print('v1 is :', v1)
print('v2 is :', v2)
print('v3 is :', v3)
print('v4 is :', v4)
print('y is :', y)
​
print('dv1 is :', dv1)
print('dv2 is :', dv2)
print('dv3 is :', dv3)
print('dv4 is :', dv4)
print('grad is : ', grads[0],grads[1])

Результат:

v1 is : tensor(0.6931, grad_fn=<LogBackward>)
v2 is : tensor(10., grad_fn=<MulBackward0>)
v3 is : tensor(-0.9589, grad_fn=<SinBackward>)
v4 is : tensor(10.6931, grad_fn=<AddBackward0>)
y is : tensor(11.6521, grad_fn=<SubBackward0>)
dv1 is : (tensor(0.5000),)
dv2 is : (tensor(5.),)
dv3 is : (tensor(0.2837),)
dv4 is : (tensor(5.5000), tensor(2.))
grad is :  tensor(5.5000) tensor(1.7163)

4.3.3 Реверсивный режим

Рассмотрим примерную симуляцию обратного режима.Представим промежуточный расчет в виде переменной, где переменная серии dv — это градиент.

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

import torch
x1 = torch.tensor(2., requires_grad=True)
x2 = torch.tensor(5., requires_grad=True)

v1 = torch.log(x1)
v2 = x1 * x2
v3 = torch.sin(x2)
v4 = v1 + v2
y = v4 - v3

dv4 = torch.autograd.grad(y, [v4], retain_graph=True)
dv3 = torch.autograd.grad(y, [v3], retain_graph=True)
dv2 = torch.autograd.grad(v4, [v2], retain_graph=True)
dv1 = torch.autograd.grad(v4, [v1], retain_graph=True)
dv0 = torch.autograd.grad(v3, [x2], retain_graph=True)
dv21 = torch.autograd.grad(v2, [x1], retain_graph=True)
dv22 = torch.autograd.grad(v2, [x2], retain_graph=True)
dv11 = torch.autograd.grad(v1, [x1], retain_graph=True)

grads = torch.autograd.grad(y, [x1, x2])

print('v1 is :', v1)
print('v2 is :', v2)
print('v3 is :', v3)
print('y is :', y)

print('dv4 is :', dv4)
print('dv3 is :', dv3)
print('dv2 is :', dv2)
print('dv1 is :', dv1)
print('dv0 is :', dv0)
print('dv21 is :', dv21)
print('dv22 is :', dv22)
print('dv11 is :', dv11)

print('grad is : ', grads[0],grads[1])

вывод

v1 is : tensor(0.6931, grad_fn=<LogBackward>)
v2 is : tensor(10., grad_fn=<MulBackward0>)
v3 is : tensor(-0.9589, grad_fn=<SinBackward>)
y is : tensor(11.6521, grad_fn=<SubBackward0>)
dv4 is : (tensor(1.),)
dv3 is : (tensor(-1.),)
dv2 is : (tensor(1.),)
dv1 is : (tensor(1.),)
dv0 is : (tensor(0.2837),)
dv21 is : (tensor(5.),)
dv22 is : (tensor(2.),)
dv11 is : (tensor(0.5000),)
grad is :  tensor(5.5000) tensor(1.7163)

0xEE Личная информация

★★★★★★Думая о жизни и технологиях★★★★★★

Публичный аккаунт WeChat:мысли Росси

ссылка 0xFF

blog.paper space.com/py torch-101…

Ян ЛеКун: Глубокое обучение мертво, да здравствует дифференцируемое программирование!

метод автоматической дифференциации

Практика дифференцируемого программирования TensorFlow 2 --- Автоматическая система символов дифференциации

Итак, Wikipedia.org/wiki/auto…

Pytorch Learning Spring 2020-1-Линейная регрессия

Автоматическая дифференциация

Введение в автоматическую дифференциацию — основные принципы тензорного потока

Говоря о тензоре и его использовании в PyTorch

[Красота глубокого обучения 01] Что такое (машинное/глубокое) обучение?

[Красота глубокого обучения 15] Как воспринимать функцию потерь?

[Красота глубокого обучения 18] Что такое градиент?

[Красота глубокого обучения 21] Подробное объяснение алгоритма BP перед распространением

[Красота глубокого обучения 22] Цепное правило алгоритма BP Подробное объяснение

[Красота глубокого обучения 23] Обратное распространение алгоритма BP Подробное объяснение

[Теория глубокого обучения] Градиентный спуск

Принцип и этапы реализации алгоритма градиентного спуска (Gradient Descent)

Методы градиентного спуска и производные

[Теория глубокого обучения] Pure Formula Hand Push + Code Rolling — обратное распространение нейронной сети + градиентный спуск

Глубокое обучение --- частный случай обратного распространения

Принцип алгоритма BP в нейронной сети и анализ исходного кода реализации Python

Машинное обучение — автоматический вывод

Automatic Differentiation in Machine Learning: a Survey, АР Вест V.org/PDF/1502.05…

AI Engine — автоматическая дифференциация

Введение в автоматическую дифференциацию