본문 바로가기

프로그래머/Pytorch

[Pytorch tutorial] Autograd: 자동미분

본 포스팅은 파이토치 튜토리얼 한글판 홈페이지 바탕으로 작성하였습니다.
pytorch tutorial - Autograd: 자동 미분

Autograd: 자동 미분

  • autograd 패키지는 tensor의 모든 연산에 대해 자동 미분을 제공
  • 코드를 어떻게 작성하여 실행하느냐에 따라 역전파 정의됨(학습 과정의 매 단계마다 달라짐)

Tensor

  • .requires_grad=True로 설정하면 그 tensor에서 이뤄진 모든 연산들을 추적
  • 계산 완료 후 .backward()를 호출하여 gradient 자동 계산
  • tensor의 변화도는 .grad 속성에 누적
  • .detach()를 호출하여 tensor가 기록을 추적하는 것을 방지
  • with torch.no_grad(): 로 코드 블럭을 감싸 기록 추적(메모리 사용) 방지
    • 학습 가능한 매개변수를 갖는 모델을 평가할 때 유용
  • 각 tensor은 .grad_fn 속성을 가지고 있는데, 이는 tensor을 생성한 function을 참조
    • 단, 사용자가 만든 tensor의 grad_fn은 None
import torch

x = torch.ones(2, 2, requires_grad=True)
y = x + 2
print(y)

tensor([[3., 3.],
[3., 3.]], grad_fn=)

  • y는 연산의 결과로 생성된 tensor.
print(y.grad_fn)

< AddBackward0 object at 0x7f12fa665940>

z = y * y * 3
out = z.mean()

print(z, out)

tensor([[27., 27.],
[27., 27.]], grad_fn=) tensor(27., grad_fn=)

  • .requires_grad_( ... ) 는 기존 Tensor의 requires_grad 값을 바꿔치기 (in-place)하여 변경
  • 입력값이 지정되지 않으면 기본값은 False
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 0x7f12fa75e8d0>

변화도

  • out 은 하나의 스칼라 값(27)만 갖고 있기 때문에, out.backward() 는 out.backward(torch.tensor(1.)) 과 동일
  • 변화도 d(out)/dx를 출력
out.backward()

print(x.grad)

tensor([[4.5000, 4.5000],
[4.5000, 4.5000]])

  • 일반적으로 torch.autograd는 vector-Jacobian 곱을 계산하는 엔진
  • y의 L2 norm(Euclidean norm)이 1000을 넘어갈 때까지 계산
x = torch.randn(3, requires_grad=True)

y = x * 2
while y.data.norm() < 1000:
    y = y * 2

print(y)

tensor([-1329.3849, 383.0396, 364.1252], grad_fn=)

  • torch.autograd 는 전체 야코비안을 직접 계산할수는 없지만, 벡터-야코비안 곱은 간단히 backward 에 해당 벡터를 인자로 제공하여 얻을 수 있다
  • y의 x에 대한 벡터-야코비안 곱에서 x에 v를 인자로 제공
v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
y.backward(v)

print(x.grad)

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

  • .detach() 를 호출하여 내용물(content)은 같지만 require_grad가 다른 새로운 Tensor를 가져옴
print(x.requires_grad)
y = x.detach()
print(y.requires_grad)
print(x.eq(y).all())