Autograd 사용하기.
1. requires_grad
를 True
로 설정.
편미분을 구할 Tensor 인스턴스에 대해 requires_grad=True
가 되도록하여,
해당 Tensor 인스턴스에 대한 연산을 추적하고, 자동으로 gradient를 계산할 수 있도록 수행.
x = torch.tensor([2.0], requires_grad=True)
autograd와 관련된 attributes는 다음과 같음.
x.requires_grad
:- grad 를 구하기위해 forward propagation에서
- gradient를 구하기 위해 필요한 연산 및 중간값을 추적하여 기록할지 여부를 나타냄
x.is_leaf
:- 계산 그래프에서 “leaf node"인지 여부를 나타냄.
- leaf node는 다른 tensor를 기반으로하는 어떤 연산을 통해 생성되지 않은 tensor로서 직접적으로 생성한 것들임.
- 만약 tensor 가 다른 tensor로부터 어떤 연산을 통해 생성되었다면,
is_leaf
속성은False
임.
x.grad_fn
:- 텐서가 다른 텐서들의 연산을 통해 생성된 경우(=leaf node가 아닌 경우),
grad_fn
은 해당 연산을 나타내며,- 이 정보를 통해 back-propagation 에서 gradient를 계산할 때 어떤 연산을 통해 편미분을 계산하는지 알 수 있음.
x.grad
:- tensor 인스턴스
x
에 대해 수행된 마지막.backward()
연산으로부터 계산된 gradient가 저장됨. x
의requires_grad = True
일 때,- PyTorch는
x
에 대한 모든 연산을 추적하며 .backward()
가 호출될 때 자동으로 gradient를 계산하여x.grad
에 저장
- PyTorch는
- 만약
x
가 scalar가 아닌 경우,.backward()
호출 시 gradient의 shape를 지정하는 argument를 전달해야함
(.backward()
를 호출하는 tensor와 같은 shape 이며 각 요소로 scalar 1을 가지는 tensor 인스턴스가 주로 argument로 사용됨.)
- tensor 인스턴스
2. 앞서 tensor 인스턴스에 대한 연산을 기재.
# 간단한 연산으로 구성한 예. 실제 구현에서는 매우 복잡해질 것임.
a = 2*x +10
b = 3*a
b.retain_grad()
c = 2*b
- 1번 과정에서
requires_grad=True
인 tensor 인스턴스에 연결된 모든 tensor 인스턴스도 다requires_grad=True
임. - 맨 처음
requires_grad=True
로 지정한 tensor 인스턴스는is_leaf
는 True로 설정되고, 이후 해당 인스턴스에 연결되는 Tensor들은is_leaf
가 False 임. - 이들 연결된 tensor들의
grad_fn
에Backward function
이 할당됨
leaf node 가 아닌
다른 tensor 와 연산으로 만들어진 tensor 인스턴스에 대해
Gradient를 구하고 싶을때는
해당 tensor 인스턴스의 메서드.retain_grad()
를.backward()
호출 전에 호출하면 됨:
이를 통해 해당 tensor 인스턴스의 gradient를 "유지"하게 됨.
3. 결과값의 tensor 부터 back-propagation 수행
backpropagation이 시작되는 loss function 의 결과 Tensor 인스턴스 ( b
)에서backward()
메서드를 수행하여 gradient를 계산하고,
computational graph의 leaf node의 attribute 인 .grad
에 계산된 gradient를 accumulate 시킴.
.backward()
Computes the sum of gradients of given tensors
with respect to graph leaves.
c.backward( torch.ones(c.shape) )
# 아니면,
# l = torch.sum(b)
# l.backward()
torch.autograd
provides classes and functions
implementing automatic differentiation of arbitrary scalar valued functions.
때문에
.backward()
가 호출될 때 넘겨지는 argument는
"호출에 사용되는 tensor 인스턴스"와 같은 shape이면서 값은 scalar 1을 가지는 tensor 인스턴스 이어야 함.
아니면.backward()
메서드를 호출하는 tensor 인스턴스가 scalar 여야 한다.
argument로 retain_graph=True
를 넘겨줄 경우, .backward()
를 여러차례 호출이 가능함.
- 해당 argument를 True로 설정하지 않으면,
.backward()
가 gradient를 계산하면서, forward phase에서 구해진 intermediate value들을 모두 free 시켜서- 이후 forward phase 수행 없이 곧바로 다시
.backward()
를 호출할 경우 runtime error가 발생함.
.backward()
를 여러차례 호출할 경우, leaf node의 grad
에 계산된 gradient가 accumulated 되는 것을 쉽게 확인 가능함.
(모델의 파라메터를 제대로 갱신하려면, 꼭 leaf node의 grad
의 초기화가 필요함.)
4. 이후, gradient를 다음으로 확인 가능.
# gradient를 출력.
print(x.grad)
참고로, DL등에서 다음 backward()
를 수행하기 전에 꼭 구한 gradient를 초기화해야함 (아래 예제 참조).
x.grad.zeros_() # leaf node x의 grad를 0으로 초기화 하는 in-place op.
- 이 초기화를 하지 않을 경우, grad에 gradient 값이 누적이 되어
- 제대로 된 gradient를 구하지 못하게 됨.
초기화 관련하여 좀 더 자세히
일반적으로,
Training 과정 중 "한 번의 iteration (단일 batch에 대해)"이 끝난 후에는 grad 를 0으로 초기화해야 함.
- grad 를 초기화하지 않으면, 각 batch에 대한 grad가 이전 batch의 grad에 더해져 누적됨.
- 이후 (gradient decent 기반의 ) optimizer를 통해 누적된 grad 를 기반으로 model의 parameters가 update되며,
- 이는 잘못된 parameter update를 의미하고, 결국 제대로 학습이 이루어지지 못하는 결과로 이어짐.
즉, 한 epoch가 여러 batch의 iteration으로 이루어질 때,
- 각 배치 처리(=하나의 iteration) 후에 구해진 grad 들을 0으로 초기화하지 않으면
- 첫 번째 batch 에 대한 grad 계산이 두 번째 batch 의 grad 에 영향을 주고,
- 이는 모든 이어지는 batch들에 마찬가지로 영향을 줌.
- 결국 한 epoch에서의 gradient가 제대로 반영되지 못하게 된다.
이를 막기위해 대부분의 training loop 에서는
- 적절한 위치에
x.grad.zero_()
(또는optimizer.zero_grad()
)를 호출하여 - gradient를 초기화함.
다음은 이를 보여주는 예제 코드임. :
for data, target in dataset:
optimizer.zero_grad() # model의 모든 parameters의 grad를 0으로 초기화
output = model(data)
loss = loss_function(output, target)
loss.backward() # back-propagation을 통해 gradient 계산
optimizer.step() # 계산된 gradient를 통해 model의 parameters 갱신 (or 수정).
다음의 ipynb 는 PyTorch에서 AutoGrad를 쓰는 간단한 예제를 보여줌.
https://gist.github.com/dsaint31x/5642ae694a4bb1ec5887fc3642b7d91d
기타 참고자료.
https://teamdable.github.io/techblog/PyTorch-Autograd: 꼭 읽어볼 것.
https://velog.io/@olxtar/PyTorch-Autograd-03-Practice02
'Python' 카테고리의 다른 글
[Python] Enum (열거형, Enumeration Type) (0) | 2024.03.24 |
---|---|
[OOP] Example: MObject, Point, and Class (0) | 2024.03.23 |
[DL] PyTorch: view, data, and detach (0) | 2024.03.22 |
[DL] PyTorch: TPU사용하기 (0) | 2024.03.21 |
[DL] Storage: PyTorch 텐서를 위한 메모리 관리 (0) | 2024.03.21 |