Пошаговый пример автоматического дифференцирования в обратном режиме


27

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

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

Пара вопросов, с которыми у меня больше всего проблем:

  • семена - зачем они вообще нужны?
  • правила обратной дифференциации - я знаю, как сделать прямую дифференциацию, но как мы пойдем назад? Например, в примере из этого раздела , откуда мы знаем, что ?w2¯=w3¯w1
  • мы работаем только с символами или передаем фактические значения ? Например, в этом же примере символы или значения являются и ?wiwi¯

«Практическое машинное обучение с Scikit-Learn & TensorFlow» Приложение D дает очень очень хорошее объяснение, на мой взгляд. Я рекомендую это.
Агустин Баррачина

Ответы:


37

Допустим, у нас есть выражение и мы хотим найти производные и . В обратном режиме AD эта задача разбивается на 2 части: прямой и обратный проходы.z=x1x2+sin(x1)dzdx1dzdx2

Прямой проход

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

w1=x1
w2=x2
w3=w1w2
w4=sin(w1)
w5=w3+w4
z=w5

Преимущество этого представления в том, что правила дифференцирования для каждого отдельного выражения уже известны. Например, мы знаем, что производная от есть , и поэтому . Мы будем использовать этот факт в обратном порядке ниже.sincosdw4dw1=cos(w1)

По сути, прямой проход состоит из оценки каждого из этих выражений и сохранения результатов. Скажем, наши входные данные: и . Тогда мы имеем:x1=2x2=3

w1=x1=2
w2=x2=3
w3=w1w2=6
w4=sin(w1) =0.9
w5=w3+w4=6.9
z=w5=6.9

Обратный проход

Это волшебство начинается, и оно начинается с правила цепочки . В своей основной форме правило цепочки утверждает, что если у вас есть переменная которая зависит от которая, в свою очередь, зависит от , то:t(u(v))uv

dtdv=dtdududv

или, если зависит от через несколько путей / переменных , например:tvui

u1=f(v)
u2=g(v)
t=h(u1,u2)

тогда (см. доказательство здесь ):

dtdv=idtduiduidv

В терминах графа выражений, если у нас есть конечный узел и входные узлы , а путь от до проходит через промежуточные узлы (т.е. где ), мы можем найти производную какzwizwiwpz=g(wp)wp=f(wi)dzdwi

dzdwi=pparents(i)dzdwpdwpdwi

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

Обратный проход начинается в конце (то есть ) и распространяется обратно на все зависимости. Здесь мы имеем (выражение для «семян»):dZdZ

dZdZзнак равно1

Это может быть прочитано как «изменение в приводит к точно такому же изменению в », что совершенно очевидно.ZZ

Тогда мы знаем, что и так:Zзнак равновес5

dZdвес5знак равно1

вес5 линейно зависит от и , поэтому и . Используя правило цепочки, мы находим:вес3вес4dвес5dвес3знак равно1dвес5dвес4знак равно1

dZdвес3знак равноdZdвес5dвес5dвес3знак равно1×1знак равно1
dZdвес4знак равноdZdвес5dвес5dвес4знак равно1×1знак равно1

Из определения и правил частных производных мы находим, что . Таким образом:вес3знак равновес1вес2dвес3dвес2знак равновес1

dZdвес2знак равноdZdвес3dвес3dвес2знак равно1×вес1знак равновес1

Который, как мы уже знаем из прямого прохода, является:

dZdвес2знак равновес1знак равно2

Наконец, вносит вклад в через и . Еще раз, из правил частных производных мы знаем, что и . Таким образом:вес1Zвес3вес4dвес3dвес1знак равновес2dвес4dвес1знак равносоз(вес1)

dZdвес1знак равноdZdвес3dвес3dвес1+dZdвес4dвес4dвес1знак равновес2+соз(вес1)

И снова, учитывая известные входные данные, мы можем рассчитать это:

dZdвес1знак равновес2+соз(вес1)знак равно3+соз(2) знак равно2,58

Поскольку и являются просто псевдонимами для и , мы получаем наш ответ:вес1вес2Икс1Икс2

dZdИкс1знак равно2,58
dZdИкс2знак равно2

Вот и все!


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

  1. Производные могут иметь гораздо более высокую размерность, чем входные или выходные данные, например, производная вектора от вектора является матрицей, а производная матрицы от матрицы - это 4-мерный массив (иногда его называют тензором). Во многих случаях такие производные очень редки.
  2. Каждый компонент в выходном массиве является независимой функцией от 1 или более компонентов входного массива (ов). Например, если и оба и являются векторами, никогда не зависит от , а только от подмножества . В частности, это означает, что поиск производной сводится к отслеживанию зависимости от .Yзнак равное(Икс)ИксYYяYJИксКdYяdИксJYяИксJ

Сила автоматического дифференцирования заключается в том, что он может работать со сложными структурами из языков программирования, такими как условия и циклы. Однако, если все, что вам нужно, это алгебраические выражения, и у вас есть достаточно хорошая структура для работы с символическими представлениями, можно построить полностью символические выражения. Фактически, в этом примере мы могли бы создать выражение и вычислить эту производную для любых входных данных, которые мы хотим.dZdвес1знак равновес2+соз(вес1)знак равноИкс2+соз(Икс1)


1
Очень полезный вопрос / ответ. Спасибо. Просто небольшая критика: вы, кажется, двигаетесь по древовидной структуре без объяснений (вот когда вы начинаете говорить о родителях и т. Д.)
MadHatter,

1
Также не помешает прояснить, зачем нам семена.
MadHatter

@MadHatter спасибо за комментарий. Я попытался перефразировать пару абзацев (те, которые относятся к родителям), чтобы подчеркнуть структуру графа. Я также добавил «семя» к тексту, хотя само это имя, по моему мнению, может вводить в заблуждение: в AD семя всегда является фиксированным выражением - , а не тем, что вы можете выбрать или сгенерировать. dZdZзнак равно1
друг

Благодарность! Я заметил, что когда нужно установить более одного «семени», обычно выбирают 1 и 0. Я хотел бы знать, почему. Я имею в виду, что каждый берет «фактор» дифференциала по отношению к самому себе, так что «1», по крайней мере, интуитивно оправдано. Но как насчет 0? А что, если нужно собрать более 2 семян?
MadHatter

1
Насколько я понимаю, более одного семени используется только в прямом режиме AD. В этом случае вы устанавливаете начальное значение в 1 для входной переменной, которую вы хотите дифференцировать, и устанавливаете начальное значение в 0 для всех остальных входных переменных, чтобы они не влияли на выходное значение. В обратном режиме вы устанавливаете начальное значение для выходной переменной, и у вас обычно есть только одна выходная переменная. Я предполагаю, что вы можете построить конвейер AD обратного режима с несколькими выходными переменными и установить для всех них, кроме одной, 0, чтобы получить тот же эффект, что и в прямом режиме, но я никогда не исследовал эту опцию.
друг
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.