Я читаю документацию по PyTorch и нашел пример, где они пишут
gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients)
print(x.grad)
где x - начальная переменная, из которой строится y (3-вектор). Вопрос в том, что такое аргументы 0.1, 1.0 и 0.0001 тензора градиентов? В документации это не очень понятно.
Для нейронных сетей мы обычно используем лосс
, чтобы оценить, насколько хорошо сеть научилась классифицировать входное изображение (или другие задачи). Член loss
обычно является скалярным значением. Чтобы обновить параметры сети, нам нужно вычислить градиент loss
по отношению к параметрам, который фактически является листовым узлом
в графе вычислений (кстати, эти параметры в основном являются весами и смещениями различных слоев, таких как конволюция, линейный и так далее).
Согласно правилу цепочки, чтобы вычислить градиент убытка
по отношению к узлу листа, мы можем вычислить производную убытка
по отношению к некоторой промежуточной переменной, градиент промежуточной переменной по отношению к переменной листа, сделать точечное произведение и просуммировать все это.
Аргументы gradient
метода переменной
's backward()
используется для вычисления взвешенной суммы каждого элемента переменной по отношению к leaf Variable. Эти веса - просто производная конечного убытка
по отношению к каждому элементу промежуточной переменной.
Давайте рассмотрим конкретный и простой пример, чтобы понять это.
from torch.autograd import Variable
import torch
x = Variable(torch.FloatTensor([[1, 2, 3, 4]]), requires_grad=True)
z = 2*x
loss = z.sum(dim=1)
# do backward for first element of z
z.backward(torch.FloatTensor([[1, 0, 0, 0]]), retain_graph=True)
print(x.grad.data)
x.grad.data.zero_() #remove gradient in x.grad, or it will be accumulated
# do backward for second element of z
z.backward(torch.FloatTensor([[0, 1, 0, 0]]), retain_graph=True)
print(x.grad.data)
x.grad.data.zero_()
# do backward for all elements of z, with weight equal to the derivative of
# loss w.r.t z_1, z_2, z_3 and z_4
z.backward(torch.FloatTensor([[1, 1, 1, 1]]), retain_graph=True)
print(x.grad.data)
x.grad.data.zero_()
# or we can directly backprop using loss
loss.backward() # equivalent to loss.backward(torch.FloatTensor([1.0]))
print(x.grad.data)
В приведенном выше примере результат первого print
будет следующим
2 0 0 0 [torch.FloatTensor размера 1x4]
что в точности является производной z_1 по отношению к x.
Результат второго print
следующий:
0 2 0 0 [torch.FloatTensor размера 1x4]
который является производной z_2 по отношению к x.
Теперь, если использовать вес [1, 1, 1, 1, 1] для вычисления производной z по отношению к x, результат будет следующим: 1*dz_1/dx + 1*dz_2/dx + 1*dz_3/dx + 1*dz_4/dx
. Поэтому неудивительно, что вывод 3-го print
будет таким:
2 2 2 2 [torch.FloatTensor размера 1x4]
Следует отметить, что весовой вектор [1, 1, 1, 1, 1] в точности является производной потери
по отношению к z_1, z_2, z_3 и z_4. Производная убытка
по отношению к x
вычисляется как:
d(loss)/dx = d(loss)/dz_1 * dz_1/dx + d(loss)/dz_2 * dz_2/dx + d(loss)/dz_3 * dz_3/dx + d(loss)/dz_4 * dz_4/dx
Таким образом, вывод 4-го принта
будет таким же, как и 3-го принта
:
2 2 2 2 [torch.FloatTensor размера 1x4]
Как правило, ваш вычислительный граф имеет один скалярный выход loss
. Тогда вы можете вычислить градиент loss
по отношению к весам (w
) с помощью loss.backward()
. Где аргумент по умолчанию backward()
равен 1.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
.
Здесь выход forward(), т.е. y - это 3-вектор.
Три значения - это градиенты на выходе сети. Обычно они устанавливаются равными 1.0, если y - конечный выход, но могут иметь и другие значения, особенно если y является частью более крупной сети.
Например, если x - вход, y = [y1, y2, y3] - промежуточный выход, который используется для вычисления конечного выхода z,
Тогда,
dz/dx = dz/dy1 * dy1/dx + dz/dy2 * dy2/dx + dz/dy3 * dy3/dx
Итак, здесь три значения, которые нужно отбросить назад, следующие
[dz/dy1, dz/dy2, dz/dy3]
и затем функция backward() вычисляет dz/dx
оригинальный код я еще'т найти на сайте 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а + 2б*б + факел.бревно(с) и параметры тензоров с тремя элементами внутри.
Но есть и другой вариант:
Пример 2:
import torch
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)
gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients)
print(a.grad) # tensor(3.3003)
print(b.grad) # tensor(4.4004)
print(c.grad) # tensor(1.1001)
В градиенты = факел.FloatTensor([0.1, 1.0, 0.0001])
- это аккумулятор.
Следующий пример будет обеспечивать одинаковые результаты.
Пример 3:
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)
gradients = torch.FloatTensor([0.1])
y.backward(gradients,retain_graph=True)
gradients = torch.FloatTensor([1.0])
y.backward(gradients,retain_graph=True)
gradients = torch.FloatTensor([0.0001])
y.backward(gradients)
print(a.grad) # tensor(3.3003)
print(b.grad) # tensor(4.4004)
print(c.grad) # tensor(1.1001)
Как вы можете услышать расчета системы PyTorch Автоград эквивалентно Якобиана продукта.
В случае, если у вас есть функция, как это сделали мы:
y=3*a + 2*b*b + torch.log(c)
Якобиан будет [3, 4*Б, 1/с]
. Однако, это Якоби не как PyTorch делает вещи для вычисления градиентов в определенный момент.
Для предыдущей функции PyTorch бы сделать, например δy/δb
, для `Б=1 и Б=1+ε, где ε мало. Поэтому нет ничего похожего на символическую занимается математикой.
Если вы Don'т использовать градиенты в год.назад()`:
Пример 4
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.)
Вы просто получите результат в точке, основываясь на том, как вы изначально ваш А
, Б
, тензоров с
.
Будьте осторожны, как вы инициализируете свой а
, б
, с
:
Пример 5:
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])
Если вы используете факел.пустая () и Дон'т использовать
pin_memory=true`, то вы можете иметь различные результаты каждый раз.
Кроме того, градиенты, обратите внимание, как аккумуляторы так, ноль при необходимости.
Пример 6:
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 создает динамическая компьютерная графика при расчете градиентов. Это выглядит очень похоже на дерево.
Поэтому вы часто будете слышать листья дерева вклад тензоры и корень тензор выход.
Градиенты вычисляются путем отслеживания графика от корня до листьев и множатся с каждым уклон в сторону использования правила, услуги.