앎을 경계하기

Machine Learning

DEEP LEARNING WITH PYTORCH: A 60 MINUTE BLITZ - TORCH.AUTOGRAD

양갱맨 2021. 3. 5. 13:55

torch.autograd

PyTorch는 자동 미분 계산을 지원한다.

이번 섹션에서는 neural network 학습을 위해 어떻게 자동미분을 적용하는지 개념적으로 이해하도록 한다.

Background

뉴럴넷은 어떠한 입력 데이터에 대해 실행되는 연결된 함수들(신경망)의 집합이다.

이러한 함수들은 weight와 bias로 구성된 parameters에 의해 정의되고, 이것들은 Tensor 형태로 저장된다.

뉴럴네트워크 학습 과정은 크게 2단계로 나눌 수 있는데, forward propagation과 backward propagation이다.

  1. Forward propagation

    input data를 각 함수들에 대해 통과시켜서 가장 옳은 output을 만들어내는 과정이다.

  1. Backward propagation

    forward propagation 과정으로 얻은 예측의 오류에 대해 뉴럴넷의 파라미터를 조정함으로써 예측을 위한 각 함수들의 파라미터들의 최적의 포인트를 찾기위한 과정이다. output으로부터 역방향으로 진행되며 이 때, 각 신경망 함수들의 파라미터들에 대한 미분값을 구해 gradient descent를 사용하여 optimal values를 찾는다.

Usage in PyTorch

  1. resnet 18 모델을 사용해서 간단한 학습 과정을 익힌다. PyTorch에서는 유명한 딥러닝 모델을 torchvision에서 제공하고 있다.
    import torch, torchvision
    model = torchvision.models.resnet18(pretrained=True) # 미리 학습된 resnet18모델 불러오기
    data = torch.rand(1, 3, 64, 64)                      # 1x3x64x64 차원의 랜덤한 데이터 텐서 생성
    labels = torch.rand(1, 1000)                         # 정답 라벨 텐서 생성
  1. 데이터를 모델에 전달하여 Forwarding pass한다.
    prediction = model(data)
  1. 모델이 얼마나 잘 예측하는지(정확도)를 측정하기 위해 loss 값을 계산한다. 실제 정답과 예측이 얼마나 다른지 계산한 값을 backward()를 호출해서 backpropagation 수행한다.
    loss = (prediction - labels).sum()
    loss.backward()
  1. backpropagation 과정에서 각 신경망들의 최적의 파라미터 값을 찾기 위해 Gradient Descent 방식을 사용한다. 이러한 방법들을 PyTorch에서는 optimizer라고 하며, 많이 사용하는 알고리즘들을 제공하고 있다. 예제에서는 Stochastic Gradient Descent를 사용한다.
    optim = torch.optim.SGD(model.parameters(), lr=le-2, momentum=0.9)
  1. 순전파 부분과 역전파 부분, 옵티마이저까지 설정이 되었으면 step()을 호출하여 gradient descent를 초기화한다. optimizer가 조정하는 파라미터들은 .grad에 저장된 gradient로 조정된다.
    optim.step() # gradient descent

Differentiation in Autograd

자동 미분 과정을 확인하자.

import torch

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

텐서 a, b가 뉴럴넷의 파라미터라고 하고, Q가 loss일 때, 각 파라미터들에 대한 loss의 gradient는 다음과 같이 정의된다.

Qa=9a2Qb=2b\frac{\partial}{\partial}=9a^2\\ \frac{\partial}{\partial}=-2b

각 파라미터들의 gradients는 .gard속성에서 확인할 수 있다.

print(9*a**2 == a.grad)
print(-2*b == b.grad)
tensor([True, True])
tensor([True, True])

PyTorch에서는 Autograd를 적용하기 위해 directed acyclic graph(유향 비순환 그래프;DAG) 형태로 연산과 데이터를 구성한다. backpropagation은 output에서 input 방향(역방향)으로 진행되기 때문에 leaves는 input tensors가 되고 root는 output tensors가 된다.

여기서 드는 의문점 한 가지, autograd에 적용하지 않을 데이터는 어떻게 할까?

gradient computation DAG에서 제외시키기 위해서는 텐서 생성 시, requires_grad를 False로 설정해주면 된다.

x = torch.rand(5, 5) # requires_grad default : False
y = torch.rand(5, 5)
z = torch.rand((5, 5), requires_grad=True)

a = x + y
print(f"Does `a` require gradients? : {a.requires_grad}")
b = x + z
print(f"Does `b` require gradients?: {b.requires_grad}")
Does `a` require gradients? : False
Does `b` require gradients? : True

이렇게 requires_grad를 False하게 되면 backward 과정에서 해당 텐서가 gradient 계산에서 제외되기 때문에 파라미터 값들이 Freeze되는 것이다.

torch.no_grad()를 사용해도 같은 효과를 낼 수 있다.

DAG에서 제외시키는 것이 사용되는 경우가 주로 pre-trained model을 fine-tuning 할 때 이다.

from torch import nn, optim

model = torchvision.models.resnet18(pretrained=True)

# model의 모든 파라미터를 freeze 시킨다.
for param in model.parameters():
	param.requires_grad = False

# model에서 맨 마지막 classifier인 fc layer를 만든다. 새로운 레이어는 gradient 계산 설정이 True임.
model.fc = nn.Linear(512, 10)
optimizer = optim.SGD(model.fc.parameters(), lr=1e-2, momentum=0.9) # 옵티마이저 설정