본문 바로가기

개발/AI (ML, DL, DS, etc..)

[파이썬, 딥러닝, 파이토치] #4 "Autograd"

안녕하세요! Ash입니다 👋👋
이번 포스팅은 pyTorch의 내용중 Back Propagation을 이용한 파라미터 업데이트 방법, Autograd를 공부하겠습니다!

순차적으로 천천히 알아보도록 합시다.
여러분들중엔 GPU를 가진사람도 있고 아닌사람도 있을겁니다.
저의 경우 Local환경에서 GPU돌리는것을 디폴트환경으로 생각하고 진행하기 때문에 pyTorch에서 gpu를 사용하는 방법부터 알려드리겠습니다.

import torch

if torch.cuda.is_available()	device = torch.device('cuda')	else	device = torch.device('cpu')

요렇게 작성하면 되는데요, 혹시나 torch.device()안에 cuda와 cpu를 수정하진 마세요. 그거 수정하면 에러나더라구요..ㅎ

BATCH_SIZE = 64 # 딥러닝 모델에서 파라미터를 업데이트할 때 사용되는 데이터의 갯수
INPUT_SIZE = 1000 # 딥러닝 모델에서 Input의 크기이자 입력츠으이 노드 수
HIDDEN_SIZE = 100 # 딥러닝 모델에서 Input을 다수의 파라미터를 이용해 계산한 결과에 한번 더 계산되는 파라미터의 수
OUTPUT_SIZE = 10 # 딥러닝 모델에서 최종으로 출력되는 값의 벡터의 크기를 의미

  BATCH_SIZE는 딥러닝 모델에서 파라미터를 업데이트할 때 사용되는 데이터의 갯수를 의미합니다.
즉, BATCH_SIZE의 수만큼 데이터를 이용해 OUTPUT을 계산하고 이 수만큼 출력된 결괏값에 대한 오차를 계산합니다. BATCH_SIZE 수만큼 계산된 오찻값을 평균해 BackPropagation을 적용하고 이를 통해 파라미터를 업데이트합니다. 해당 예제에서 BATCH_SIZE=64이기때문에 이는 INPUT으로 이용되는 데이터가 64개임을 의미합니다.
  INPUT_SIZE는 딥러닝 모델에서의 INPUT의 크기이자 입력층의 노드 수를 의미합니다. 예제에서 1000이라 되어있는 것은 입력 데이터의 크기아 1000이라는 것을 의미합니다. 즉, BATCH_SIZE가 64, INPUT_SIZE가 1000이므로 1000크기의 벡터 값을 64개 사용한다는 의미이죠. 이를 모양으로 표현하면 (64, 1000)입니다.
  HIDDEN_SIZE는 딥러닝 모델에서 INPUT을 다수의 파라미터를 이용해 계산한 결과에 한 번 더 계산되는 파라미터 수를 의미합니다. 입력층에서 은닉층으로 전달되었을 때 은닉층의 노드 수를 의미하죠. 예제상으로 (64, 1000)의 INPUT들이 (1000, 100) 크기의 행렬과 행렬곱 연산을 하기위해 설정한 수입니다.
  OUTPUT_SIZE는 딥러닝 모델에서 최종으로 출력되는 값의 벡터크기를 의미합니다. 통상적으로 OUTPUT의 크기는 최종적으로 비교하고자 하는 레이블의 크기와 동일하게 설정합니다. 단순한 예로, 손글씨 데이터를 이용하여 a부터 z까지의 글자를 분류하는 분류기를 만든다고 가정할 때 이 모델에서의 OUTPUT_SIZE는 a부터 z까지의 알파벳 갯수인 23이 될 것입니다. 

# 1번 예제
x = torch.randn(BATCH_SIZE, INPUT_SIZE, device=DEVICE, dtype=torch.float, requires_grad=False)

# 2번 예제
y = torch.randn(BATCH_SIZE, OUTPUT_SIZE, device=DEVICE, dtype=torch.float, requires_grad=False)

# 3번 예제
w1 = torch.randn(INPUT_SIZE, HIDDEN_SIZE, device=DEVICE, dtype=torch.float, requires_grad=True)

# 4번 예제
w2 = torch.randn(HIDDEN_SIZE, OUTPUT_SIZE, device=DEVICE, dtype=torch.float, requires_grad=True)

위의 예제들을 봐봅시다.
  1, 2번 예제들을 보면 각각 INPUT과 OUTPUT으로 이용되기 때문에 Gradient를 계산할 필요가 없습니다. 3번 예제의 w1은 INPUT 데이터 크기가 1000이며 이와 행렬 곱을 하기위해 다음 행의 값이 1000이어야 합니다. 또한 Gradient를 계산해야하기에 required_grad=True로 설정해줍니다. 4번 w2는 w1과 x를 행렬 곱한 결과에 계산할 수 있는 데이터여야 합니다. 즉, OUTPUT을 계산할 수 있도록 w2 모양을 설정해야 합니다.

learning_rate = 1e-6
for t in range(1, 501):
	y_pred = x.mm(w1).clamp(min = 0).mm(w2)
    
    loss = (y_pred - y).pow(2).sum()
    if t % 100 == 0:
    	print("Iteration: ", t, "\t", "Loss: ", loss.item())
    loss.backward()
    
    with torch.no_grad():
    	w1 -= learning_rate * w1.grad
        w2 -= learning_rate * w2.grad
        
        w1.grad.zero_()
        w2.grad.zero_()

위 예제를 살펴 봅시다.
파라미터를 업데이트할 때, Gradient를 계산한 결괏값에 learning_rate만큼 곱한 값을 이용해 업데이트 합니다.
학습률이라고도 불리며 이것이 큰 경우 데이터가 최저점에 수렴하지 못하고, 작은경우 학습하는 시간이 너무 오래걸리게 됩니다. 이처럼 파라미터 값을 업데이트 할 때 가장 중요한 파라미터하이퍼파라미터(Hyperparameter)라고 합니다.

예측값과 실제 레이블 값을 비고해 오차를 계산하는 것loss라고 합니다. 
loss는 영어단어로 상실, 손실을 의미하는데 기계학습에서의 loss는 어떤 학습된 모델을 실제 데이터에 적용했을 때 모델의 추정 오차로 인해 발생하는 손실을 의미합니다. 


예제를 기반으로 장황하게 설명하였는데 핵심내용만 따로 보기좋게 정리해보겠습니다.
pyTorch로 구현하는 모든 신경망의 중심에는 "autograd"가 있습니다. 이는 Tensor의 모든 연산에 대해 자동미분을 제공합니다. pyTorch는 Define-by-Run 기반의 프레임워크이기에 코드의 구현에 따라 역전파가 정의된다는 뜻이며, 이 학습과정은 매 단계마다 달라집니다.

📑 Tensor Class

  • Autograd 패키지에는 torch.Tensor 클래스가 있습니다. ".requires_grad"속성을 True로 설정하면, 그 tensor에서 이뤄진 모든 연산들을 Tracking(추적)하기 시작합니다.
    • 계산이 완료된 후 .backward()를 호출하여 모든 변화도(gradient)를 자동으로 계산할 수 있습니다.
    • 해당 tensor의 gradient는 .grad에 누적됩니다.
  • Tensor의 gradient tracking을 중단하려면, .detach()를 호출하여 연산기록으로부터 분리하여 이후 연산들이 추적되는 것들을 방지할 수 있습니다.
  • tracking과 memory dump를 방지하기위해서 코드블럭을 with torch.no_grad():로 감쌀 수 있습니다.
  • 특히, Gradient는 필요 없지만, requires_grad = True가 설정되어 학습 가능한 매개변수를 갖는 모델을 평가할 때 매우 유용하답니다.

📑 Function Class

  • Autograd 패키지에는 Function 클래스가 있습니다. Tensor와 Function은 서로 연결되어 있으며, 모든 연산과정을 부호화(encode)하여 순환하지 않는 그래프를 생성합니다.
  • 각 tensor는 .grad_fn속성을 가지고 있는데, 이는 Tensor를 생성한 function을 참조하고 있습니다.
  • 사용자가 만든 tensor의 경우는 .grad_fn이 None으로 지정됩니다.

즉, pyTorch의 Automatic differentiation(Gradient) package (= autograd)는 neural net이 backpropagate할 때 필요한 gradient 계산들을 대신해줍니다.
  Require_grad=True로 설정된 모든 Tensor들에 대해 gradient를 계산하고 기본적으로 이들을 축적(=tracking)시킨다.
autograd에서 gradient가 축적되는것이 default이기 때문에 축적을 방지하는 것으로 with torch.no_grad():를 사용한다. 이는 모델을 평가할 때 유용합니다.