Исходный код я больше не нашел на сайте PyTorch.
gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients)
print(x.grad)
Проблема с приведенным выше кодом: нет функции, основанной на том, что вычислять градиенты. Это означает, что мы не знаем, сколько параметров (аргументов принимает функция) и размерность параметров.
Чтобы полностью понять это, я создал пример, близкий к оригиналу:
Пример 1:
a = torch.tensor([1.0, 2.0, 3.0], requires_grad = True)
b = torch.tensor([3.0, 4.0, 5.0], requires_grad = True)
c = torch.tensor([6.0, 7.0, 8.0], requires_grad = True)
y=3*a + 2*b*b + torch.log(c)
gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients,retain_graph=True)
print(a.grad) # tensor([3.0000e-01, 3.0000e+00, 3.0000e-04])
print(b.grad) # tensor([1.2000e+00, 1.6000e+01, 2.0000e-03])
print(c.grad) # tensor([1.6667e-02, 1.4286e-01, 1.2500e-05])
Я предположил, что наша функция - y=3*a + 2*b*b + torch.log(c)
это тензоры с тремя элементами внутри.
Вы можете думать так, как gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
будто это аккумулятор.
Как вы, возможно, слышали, расчет системы автограда PyTorch эквивалентен произведению Якоби.
Если у вас есть функция, как у нас:
y=3*a + 2*b*b + torch.log(c)
Якобианец был бы [3, 4*b, 1/c]
. Однако этот якобиан - это не то, как PyTorch вычисляет градиенты в определенной точке.
PyTorch использует в тандеме прямой проход и режим автоматического дифференцирования (AD) в обратном режиме .
Здесь нет символической математики и числового дифференцирования.
Численное дифференцирование должно быть вычислено δy/δb
для b=1
и b=1+ε
где ε мало.
Если вы не используете градиенты в y.backward()
:
Пример 2
a = torch.tensor(0.1, requires_grad = True)
b = torch.tensor(1.0, requires_grad = True)
c = torch.tensor(0.1, requires_grad = True)
y=3*a + 2*b*b + torch.log(c)
y.backward()
print(a.grad) # tensor(3.)
print(b.grad) # tensor(4.)
print(c.grad) # tensor(10.)
Вы просто получить результат в точке, основываясь на том , как вы установите a
, b
, c
тензоры изначально.
Будьте осторожны , как вы инициализации a
, b
, c
:
Пример 3:
a = torch.empty(1, requires_grad = True, pin_memory=True)
b = torch.empty(1, requires_grad = True, pin_memory=True)
c = torch.empty(1, requires_grad = True, pin_memory=True)
y=3*a + 2*b*b + torch.log(c)
gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients)
print(a.grad) # tensor([3.3003])
print(b.grad) # tensor([0.])
print(c.grad) # tensor([inf])
Если вы используете torch.empty()
и не используете, у pin_memory=True
вас могут быть разные результаты каждый раз.
Кроме того, градиенты нот похожи на аккумуляторы, поэтому при необходимости обнуляйте их.
Пример 4:
a = torch.tensor(1.0, requires_grad = True)
b = torch.tensor(1.0, requires_grad = True)
c = torch.tensor(1.0, requires_grad = True)
y=3*a + 2*b*b + torch.log(c)
y.backward(retain_graph=True)
y.backward()
print(a.grad) # tensor(6.)
print(b.grad) # tensor(8.)
print(c.grad) # tensor(2.)
И напоследок несколько советов по терминам, которые использует PyTorch:
PyTorch создает динамический вычислительный график при вычислении градиентов в прямом проходе. Это очень похоже на дерево.
Поэтому вы часто слышите, что листья этого дерева являются входными тензорами, а корень - выходными тензорами .
Градиенты вычисляются путем отслеживания графика от корня к листу и умножения каждого градиента способом с использованием правила цепочки . Это умножение происходит при обратном проходе.
объяснение
Для нейронных сетей мы обычно используем,
loss
чтобы оценить, насколько хорошо сеть научилась классифицировать входное изображение (или другие задачи).loss
Термин, как правило , скалярное значение. Чтобы обновить параметры сети, нам нужно вычислить градиент по отношениюloss
к параметрам, который на самом деле находитсяleaf node
в графике вычислений (кстати, эти параметры в основном являются весом и смещением различных слоев, таких как Свертка, Линейный и скоро).Согласно правилу цепочки, чтобы вычислить градиент по отношению
loss
к конечному узлу, мы можем вычислить производную поloss
некоторой промежуточной переменной и градиент промежуточной переменной по отношению к листовой переменной, выполнить точечное произведение и просуммировать все это.В
gradient
АргументыVariable
«Sbackward()
метод используется для вычисления взвешенной суммы каждого элемента переменной WRT с листа переменной . Этот вес является производным от final поloss
каждому элементу промежуточной переменной.Конкретный пример
Давайте рассмотрим конкретный и простой пример, чтобы понять это.
В приведенном выше примере результат первого
print
:что в точности является производной z_1 по x.
Результат второго
print
:которая является производной z_2 по x.
Теперь, если использовать вес [1, 1, 1, 1] для вычисления производной z по x, результат будет
1*dz_1/dx + 1*dz_2/dx + 1*dz_3/dx + 1*dz_4/dx
. Поэтому неудивительно, что результат 3rdprint
:Следует отметить, что вектор весов [1, 1, 1, 1] в точности является производным от
loss
z_1, z_2, z_3 и z_4. Производная по отношениюloss
кx
рассчитывается как:Таким образом, результат 4-го
print
такой же, как и 3-йprint
:источник
gradient
лучше объяснить этот аргумент. Спасибо за Ваш ответ.[1, 1, 1, 1]
именно производнаяloss
WRT кz_1
,z_2
,z_3
иz_4
.» Я думаю, что это утверждение действительно является ключом к ответу. При взгляде на код OP возникает большой вопрос: откуда берутся эти произвольные (магические) числа для градиента. В вашем конкретном примере, я думаю, было бы очень полезно сразу указать на связь между, например,[1, 0, 0 0]
тензором иloss
функцией, чтобы можно было увидеть, что значения в этом примере не произвольны.loss = z.sum(dim=1)
, он станетloss = z_1 + z_2 + z_3 + z_4
. Если вы знаете простое исчисление, вы будете знать, что производная отloss
поz_1, z_2, z_3, z_4
равна[1, 1, 1, 1]
.Обычно ваш вычислительный граф имеет один скалярный результат
loss
. Затем вы можете вычислить градиент поloss
weights (w
)loss.backward()
. Если аргумент по умолчаниюbackward()
IS1.0
.Если ваш вывод имеет несколько значений (например
loss=[loss1, loss2, loss3]
), вы можете вычислить градиенты потерь относительно весов с помощьюloss.backward(torch.FloatTensor([1.0, 1.0, 1.0]))
.Кроме того, если вы хотите добавить веса или значения к различным потерям, вы можете использовать
loss.backward(torch.FloatTensor([-0.1, 1.0, 0.0001]))
.Это означает
-0.1*d(loss1)/dw, d(loss2)/dw, 0.0001*d(loss3)/dw
одновременное вычисление .источник
grad_tensors
заключается не в том, чтобы взвесить их по-разному, но они являются градиентами по каждому элементу соответствующих тензоров.Здесь вывод forward (), т.е. y - это 3-вектор.
Три значения - это градиенты на выходе сети. Обычно они устанавливаются в 1.0, если y является окончательным выходом, но могут иметь и другие значения, особенно если y является частью более крупной сети.
Например, если x является входом, y = [y1, y2, y3] является промежуточным выходом, который используется для вычисления окончательного выхода z,
Затем,
Итак, три значения для обратного отсчета:
а затем backward () вычисляет dz / dx
источник