2. Autograd-자동미분
Autograd 패키지에 대해 알아보겠습니다😃
PyTorch의 모든 신경망의 중심에는 autograd 패키지가 있다. 먼저 이것을 가볍게 살펴본 뒤, 첫 번째 신경망을 학습시켜보겠다.(주의! 가볍지 않을 수 있음😑)
autograd패키지는 Tensor의 모든 연산에 대해 자동 미분을 제공한다. 이는 실행-기반-정의(define-by-run)프레임워크로, 코드를 어떻게 작성하여 실행하느냐에 따라 역전파가 정의된다는 뜻이며, 역전파는 학습 과정의 매 단계마다 달라진다.
😃 더 간단한 용어로 몇 가지 예를 살펴보자.
Tensor
📝Note
Tensor 클래스
-
패키지의 중심에는 torch.Tensor 클래스가 있다. 만약 .requires_grad속성을 True로 설정하면, 그 tensor에서 이뤄진 모든 연산들을 추적(track)하기 시작한다.
-
.requires_grad=True
<- 뒤에서 많이 보게 될 것이다.계산이 완료된 후 .backward()를 호출하여 모든 변화도(gradient)를 자동으로 계산할 수 있다. 이 Tensor의 변화도는 .grad 속성에 누적된다.
-
-
Tensor가 기록을 추적하는 것을 중단하려면, .detach()를 호출하여 연산 기록으로부터 분리(detach)하여 이후 연산들이 추적되는 것을 방지할 수 있다.
-
기록을 추적하는 것과 메모리를 사용하는 것을 방지하기 위해, 코드 블럭을 with torch.no_grad():로 감쌀 수 있다. 특히 변화도(gradient)는 필요 없지만, requires_grad=True가 설정되어 학습 가능한 매개변수를 갖는 모델을 평가(evaluate)할 때 유용하다.
Function 클래스
-
Autograd 구현에서 매우 중요한 클래스가 하나 더 있다. 바로 Function 클래스이다.
-
Tensor와 Function은 서로 연결되어 있으며, 모든 연산 과정을 부호화(encode)하여 순환하지 않는 그래프(acyclic graph)를 생성한다. 각 tensor는 .grad_fn속성을 갖고 있는데, 이는 Tensor를 생성한 Function을 참조하고 있다.(단, 사용자가 만든 Tensor는 예외로, 이 때 grad_fn은 None)이다.
🤨 이 말은 도통 설명만 들어서 모르겠군... -
도함수를 계산하기 위해서는 Tensor의 .backward()를 호출하면 된다. 만약 Tensor가 스칼라(scalar)인 경우(예. 하나의 요소 값만 갖는 등) 에는 인자를 정해줄 필요가 없다. 하지만 여러 개의 요소를 갖고 있을 때는 tensor의 모양을 gradient의 인자로 지정할 필요가 있다.
Tutorial
👨🏻💻 Tutorial Start!
import torch
torch를 생성하고 requires_grad=True를 설정하여 연산을 기록한다.
x = torch.ones(2, 2, requires_grad=True) # 요소값이 1인 2x2행렬을 선언한다.
print(x)
tensor([[1., 1.],
[1., 1.]], requires_grad=True)
tensor에 연산을 수행한다. requires_grad=True를 설정했기 때문에 연산을 기록한다.
y = x + 2
print(y)
tensor([[3., 3.],
[3., 3.]], grad_fn=<AddBackward0>)
y는 연산의 결과로 생성된 것이므로 grad_fn을 갖는다
grad_fn이 생성된다.
print(y.grad_fn)
<AddBackward0 object at 0x000001EBCC2EC208>
y에 다른 연산을 수행한다.
z = y * y * 3
out = z.mean()
print(z, out)
tensor([[27., 27.],
[27., 27.]], grad_fn=<MulBackward0>) tensor(27., grad_fn=<MeanBackward0>)
.requires_grad(...)
등장
.requires_grad(...)는 기존 Tensor의 requires_grad 값을 바꿔치기 (in-place)하여 변경한다. 입력값이 지정되지 않으면 기본값은 False이다.
🤷🏻♂️ 뭔 말이냐... 20.02.19.Wed pm4:43
아래의 예제를 통해 살펴보자 pm6:52
a = torch.randn(2, 2) # standard normal distribution (2, 2)
a = ((a * 3) / (a - 1))
print(a.requires_grad)
a.requires_grad_(True) # .requires_grad(...)는 기존 Tensor의 requires_grad 값을 바꿔치기(in-place)하여 변경한다.
print(a.requires_grad)
b = (a * a).sum()
print(b.grad_fn) # a.requires_grad_(True)로 변경해줬기 떄문에 이제 추적이 되는거다.
False
True
<SumBackward0 object at 0x000001EBCC32D908>
🤔 Q1. requires_grad(...)함수가 False일 때와 True일 때, 무엇이 다른 걸까?
🤔 Q2. grad_fn은 뭔가...?
✍️ A1. requires_grad=True를 설정하면 연산을 기록할 수 있다. True일 때, 연산을 기록할 수 있다면 False일 때는 연산을 기록할 수 없다는 것이겠지!
✍️ A2. requires_grad=True로 설정한 변수로 다른 연산을 할 경우 생성되는 것 같다.(확실하지는 않다.)
Reference
🙆♂️ Updata(20.02.20.Thur) .requires_grad=True Tensor로 계산한 Funcion을 참조하는 것이다
https://pytorch.org/tutorials/beginner/former_torchies/autograd_tutorial.html
AUTOGRAD
Autograd is now core torch package for automatic differenciation. It uses a tape based system for automatic differenciation.
In the forward phase, the autograd tape will remember all the operations it executed, and in the backward phase, it will replay the operations.
Each variable has a .grad_fn attribute that references a function that has created a function (except for Tensors created by the user - these have None as .grad_fn).
분명 한국어를 더 잘하는데... 영어로 받아들이는 게 더 잘 이해가 되다니...!
"각 변수에는 함수를 생성한 함수를 참조하는 .grad_fn 속성이 있다."
결국 이것도 forward와 backward에서 계산을 추적하고 기억하기 위함이 아닐까
Gradient(변화도)
이제 역전파(backprop)를 해보자. out은 하나의 스칼라 값만 갖고 있기 때문에, out.backward()는 out.backward(torch.tensor(1.))과 동일하다.
out.backward()
변화도 d(out)/dx를 출력한다.
해석:: x값이 아주 조금 증가했을 때의 변화량. 즉, 기울기
(x는 위에서 이미 선언했다)
print(x.grad)
tensor([[4.5000, 4.5000],
[4.5000, 4.5000]])
🤨 아... 지난번에 했을 때, 여기서 막혔던 기억이...
😃 밑러닝1을 한 번 독파하고 왔기에 패기 있게 밀고 나간다 -20.02.19.wed pm7:10-
일반적으로, torch.autograd는 벡터-야코비안 곱을 계산하는 엔진이다.(Jocobian Matrix란 수학적으로 i에 대한 y의 변화도를 나타낸다.)
즉, 어떤 벡터 v=(v1 v2⋯vm) T에 대해 vT⋅J을 연산합니다. 만약 v 가 스칼라 함수 l=g(y⃗ )의 기울기인 경우, v=(∂l∂y1⋯∂l∂ym) T이며, 연쇄 법칙(chain rule)에 따라 벡터-야코 비안 곱은 x⃗ 에 대한 l의 기울기가 됩니다:
🤨 말이 참 어렵죠?
😃 밑러닝 수치 미분 편을 참고하시면 좋을 것 같습니다 (곧 링크를 달아드리겠습니다)
벡터-야코 비안 곱의 이러한 특성은 스칼라가 아닌 출력을 갖는 모델에 외부 변화도를 제공(feed)하는 것을 매우 편리하게 해준다. (파라미터에 대한 기울기를 구할 때, 매우 유용합니다. 뒤에서 다루겠죠...!)
이제 벡터-야코비안 곱의 예제를 살펴보자.
# torch x 선언
x = torch.randn(3, requires_grad=True) # 연산을 기록한다.
# set requires_grad=True to track computation with it
y = x * 2
while y.data.norm() < 1000:
y = y * 2
print(y)
tensor([-600.2968, 90.4080, 872.5602], grad_fn=<MulBackward0>)
이 경우 y는 더 이상 스칼라 값이 아니다. torch.autograd는 전체 야코 비안을 직접 계산할 수는 없지만, 벡터-야코 비안 곱은 간단히 backward에 해당 벡터를 인자로 제공하여 얻을 수 있다.
v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
y.backward(v) # 벡터 야코비안 곱을 통해 간단히 backward 벡터값을 얻을 수 있다.
# 밑바닥부터 gradient를 구현해보면 이게 왜 혁명인지 알 수 있을 것이다.
print(x.grad)
vi에 대한 y의 기울기를 구할 수 있게 되었다! 매우 간단하게!
tensor([1.0240e+02, 1.0240e+03, 1.0240e-01])
또한 with torch.no_grad():로 코드 블럭을 감싸서 autograd가 .requires_grad=True인 Tensor들의 연산 기록을 추적하는 것을 멈출 수 있다.
print(x.requires_grad)
print((x**2).requires_grad)
with torch.no_grad():
print((x**2).requires_grad)
True
True
False
.requires_grad=True였던 x가 torch.no_grad()코드 블럭으로 인해 .requires_grad=Fale가 되었다.
Tensor들의 연산 기록 추적이 멈치 게 되었다.
Reference:
- 한글: https://tutorials.pytorch.kr/beginner/blitz/autograd_tutorial.html
- 영문: https://pytorch.org/tutorials/beginner/former_torchies/autograd_tutorial.html
한글판과 영문판의 내용이 조금 다르다... 이래서 영어를 해야 한다는...!
'Deep Learning > 밑바닥부터 시작하는 딥러닝' 카테고리의 다른 글
[정리노트] AutoEncoder의 모든것 Chap1. Deep Neural Network의 학습 방법에 대해 알아보자(딥러닝 학습방법) (7) | 2020.07.11 |
---|---|
[Deep Learning] Bottleneck 현상이란 무엇인가? (0) | 2020.05.08 |
[Deep Learning] NVIDIA CUDA란 무엇인가? (1) | 2020.05.08 |
파이토치(PyTorch)-3.Neural Network 구현하기 (0) | 2020.02.21 |
PyTorch를 공부하며 기록하는 모든것 (0) | 2020.02.19 |
댓글