Python/PyTorch 공부

[PyTorch] 2. 파이토치 torch.nn을 사용해서 신경망 구축하기

AI 꿈나무 2020. 12. 8. 14:49
반응형

2.공부 목적으로 PyTorch 튜토리얼 홈페이지를 변역해보았습니다.

 

What is torch.nn really? — PyTorch Tutorials 1.7.0 documentation

Note Click here to download the full example code What is torch.nn really? by Jeremy Howard, fast.ai. Thanks to Rachel Thomas and Francisco Ingham. We recommend running this tutorial as a notebook, not a script. To download the notebook (.ipynb) file, clic

pytorch.org

 이전 포스팅에서 PyTorch module 없이 표준 python만을 사용하여 MNIST 데이터 셋에 대한 간단한 신경망을 구축했습니다.

 이번에는 PyTorch의 torch.nn 을 사용해서 신경망을 구축해보겠습니다.

 

torch.nn.functional 사용하기

 이제 코드를 재구성하겠습니다. 그럼으로써, 이전과 동일한 기능을 수행하고 모델을 더욱 정확하고 유연성있게 만드는 PyTorch의 nn 클래스의 이점을 활용할 것입니다.

 여기서부터 매 단계에서, 코드를 더 짧고 이해하기 쉽고 유연하게 만들어야 합니다.

 

 처음이면서 가장 쉬운 단계는 torch.nn.functional 의 함수(관례에 따라, 일반적으로 F 네임스페이스로 임포트합니다.)로 직접 작성한 활성화 함수, 손실함수를 대체하여 코드를 더 짧게 만들것 입니다.

 이 모듈은 torch.nn 라이브러리(라이브러리의 다른 부분에는 클래스가 포함되어 있습니다.) 안에 있는 모든 함수를 포함합니다.

 광범위한 손실, 활성화 함수뿐만 아니라 신경망을 생성하기 위한 풀링 함수와 같은 편리한 함수도 찾을 수 있습니다.

 (합성곱, 신경망 등등을 하기위한 함수들이 있습니다. 하지만 이것들은 라이브러리의 다른 부분을 사용하여 더 잘 다룰 수 있습니다.)

 

 만약 음의 우도 손실과 로그 소프트맥스 활성화함수를 사용한다면, Pytorch는 두 개를 결합한 F.croos_entropy 단일 함수를 제공합니다.

 

import torch.nn.functional as F

loss_func = F.cross_entropy

def model(xb):
    return xb @ weights + bias

 

 더이상 model 함수에 있는 log_softmax를 호출하지 않습니다.

 손실과 정확도가 이전과 동일한지 확인하겠습니다.

print(loss_func(model(xb), yb), accuracy(model(xb), yb))

 

Out :

tensor(0.0827, grad_fn=<NllLossBackward>) tensor(1.)

 

nn.Module을 사용하여 코드를 재구성하기

 다음으로, 명확하고 간결한 훈련 루프를 위해 nn.Modulenn.Parameter를 사용하겠습니다.

 nn.Module (이거 자체가 클래스이고 상태를 추적할 수 있는) 하위 클래스를 만듭니다.

 이 경우에, 순전파 단계를 위한 가중치, 편향, 메소드를 갖고 있는 클래스를 생성해야 합니다.

 nn.Module은 우리가 사용할 수 많은 속성과 메소드(.parameters().zero_grad()와 같은)를 갖고 있습니다.

 

 nn.Module (대문자 M)은 PyTorch의 특정 개념이고 많이 사용될 클래스입니다.

 nn.Module은 임포트하기 위한 Python 코드 파일인 (소문자 m) module의 Python 개념과 헷갈리면 안됩니다.

 

from torch import nn

class Mnist_Logistic(nn.Module):
    def __init__(self):
        super().__init__()
        self.weights = nn.Parameter(torch.randn(784, 10) / math.sqrt(784))
        self.bias = nn.Parameter(torch.zeros(10))
        
    def forward(self, xb):
        return xb @ self.weights + self.bias

 

 단지 함수를 사용하기보다 객체를 사용하기 때문에 모델을 인스턴스화 해야 합니다.

model = Mnist_Logistic()

 

 이제 이전과 같은 방법으로 손실을 계산하겠습니다.

 nn.Module 객체는 마치 함수처럼 사용되지만 배후에서 PyTorch는 forward 매소드를 자동으로 호출합니다.

print(loss_func(model(xb), yb))

 

Out :

tensor(2.3054, grad_fn=<NllLossBackward>)

 

 이전에는 훈련 루프를 위해 이름 별로 각 매개변수의 값을 갱신해야 했고 각각의 매개변수에 대한 기울기를 개별적으로 수동으로 0으로 제거해야 했습니다.

with torch.no_grad():
    weights -= weights.grad * lr
    bias -= bias.grad * lr
    weights.grad.zero_()
    bias.grad.zero_()

 

 만약 복잡한 모델을 갖고 있는 경우, 특히 매개변수를 잊어버리는 오류를 줄이고 각 단계를 더 정확하게 만들기 위해 model.parameters() 와 model.zero_grad() (둘 다 nn.Module에 대해 PyTorch에 의해 정의되었습니다.) 의 장점을 취하겠습니다.

with torch.no_grad():
    for p in model.parameters(): p -= p.grad * lr
    model.zero_grad()

 

 이것을 나중에 다시 실행할 수 있도록 fit 함수 안에 작은 훈련 루프를 감쌀것입니다.

def fit():
    for epoch in range(epochs):
        for i in range((n - 1) // bs + 1):
            start_i = i * bs
            end_i = start_i + bs
            xb = x_train[start_i:end_i]
            yb = y_train[start_i:end_i]
            pred = model(xb)
            loss = loss_func(pred, yb)
            
            loss.backward()
            with torch.no_grad():
                for p in model.parameters():
                    p -= p.grad * lr
                model.zero_grad()
                
fit()

 

 손실이 감소했는지 다시 한번 확인하겠습니다.

print(loss_func(model(xb), yb))

 

Out :

tensor(0.0801, grad_fn=<NllLossBackward>)

 

nn.Linear을 사용해서 재구성하기

 계속해서 코드를 재구성하겠습니다.

 수동으로 self.weightsself.bias 를 정의하고 초기화하고 xb @ self.weights + self.bias를 계산하는 대신에 이것들을 해내는 PyTorch 클래스인 nn.Linear를 선형 계층으로 사용하겠습니다.

 PyTorch는 코드를 훨씬 간단히 할 수 있는 미리 정의된 계층의 많은 종류를 갖고 있습니다.

 그리고 종종 코드를 더 빠르게 합니다.

 

class Mnist_Logistic(nn.Module):
    def __init__(self):
        super().__init__()
        self.lin = nn.Linear(784, 10)
        
    def forward(self, xb):
        return self.lin(xb)

 

 모델을 인스턴스화하고 이전과 동일한 방법으로 손실을 계산하겠습니다.

model = Mnist_Logistic()
print(loss_func(model(xb), yb))

 

Out :

tensor(2.3514, grad_fn=<NllLossBackward>)

 

 여전히 이전과 동일한 fit 메서드를 사용할 수 있습니다.

fit()

print(loss_func(model(xb), yb))

 

Out :

tensor(0.0810, grad_fn=<NllLossBackward>)

 

optim 을 사용하여 재구성하기

 PyTorch는 다양한 최적화 알고리즘을 가진 패키지, torch.optim 을 갖고 있습니다.

 수동으로 각각의 매개변수를 갱신하는 대신에 optimizer의 step 메서드를 사용하여 순전파를 진행할 수 있습니다.

 

 이것은 이전 수동으로 작성된 최적화 단계를 대체합니다.

with torch.no_grad():
    for p in model.parameters(): p -= p.grad * lr
    model.zero_grad()

 

 또는 이렇게 사용해도 됩니다.

opt.step()
opt.zero_grad()

 optim.zero_grad() 는 기울기를 0으로 재설정 합니다.

 다음 미니배치에 대한 기울기를 계산하기전에 호출해야 합니다.

 

from torch import optim

 

 추후에 재사용 할 수 있도록 모델과 optimizer를 생성하기 위한 작은 함수를 정의합니다.

def get_model():
    model = Mnist_Logistic()
    return model, optim,SGD(model.parameters(), lr=lr)
    
model, opt = get_model()
print(loss_func(model(xb), yb))

for epoch in range(epochs):
    for i in range((n - 1) // bs + 1):
        start_i = i * bs
        end_i = start_i + bs
        xb = x_train[start_i:end_i]
        yb = y_train[start_i:end_i]
        pred = model(xb)
        loss = loss_func(pred, yb)

        loss.backward()
        opt.step()
        opt.zero_grad()

print(loss_func(model(xb), yb))

 

Out :

tensor(2.3365, grad_fn=<NllLossBackward>)
tensor(0.0810, grad_fn=<NllLossBackward>)

 

 

 

 

반응형