Pytorch, 그래디언트 인수는 무엇입니까


112

나는 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 인수는 무엇입니까? 문서는 그것에 대해 명확하지 않습니다.

답변:


15

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 는 순방향 패스에서 기울기를 계산할 때 동적 계산 그래프를 생성합니다 . 이것은 나무처럼 보입니다.

따라서이 나무 의 입력 텐서 이고 루트출력 텐서 라는 소리를 자주 듣게 될 것 입니다.

그라디언트는 루트에서 잎까지 그래프를 추적하고 체인 규칙을 사용하는 방식으로 모든 그라디언트를 곱하여 계산됩니다 . 이 곱셈은 역방향 패스에서 발생합니다.


좋은 대답입니다! 그러나 나는 Pytorch가 수치 미분을한다고 생각하지 않습니다. ")-자동 분화가 가능하다고 생각합니다.
max_max_mir

예, 그것은 AD 또는 자동 미분을 사용합니다. 나중에이 PDF 에서와 같이 AD를 더 조사 했지만이 답변을 설정했을 때 잘 알지 못했습니다.
prosti

예를 들어 예제 2는 RuntimeError : Mismatch in shape를 제공합니다. grad_output [0]은 torch.Size ([3])의 모양을 가지고 output [0]은 torch.Size ([])의 모양을 가지고 있습니다.
Andreas K.

@AndreasK., 당신 말이 맞았습니다. PyTorch는 최근에 크기가 0 인 텐서를 도입 했으며 이것은 이전 예제에 영향을 미쳤습니다. 이 예제는 중요하지 않았기 때문에 제거되었습니다.
prosti

100

설명

신경망의 경우 일반적으로 loss네트워크가 입력 이미지 (또는 기타 작업)를 분류하는 방법을 얼마나 잘 학습했는지 평가하는 데 사용 합니다. 이 loss용어는 일반적으로 스칼라 값입니다. 네트워크의 매개 변수를 업데이트하려면 loss실제로 leaf node계산 그래프에있는 매개 변수에 대한 wrt 의 기울기를 계산해야합니다 (그런데 이러한 매개 변수는 대부분 Convolution, Linear 및 곧).

체인 규칙에 따르면, losswrt의 잎 노드에 대한 기울기를 계산하기 위해 , 우리는 loss어떤 중간 변수에 대한 wrt의 도함수 와 잎 변수에 대한 중간 변수 wrt의 기울기를 계산 하고 내적을 수행하고이 모든 것을 합산 할 수 있습니다.

의 메서드 의 gradient인수 리프 변수 wrt 변수의 각 요소에 대한 가중 합계계산하는 데 사용됩니다 . 이 가중치는 중간 변수의 각 요소에 대한 최종 wrt 의 파생물 일뿐 입니다.Variablebackward()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]는 losswrt에서 z_1, z_2, z_3 및 z_4 로 정확히 파생된다는 점에 유의해야합니다 . losswrt 의 도함수 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의 출력은 print3rd와 동일합니다 print.

2 2 2 2
[torch.FloatTensor of size 1x4]


1
의심의 여지가 있습니다. 왜 우리 는 손실 또는 z에 대한 그래디언트에 대해 x.grad.data 를 계산 합니까 ?
Priyank Pathak를

7
내가 뭔가를 놓쳤을지도 모르지만 공식 문서가 실제로 gradient논쟁을 더 잘 설명 할 수 있다고 생각합니다 . 답변 해 주셔서 감사합니다.
주인공

3
@jdhao "가중 벡터 [1, 1, 1, 1]losswrt z_1에서 z_2, z_3및으로 정확히 파생 된다는 점에 유의해야합니다 z_4." 이 진술이 답의 핵심이라고 생각합니다. OP의 코드를 볼 때 큰 물음표는 그라디언트에 대한 이러한 임의의 (마법) 숫자 가 어디에서 왔는지입니다. 구체적인 예에서 예를 들어 [1, 0, 0 0]텐서와 loss함수 사이의 관계를 즉시 지적하면이 예에서 값이 임의적이지 않다는 것을 알 수 있습니다.
a_guest

1
@smwikipedia, 그건 사실이 아닙니다. 우리가 확장 loss = z.sum(dim=1)하면 그것은 될 것 loss = z_1 + z_2 + z_3 + z_4입니다. 간단한 미적분을 알고 있다면 losswrt to 의 도함수 z_1, z_2, z_3, z_4[1, 1, 1, 1].
jdhao

1
사랑해. 내 의심을 해결했습니다!
Black Jack 21

45

일반적으로 계산 그래프에는 다음과 같은 스칼라 출력이 하나 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동시에 계산 하는 것을 의미합니다 .


1
"다른 손실에 가중치 또는 중요도를 추가하려면 loss.backward (torch.FloatTensor ([-0.1, 1.0, 0.0001]))을 사용할 수 있습니다." -> 이것은 사실이지만 다소 오해의 소지가 있습니다. 우리가 통과하는 주된 이유 grad_tensors는 그것들의 무게를 다르게 측정하는 것이 아니라 해당 텐서의 각 요소에 대한 그라디언트이기 때문입니다.
Aerin

27

여기서 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를 계산합니다.


5
답변 해 주셔서 감사합니다. 그러나 이것이 실제로 어떻게 유용합니까? 하드 코딩 backprop 외에 [dz / dy1, dz / dy2, dz / dy3]가 필요한 곳은 어디입니까?
hi15

제공된 그래디언트 인수가 네트워크의 후반부에서 계산 된 그래디언트라고 말하는 것이 맞습니까?
Khanetor
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.