파이토치 튜토리얼 2번째~!!!
이 글은 파이토치 공식 튜토리얼에서 가져온 예제다. 더 정확한 내용을 보시려면 여기를 참조
Pytorch - 새 autograd 함수 정의하기.
내부적으로 autograd 의 기본 연산자는 실제로 tensor를 조작하는 2개의 함수다. forward는 익력tensor로부터 출력 tensor를 계산한다. backward 함수는 어떤 스칼라값에 대한 출력 tensor의 변화도를 전달받고 동일한 스칼라 값에 대한 입력 tensor의 변화도를 계산한다.
pytorch에서 torch.autograd.Function의 서브클래스를 정의하고 forward 와 backward함수를 구현함으로써 사용자 정의 autograd 연산자를 손쉽게 정의할 수 있다. 그 후 인스턴스?를 생성하고 이를 함수처럼 호출하여 입력데이터를 갖는 tensor를 전달하는 식으로 새로운 autograd 연산자를 사용할수 있습니다.
아래 예제는 ReLU 로 비선형적으로 동작하는 사용자 정의 autograd 함수를 정의하고 2-계층 신경망에 이를 적용해보자
import torch
class MyReLU(torch.autograd.Function):
"""
torch.autograd.Function을 상속받아 사용자 정의 autograd Function을 구현하고,
Tensor 연산을 하는 순전파와 역전파 단계를 구현하겠습니다.
"""
@staticmethod
def forward(ctx, input):
"""
순전파 단계에서는 입력을 갖는 Tensor를 받아 출력을 갖는 Tensor를 반환합니다.
ctx는 컨텍스트 객체(context object)로 역전파 연산을 위한 정보 저장에
사용합니다. ctx.save_for_backward method를 사용하여 역전파 단계에서 사용할 어떠한
객체도 저장(cache)해 둘 수 있습니다.
"""
ctx.save_for_backward(input)
return input.clamp(min=0)
@staticmethod
def backward(ctx, grad_output):
"""
역전파 단계에서는 출력에 대한 손실의 변화도를 갖는 Tensor를 받고, 입력에
대한 손실의 변화도를 계산합니다.
"""
input, = ctx.saved_tensors
grad_input = grad_output.clone()
grad_input[input < 0] = 0
return grad_input
dtype = torch.float
device = torch.device("cpu")
# device = torch.device("cuda:0") # GPU에서 실행하려면 이 주석을 제거하세요.
# N은 배치 크기이며, D_in은 입력의 차원입니다;
# H는 은닉층의 차원이며, D_out은 출력 차원입니다.
N, D_in, H, D_out = 64, 1000, 100, 10
# 입력과 출력을 저장하기 위해 무작위 값을 갖는 Tensor를 생성합니다.
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)
# 가중치를 저장하기 위해 무작위 값을 갖는 Tensor를 생성합니다.
w1 = torch.randn(D_in, H, device=device, dtype=dtype, requires_grad=True)
w2 = torch.randn(H, D_out, device=device, dtype=dtype, requires_grad=True)
learning_rate = 1e-6
for t in range(500):
# 사용자 정의 Function을 적용하기 위해 Function.apply 메소드를 사용합니다.
# 여기에 'relu'라는 이름을 붙였습니다.
relu = MyReLU.apply
# 순전파 단계: Tensor 연산을 사용하여 예상되는 y 값을 계산합니다;
# 사용자 정의 autograd 연산을 사용하여 ReLU를 계산합니다.
y_pred = relu(x.mm(w1)).mm(w2)
# 손실을 계산하고 출력합니다.
loss = (y_pred - y).pow(2).sum()
if t % 100 == 99:
print(t, loss.item())
# autograde를 사용하여 역전파 단계를 계산합니다.
loss.backward()
# 경사하강법(gradient descent)을 사용하여 가중치를 갱신합니다.
with torch.no_grad():
w1 -= learning_rate * w1.grad
w2 -= learning_rate * w2.grad
# 가중치 갱신 후에는 수동으로 변화도를 0으로 만듭니다.
w1.grad.zero_()
w2.grad.zero_()
코드를 보면 결국 이전에 올렸던것과 똑같다는 것을 볼수 있다. 클래스로 정의해 놨을뿐 forward 에서 0이상의 값들로 출력 tensor를 만들고 backward는 이전과 같이 손실의 변화를 받아서 가중치를 조절한다.
nn 모듈
연산 그래프와 autograd는 복잡한 연산자를 정의하고 도함수를 자동으로 계산하는 강력한 패러다임. 하지만 규모가 큰 신경망에서는 autograd 그 자체만으로는 너무 낮은 수준일수 있음. 신경망을 구성할때 종종 연산을 여러계층에 배열하는것으로 생각하는데 이중 일부는 학습도중 최적화가 될 학습가능한 매개변수를 갖고 있습니다.
tensorflow는 keras, tensorflow-slim, tflearn 과 같은 패키지들이 연산 그래프의 성능을 더 높여주듯 pytorch에서도 nn 패키지가 동일한 목적으로 제공됨. nn 패키지는 신경망 계층들과 거의 동일한 Module의 집합을 정의함, Module은 입력 Tensor를 받고 출력 tensor를 계산하는 한편, 학습 가능한 매개변수를 갖는 tensor같은 내부 상태를 갖습니다. nn 또한 신경망을 학습시킬때 주로 사용하는 유용한 손실 함수들도 정의하고 있음.
import torch
# N은 배치 크기이며, D_in은 입력의 차원입니다;
# H는 은닉층의 차원이며, D_out은 출력 차원입니다.
N, D_in, H, D_out = 64, 1000, 100, 10
# 입력과 출력을 저장하기 위해 무작위 값을 갖는 Tensor를 생성합니다.
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)
# nn 패키지를 사용하여 모델을 순차적 계층(sequence of layers)으로 정의합니다.
# nn.Sequential은 다른 Module들을 포함하는 Module로, 그 Module들을 순차적으로
# 적용하여 출력을 생성합니다. 각각의 Linear Module은 선형 함수를 사용하여
# 입력으로부터 출력을 계산하고, 내부 Tensor에 가중치와 편향을 저장합니다.
model = torch.nn.Sequential(
torch.nn.Linear(D_in, H), # 여기에 가중치가 들어가있다??
torch.nn.ReLU(),
torch.nn.Linear(H, D_out),
)
# 또한 nn 패키지에는 널리 사용하는 손실 함수들에 대한 정의도 포함하고 있습니다;
# 여기에서는 평균 제곱 오차(MSE; Mean Squared Error)를 손실 함수로 사용하겠습니다.
loss_fn = torch.nn.MSELoss(reduction='sum') # 흠... mse 때문인지 loss가 크게 줄어들지않음..
learning_rate = 1e-4
for t in range(500):
# 순전파 단계: 모델에 x를 전달하여 예상되는 y 값을 계산합니다. Module 객체는
# __call__ 연산자를 덮어써(override) 함수처럼 호출할 수 있게 합니다.
# 이렇게 함으로써 입력 데이터의 Tensor를 Module에 전달하여 출력 데이터의
# Tensor를 생성합니다.
y_pred = model(x)
# 손실을 계산하고 출력합니다. 예측한 y와 정답인 y를 갖는 Tensor들을 전달하고,
# 손실 함수는 손실 값을 갖는 Tensor를 반환합니다.
loss = loss_fn(y_pred, y)
if t % 100 == 99:
print(t, loss.item())
# 역전파 단계를 실행하기 전에 변화도를 0으로 만듭니다.
model.zero_grad()
# 역전파 단계: 모델의 학습 가능한 모든 매개변수에 대해 손실의 변화도를
# 계산합니다. 내부적으로 각 Module의 매개변수는 requires_grad=True 일 때
# Tensor 내에 저장되므로, 이 호출은 모든 모델의 모든 학습 가능한 매개변수의
# 변화도를 계산하게 됩니다.
loss.backward()
# 경사하강법(gradient descent)를 사용하여 가중치를 갱신합니다. 각 매개변수는
# Tensor이므로 이전에 했던 것과 같이 변화도에 접근할 수 있습니다.
with torch.no_grad():
for param in model.parameters():
param -= learning_rate * param.grad
pytorch : optim
이제 옵티마이저를 보자.
지금까지는 (autograd의 추적기록을 피하기 위해 torch.no_grad() 또는 .data를 사용하는 식으로 ) 학습가능한 매개변수를 갖는 tensor를 직접 조작하며 모델의 가중치를 갱신함. 이것은 확률적 경사하강법(SGD) 같은 간단한 최적화알고리즘엔 부담이 안되지만, 실제로 신경망을 학습할때 주로 사용하는 AdaGrad, RMSProp, Adam등과 같은 정교한 optimizer를 사용합니다. optim 패키지는 최적화 알고리즘에 대한 아이디어를 추상화하고 일반적으로 사용하는 최적화 알고리즘의 구현체를 제공함.
아래 예제에서는 optim에서 제공하는 Adam 알고리즘을 사용해보자.
import torch
# N은 배치 크기이며, D_in은 입력의 차원입니다;
# H는 은닉층의 차원이며, D_out은 출력 차원입니다.
N, D_in, H, D_out = 64, 1000, 100, 10
# 입력과 출력을 저장하기 위해 무작위 값을 갖는 Tensor를 생성합니다.
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)
# nn 패키지를 사용하여 모델과 손실 함수를 정의합니다.
model = torch.nn.Sequential(
torch.nn.Linear(D_in, H),
torch.nn.ReLU(),
torch.nn.Linear(H, D_out),
)
loss_fn = torch.nn.MSELoss(reduction='sum')
# optim 패키지를 사용하여 모델의 가중치를 갱신할 Optimizer를 정의합니다.
# 여기서는 Adam을 사용하겠습니다; optim 패키지는 다른 다양한 최적화 알고리즘을
# 포함하고 있습니다. Adam 생성자의 첫번째 인자는 어떤 Tensor가 갱신되어야 하는지
# 알려줍니다.
learning_rate = 1e-4
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
for t in range(500):
# 순전파 단계: 모델에 x를 전달하여 예상되는 y 값을 계산합니다.
y_pred = model(x)
# 손실을 계산하고 출력합니다.
loss = loss_fn(y_pred, y)
if t % 100 == 99:
print(t, loss.item())
# 역전파 단계 전에, Optimizer 객체를 사용하여 (모델의 학습 가능한 가중치인)
# 갱신할 변수들에 대한 모든 변화도를 0으로 만듭니다. 이렇게 하는 이유는
# 기본적으로 .backward()를 호출할 때마다 변화도가 버퍼(buffer)에 (덮어쓰지 않고)
# 누적되기 때문입니다. 더 자세한 내용은 torch.autograd.backward에 대한 문서를
# 참조하세요.
optimizer.zero_grad()
# 역전파 단계: 모델의 매개변수에 대한 손실의 변화도를 계산합니다.
loss.backward()
# Optimizer의 step 함수를 호출하면 매개변수가 갱신됩니다.
optimizer.step()
여기까지..
이제 다음 차례로는 사용자 정의 nn.Module과 제어흐름, 가중치 공유가 남았다. 요걸 이해해야 다음차례로 넘어갈수 있지 않을까 하는 생각이 든다~!
'Bigdata, AI > NLP' 카테고리의 다른 글
WHAT IS TORCH.NN REALLY? (0) | 2020.08.26 |
---|---|
예제로 배우는 파이토치3. 마지막. (0) | 2020.08.25 |
예제로 배우는 파이토치! (0) | 2020.08.24 |
파이토치 텐서보드 에러 ModuleNotFoundError: No module named 'torch.utils.tensorboard' (1) | 2020.07.24 |
mecab 설치하기 (0) | 2020.07.20 |
댓글