뉴비에욤
PyTorch 튜토리얼 2 - Autograd : 자동 미분 본문
2017/07/13 - [Machine Learning/PyTorch] - 윈도우 10 PyTorch 환경 구성 - 설치
2018/04/30 - [Machine Learning/PyTorch] - PyTorch 튜토리얼 1 - PyTorch란?
원문 : http://pytorch.org/tutorials/beginner/blitz/autograd_tutorial.html
* 17년도 07월에 초번 번역 후, 파이토치 최신 버전인 0.4.0에 맞추어 갱신 된 번역본
* 최근 갱신일 : 2018-04-30
목 차 ( 초보자 튜토리얼 )
A. 파이토치와 함께하는 딥러닝 : 60분만에 끝장내기!
2. Autograd : 자동 미분
- 텐서
- 그라디언트
Autograd : 자동 미분(Automatic Differentiation)
PyTorch에서 뉴럴 네트워크의 중심은 autograd 라는 패키지라 할 수 있다. 일단 간단히 살펴 본 후에 첫 번째 뉴럴 네트워크 학습을 진행할 것이다.
autograd 패키지는 텐서의 모든 연산에 대하여 자동 미분을 제공한다. 이 패키지는 실행 시점에 정의되는(define-by-run) 프레임워크인데 다시 말하면 코드가 어떻게 실행되는지에 따라 역전파(backprop)가 정의되며, 각각의 반복마다 역전파가 달라질 수 있다는 것이다. 1
텐서플로우는 정의 된 다음 실행되는(defined-and-run) 프레임워크인데 이는 그래프 구조에서 미리 조건과 반복을 정의하고 나서 실행 된다.
PyTorch를 비롯한 Chainer, DyNet 등은 define-by-run 프레임워크이다.
몇몇의 예제를 통해 좀 더 쉽게 설명하자면 다음과 같다.
텐서 ( Tensors )
torch.Tensor는 패키지에서 가장 중심이 되는 클래스이다. 텐서의 속성 중 하나인 .requires_grad를 True로 세팅하면, 텐서의 모든 연산에 대하여 추적을 시작한다. 계싼 작업이 모두 수행 되었다면 .backward()를 호출하여 모든 그라디언트들을 자동으로 계산할 수 있다. 이 텐서를 위한 그라디언트는 .grad 속성에 누적되어 저장 된다.
텐서에 대하여 기록(history) 추적을 중지하려면, .detach()를 호출하여 현재의 계산 기록으로 부터 분리시키고 이후에 일어나는 계산들은 추적되지 않게 할 수 있다.
기록 추적(및 메모리 사용)에 대하여 방지를 하려면, 코드 블럭을 with torch.no_grad():로 래핑 할(wrap)수 있다. 이는 특히 모델을 평가할 때 엄청난 도움이 되는데, 왜냐하면 모델은 requires_grad=True 속성이 적용 된 학습 가능한 파라미터를 가지고 있을 수 있으나 우리는 그라디언트가 필요하지 않기 때문이다.
자동 미분을 위해 매우 중요한 클래스가 하나 더 있는데 바로 Function 이다.
Tensor와 Function은 상호 연결되어 있으며, 비순환(비주기) 그래프를 생성하는데, 이 그래프는 계산 기록 전체에 대하여 인코딩을 수행한다. 각 변수는 Tensor를 생성한 Function을 참조하는 .grad_fn 속성을 가지고 있다. (단, 사용자에 의해 생성 된 텐서는 제외한다-해당 텐서들은 grad_fn 자체가 None 상태이다)
만약 도함수(derivatives)들을 계산하고 싶다면, Tensor의 .backward()를 호출하면 된다. 만약 Tensor가 스칼라(i.e. 한 개의 요소(element)를 가지고 있는) 형태라면, backward() 사용에 있어 그 어떠한 파라미터도 필요하지 않는다. 그러나 한 개 이상의 요소를 가지고 있다면 올바른 모양(matching shape)의 텐서인 gradient 파라미터를 명시할 필요가 있다.
파이토치 패키지 임포트
- 소스
import torch
텐서를 생성하고 requires_grad=True로 세팅하여 계산 추적한다.
- 소스
x = torch.ones(2, 2, requires_grad=True)
print(x)
- 결과
tensor([[ 1., 1.],
[ 1., 1.]])
텐서에 대하여 임의의 연산을 수행한다.
- 소스
y = x + 2
print(y)
- 결과
tensor([[ 3., 3.],
[ 3., 3.]])
y는 연산의 결과로써 생성된 것이기 때문에 grad_fn을 가지고 있다.
- 소스
print(y.grad_fn)
- 결과
<AddBackward0 object at 0x7fb9f73ea518>
y에 대하여 임의의 연산을 수행한다.
- 소스
z = y * y * 3
out = z.mean()
print(z, out)
- 결과
tensor([[ 27., 27.],
[ 27., 27.]]) tensor(27.)
.requires_grad_( ... )은 이미 존재하는 텐서의 requires_grad 플래그를 제자리(in-place)에서 변경한다. 입력 플래그를 명시하지 않은 경우 기본적으로 True가 된다.
- 소스
a = torch.randn(2, 2)
a = ((a * 3) / (a - 1))
print(a.requires_grad)
a.requires_grad_(True)
print(a.requires_grad)
b = (a * a).sum()
print(b.grad_fn)
- 결과
False
True
< SumBackward0 object at 0x7fb9f74f1a58 >
그라디언트 ( Gradients )
자, 다시 돌아와서 이제 out은 하나의 스칼라(single scalar) 값을 가지고 있기 때문에 out.backward()는 out.backward(torch.tensor(1))와 동등한 결과를 리턴한다.
- 소스
out.backward()
- 소스
print(x.grad)
- 결과 (4.5 행렬이 출력되어야 함)
tensor([[ 4.5000, 4.5000],
[ 4.5000, 4.5000]])
4.5 행렬이 출력되었다면 텐서 o에 대하여 out을 호출을 해보자.
우리의 o가 와 같다면이 된다.
그러므로, 식이 유도되고 최종적으로 가 된다.
autograd를 이용하여 수많은 미친짓들을 할 수 있다.
- 소스
x = torch.randn(3, requires_grad=True)
y = x * 2
while y.data.norm() < 1000:
y = y * 2
print(y)
- 결과
tensor([-590.4467, 97.6760, 921.0221])
gradients = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
y.backward(gradients)
print(x.grad)
결과
tensor([ 51.2000, 512.0000, 0.0512])
또한 코드 블럭을 with torch.no_grad(): 로 래핑하여 requires_grad=True 속성이 명시된 autograd가 텐서의 추적 기록에 남지 않게 할 수 있다.
- 소스
print(x.requires_grad)
print((x ** 2).requires_grad)
with torch.no_grad():
print((x ** 2).requires_grad)
- 결과
True
True
False
추후 시간이 있다면 autograd와 Function에 대한 문서를 읽어보기 바란다.
http://pytorch.org/docs/stable/autograd.html
'Machine Learning > PyTorch' 카테고리의 다른 글
PyTorch 튜토리얼 4 - 분류기(classifier) 학습 (1) | 2018.05.01 |
---|---|
PyTorch 튜토리얼 3 - 뉴럴 네트워크(신경망, Neural Networks) (3) | 2018.05.01 |
PyTorch 튜토리얼 1 - PyTorch란? (0) | 2018.04.30 |
PyTorch 튜토리얼 16 - 시퀀스 모델 및 장,단기 메모리 네트워크 (0) | 2017.07.25 |
PyTorch 튜토리얼 15 - 단어 임베딩 : 어휘 의미 체계 인코딩 (0) | 2017.07.25 |