답변:
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)
과 같이 가정 했고 매개 변수는 내부에 3 개의 요소가있는 텐서입니다.
gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
이것이 누산기 라고 생각할 수 있습니다 .
PyTorch autograd 시스템 계산은 Jacobian 곱과 동일합니다.
우리가 한 것처럼 함수가있는 경우 :
y=3*a + 2*b*b + torch.log(c)
Jacobian은 [3, 4*b, 1/c]
. 그러나이 Jacobian 은 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
매번 다른 결과가 나타날 수 있습니다.
또한 음표 그래디언트는 누산기와 같으므로 필요할 때 0으로 만듭니다.
예 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
계산 그래프에있는 매개 변수에 대한 wrt 의 기울기를 계산해야합니다 (그런데 이러한 매개 변수는 대부분 Convolution, Linear 및 곧).
체인 규칙에 따르면, loss
wrt의 잎 노드에 대한 기울기를 계산하기 위해 , 우리는 loss
어떤 중간 변수에 대한 wrt의 도함수 와 잎 변수에 대한 중간 변수 wrt의 기울기를 계산 하고 내적을 수행하고이 모든 것을 합산 할 수 있습니다.
의 메서드 의 gradient
인수 는 리프 변수 wrt 변수의 각 요소에 대한 가중 합계 를 계산하는 데 사용됩니다 . 이 가중치는 중간 변수의 각 요소에 대한 최종 wrt 의 파생물 일뿐 입니다.Variable
backward()
loss
이것을 이해하기 위해 구체적이고 간단한 예를 들어 봅시다.
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)
위의 예에서 first의 결과 print
는 다음과 같습니다.
2 0 0 0
[torch.FloatTensor of size 1x4]
이것은 정확히 z_1 wrt를 x로 미분 한 것입니다.
두 번째 결과 print
는 다음과 같습니다.
0 2 0 0
[torch.FloatTensor of size 1x4]
이것은 z_2 wrt에서 x 로의 미분입니다.
이제 [1, 1, 1, 1]의 가중치를 사용하여 z wrt와 x의 미분을 계산하면 결과는 1*dz_1/dx + 1*dz_2/dx + 1*dz_3/dx + 1*dz_4/dx
입니다. 따라서 3rd의 출력 print
은 다음과 같습니다.
2 2 2 2
[torch.FloatTensor of size 1x4]
가중치 벡터 [1, 1, 1, 1]는 loss
wrt에서 z_1, z_2, z_3 및 z_4 로 정확히 파생된다는 점에 유의해야합니다 . loss
wrt 의 도함수 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
따라서 4th의 출력은 print
3rd와 동일합니다 print
.
2 2 2 2
[torch.FloatTensor of size 1x4]
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
wrt to 의 도함수 z_1, z_2, z_3, z_4
는 [1, 1, 1, 1]
.
일반적으로 계산 그래프에는 다음과 같은 스칼라 출력이 하나 loss
있습니다. 그런 다음 loss
가중치 ( w
)에 의해 wrt의 기울기를 계산할 수 있습니다 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
동시에 계산 하는 것을 의미합니다 .
grad_tensors
는 그것들의 무게를 다르게 측정하는 것이 아니라 해당 텐서의 각 요소에 대한 그라디언트이기 때문입니다.
여기서 forward ()의 출력, 즉 y는 3- 벡터입니다.
세 가지 값은 네트워크 출력의 기울기입니다. 일반적으로 y가 최종 출력 인 경우 1.0으로 설정되지만 특히 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를 계산합니다.