欲速不達

일을 급히 하고자 서두르면 도리어 이루지 못한다.

Fantastic AI, Fantastic World

DS | Data Science/ML | Machine Learning

[Gradient] Autograd - .backward(gradient = external_grad)

_껀이_ 2022. 9. 28. 13:37
728x90
반응형
import torch

a = torch.tensor([2., 3.], requires_grad=True)
b = torch.tensor([6., 4.], requires_grad=True)
Q = 3*a**3 - b**2
external_grad = torch.tensor([1., 1.])
Q.backward(gradient=external_grad)

간단한 벡터값의 역전파 예시 코드이다. 일반적으로 업데이트 값이 스칼라인 경우는 Q.backward()로도 gradient가 업데이트 되지만 위와 같이 Q가 벡터 값으로 나오는 경우, Q.backward(gradient = external_grad)라고 external_grad를 명시해주어야 한다.

 

왜 그럴까??

 

여러 참고자료와 구글링을 통해 알아낸 점은 external_grad는 자기자신에 대한 미분값으로 위의 경우 Q가 2차원 벡터로 계산되기 때문에 2차원 텐서 값으로 입력해주어야 된다는 점이다.

하지만 이거 가지고는 여기서 왜 자기 자신에 대한 값을 넣어주어야 하는지에 대한 답을 얻지 못했다.

 

계속해서 찾아보던 도중 Autograd의 연산 방식에 대해서 이해를 해야 저 부분도 이해가 될 것이라는 결론에 도달하여 검색을 이어가는 도중,

Autograd는 특정 벡터와 야코비안 행렬의 연산이라는 것을 찾았다.

 

1. Jacobian Matrix : 야코비안 행렬

수학적으로, 벡터 값을 가지는 함수 y=f(x)에서 벡터 x에 대한 벡터 y의 gradient는 야코비안 행렬 J와 같다고 한다.

야코비안 행렬

Autograd는 특정 벡터와 야코비안 행렬의 곱을 계산하는 메서드이며, 주어진 어떤 벡터 v에 대하여 J.T*v를 연산한다.

 

이때, v가 스칼라 함수 l = g(y)의 gradient인 경우,

chain rule에 의해 벡터-야코비안 곱은 벡터 x 각각에 대한 I의 gradient가 된다.

Q.backward의 external_grad는 벡터 v를 의미하며, 이런 벡터-야코비안 곱의 특성이 torch.autograd 연산에서 반복적으로 쓰이면서 업데이트 된다.

 

 

이를 통해 autograd의 원리에 대해 알게 되었으나,

이것만 가지고는 external_grad가 자기자신인 것에 대한 충분한 답이 되지 못했기 때문에 계속해서 찾아보다가 hook에 관련 자료를 보던 중 유사한 설명이 있어서 정리해 보았다.

 

 

2. gradient

일반적으로 역전파 단계에서의 gradient는 이전 레이어에서 계산된 gradient를 받아 즉정한 연산을 통해 도출된 gradient 값을 말한다.

위의 이미지는 forward hook과 backward hook을 통해 각 레이어에 대한 input, ouput gradient 값을 보여준다.

 

먼저 forward pass를 보면 레이어의 output gradient가 다음 레이어의 input gradient가 되는 것을 볼 수 있다. 이는 초기에 input 데이터를 통해서 gradient를 계산하고 다음 레이어로 전파된 다음 또 연산되어 output gradient를 계산한다고 이해할 수 있다.

 

반면에, backward pass에서는 input gradient와 output gradient가 연결되지 않는 부분이 보인다. 이는 각 레이어의 구성이 조금씩 다르고 sigmoid와 같은 activation function이 있는 경우도 있기 때문에 완벽하게 일치하지 않다. 그러나 공통적으로 ,시작점이 되는 gradient인 자기자신에 대한 gradient (tensor([1.,1.]))에서 시작하여 forward pass에서 계산된 현재 레이어의 gradient와 backward pass에서 계산된 gradient를 통해 backward pass에서의 다음 레이어의 gradient를 계산하기 때문이다.

 

즉, backward pass에서 다음 레이어의 gradient를 계산하기 위해서는

backward pass에서의 현재 레이어의 gradient와

forward pass에서의 현재 레이어의 gradient를 통해 연산한다.

 

이때, 시작점에서는 backward pass에서의 현재 gradient가 없으므로 backward가 시작되는 지점에서의 gradient를 구하고, 일정한 값을 가지는 값에서 시작하기 위해 자기자신에 대한 미분값 ( = 1)을 시작점으로 계산한다.

 

import torch

a = torch.tensor([2., 3.], requires_grad=True)
b = torch.tensor([6., 4.], requires_grad=True)
Q = 3*a**3 - b**2
external_grad = torch.tensor([1., 1.])
Q.backward(gradient=external_grad)

결국 위의 코드에서는 레이어가 없는 모델의 연산과정(혹은 레이어가 1개인 경우)이라고 생각할 수 있으며, 이때는 다른 레이어에서 받아오는 gradient가 없기 때문에 시작점이라고 봐도 무방할 것으로 판단된다. 그러므로 backward gradient의 연산과정의 기준점이 되는 gradient가 필요하고 이를 external_grad = torch.tensor([1., 1.])로 작성한 것으로 보인다.

 

 

 

 

나름대로 이해를 하여 정리한 내용이지만 제대로 된 내용을 이해한 것인지는 아직도 의문이 남는다.

 

gradient의 연산과정에 대한 자세한 내용은 아래 링크에서 확인할 수 있다.

https://hongl.tistory.com/157

 

Pytorch - hook

Deep neural networks 는 마지막 단의 목적함수에 대한 파라미터 별 기울기를 통한 gradient descient 방식으로 업데이트 됩니다. 이때 편미분의 chain-rule 을 이용한 back-propagation (neural networks 의 뒷..

hongl.tistory.com

 

728x90
반응형