Rate this Page

파이토치(PyTorch) 기본 익히기 || 빠른 시작 || 텐서(Tensor) || Dataset과 Dataloader || 변형(Transform) || 신경망 모델 구성하기 || Autograd || 최적화(Optimization) || 모델 저장하고 불러오기

모델 매개변수 최적화하기#

이제 모델과 데이터가 준비되었으니, 데이터에 매개변수를 최적화하여 모델을 학습하고, 검증하고, 테스트할 차례입니다. 모델을 학습하는 과정은 반복적인 과정을 거칩니다; 각 반복 단계에서 모델은 출력을 추측하고, 추측과 정답 사이의 오류(손실(loss))를 계산하고, (이전 장에서 본 것처럼) 매개변수에 대한 오류의 도함수(derivative)를 수집한 뒤, 경사하강법을 사용하여 이 파라미터들을 최적화(optimize)합니다. 이 과정에 대한 자세한 설명은 3Blue1Brown의 역전파 영상을 참고하세요.

기본(Pre-requisite) 코드#

이전 장인 Dataset과 DataLoader신경망 모델 구성하기에서 코드를 가져왔습니다.

import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision.transforms import ToTensor

training_data = datasets.FashionMNIST(
    root="data",
    train=True,
    download=True,
    transform=ToTensor()
)

test_data = datasets.FashionMNIST(
    root="data",
    train=False,
    download=True,
    transform=ToTensor()
)

train_dataloader = DataLoader(training_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)

class NeuralNetwork(nn.Module):
    def __init__(self):
        super().__init__()
        self.flatten = nn.Flatten()
        self.linear_relu_stack = nn.Sequential(
            nn.Linear(28*28, 512),
            nn.ReLU(),
            nn.Linear(512, 512),
            nn.ReLU(),
            nn.Linear(512, 10),
        )

    def forward(self, x):
        x = self.flatten(x)
        logits = self.linear_relu_stack(x)
        return logits

model = NeuralNetwork()

하이퍼파라미터(Hyperparameter)#

하이퍼파라미터(Hyperparameter)는 모델 최적화 과정을 제어할 수 있는 조절 가능한 매개변수입니다. 서로 다른 하이퍼파라미터 값은 모델 학습과 수렴율(convergence rate)에 영향을 미칠 수 있습니다. (하이퍼파라미터 튜닝(tuning)에 대해 더 알아보기)

학습 시에는 다음과 같은 하이퍼파라미터를 정의합니다:
  • 에폭(epoch) 수 - 데이터셋을 반복하는 횟수

  • 배치 크기(batch size) - 매개변수가 갱신되기 전 신경망을 통해 전파된 데이터 샘플의 수

  • 학습률(learning rate) - 각 배치/에폭에서 모델의 매개변수를 조절하는 비율. 값이 작을수록 학습 속도가 느려지고, 값이 크면 학습 중 예측할 수 없는 동작이 발생할 수 있습니다.

learning_rate = 1e-3
batch_size = 64
epochs = 5

최적화 단계(Optimization Loop)#

하이퍼파라미터를 설정한 뒤에는 최적화 단계를 통해 모델을 학습하고 최적화할 수 있습니다. 최적화 단계의 각 반복(iteration)을 에폭이라고 부릅니다.

하나의 에폭은 다음 두 부분으로 구성됩니다:
  • 학습 단계(train loop) - 학습용 데이터셋을 반복(iterate)하고 최적의 매개변수로 수렴합니다.

  • 검증/테스트 단계(validation/test loop) - 모델 성능이 개선되고 있는지를 확인하기 위해 테스트 데이터셋을 반복(iterate)합니다.

학습 단계(training loop)에서 일어나는 몇 가지 개념들을 간략히 살펴보겠습니다. 최적화 단계(optimization loop)를 보려면 전체 구현 부분으로 건너뛰시면 됩니다.

손실 함수(loss function)#

학습용 데이터를 제공하면, 학습되지 않은 신경망은 정답을 제공하지 않을 확률이 높습니다. 손실 함수(loss function)는 획득한 결과와 실제 값 사이의 틀린 정도(degree of dissimilarity)를 측정하며, 학습 중에 이 값을 최소화하려고 합니다. 주어진 데이터 샘플을 입력으로 계산한 예측과 정답(label)을 비교하여 손실(loss)을 계산합니다.

일반적인 손실함수에는 회귀 문제(regression task)에 사용하는 nn.MSELoss(평균 제곱 오차(MSE; Mean Square Error))나 분류(classification)에 사용하는 nn.NLLLoss (음의 로그 우도(Negative Log Likelihood)), 그리고 nn.LogSoftmaxnn.NLLLoss를 합친 nn.CrossEntropyLoss 등이 있습니다.

모델의 출력 로짓(logit)을 nn.CrossEntropyLoss에 전달하여 로짓(logit)을 정규화하고 예측 오류를 계산합니다.

# 손실 함수를 초기화합니다.
loss_fn = nn.CrossEntropyLoss()

옵티마이저(Optimizer)#

최적화는 각 학습 단계에서 모델의 오류를 줄이기 위해 모델 매개변수를 조정하는 과정입니다. 최적화 알고리즘은 이 과정이 수행되는 방식(여기에서는 확률적 경사하강법(SGD; Stochastic Gradient Descent))을 정의합니다. 모든 최적화 절차(logic)는 optimizer 객체에 캡슐화(encapsulate)됩니다. 여기서는 SGD 옵티마이저를 사용하고 있으며, PyTorch에는 ADAM이나 RMSProp과 같은 다른 종류의 모델과 데이터에서 더 잘 동작하는 다양한 옵티마이저가 있습니다.

학습하려는 모델의 매개변수와 학습률(learning rate) 하이퍼파라미터를 등록하여 옵티마이저를 초기화합니다.

optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
학습 단계(loop)에서 최적화는 세단계로 이뤄집니다:
  • optimizer.zero_grad()를 호출하여 모델 매개변수의 변화도를 재설정합니다. 기본적으로 변화도는 더해지기(add up) 때문에 중복 계산을 막기 위해 반복할 때마다 명시적으로 0으로 설정합니다.

  • loss.backwards()를 호출하여 예측 손실(prediction loss)을 역전파합니다. PyTorch는 각 매개변수에 대한 손실의 변화도를 저장합니다.

  • 변화도를 계산한 뒤에는 optimizer.step()을 호출하여 역전파 단계에서 수집된 변화도로 매개변수를 조정합니다.

전체 구현#

최적화 코드를 반복하여 수행하는 train_loop와 테스트 데이터로 모델의 성능을 측정하는 test_loop를 정의하였습니다.

def train_loop(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)
    # 모델을 학습(train) 모드로 설정합니다 - 배치 정규화(Batch Normalization) 및 드롭아웃(Dropout) 레이어들에 중요합니다.
    # 이 예시에서는 없어도 되지만, 모범 사례를 위해 추가해두었습니다.
    model.train()
    for batch, (X, y) in enumerate(dataloader):
        # 예측(prediction)과 손실(loss) 계산
        pred = model(X)
        loss = loss_fn(pred, y)

        # 역전파
        loss.backward()
        optimizer.step()
        optimizer.zero_grad()

        if batch % 100 == 0:
            loss, current = loss.item(), batch * batch_size + len(X)
            print(f"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]")


def test_loop(dataloader, model, loss_fn):
    # 모델을 평가(eval) 모드로 설정합니다 - 배치 정규화(Batch Normalization) 및 드롭아웃(Dropout) 레이어들에 중요합니다.
    # 이 예시에서는 없어도 되지만, 모범 사례를 위해 추가해두었습니다.
    model.eval()
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    test_loss, correct = 0, 0

    # torch.no_grad()를 사용하여 테스트 시 변화도(gradient)를 계산하지 않도록 합니다.
    # 이는 requires_grad=True로 설정된 텐서들의 불필요한 변화도 연산 및 메모리 사용량 또한 줄여줍니다.
    with torch.no_grad():
        for X, y in dataloader:
            pred = model(X)
            test_loss += loss_fn(pred, y).item()
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()

    test_loss /= num_batches
    correct /= size
    print(f"Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")

손실 함수와 옵티마이저를 초기화하고 train_looptest_loop에 전달합니다. 모델의 성능 향상을 알아보기 위해 자유롭게 에폭(epoch) 수를 증가시켜 볼 수 있습니다.

loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)

epochs = 10
for t in range(epochs):
    print(f"Epoch {t+1}\n-------------------------------")
    train_loop(train_dataloader, model, loss_fn, optimizer)
    test_loop(test_dataloader, model, loss_fn)
print("Done!")
Epoch 1
-------------------------------
loss: 2.299513  [   64/60000]
loss: 2.287925  [ 6464/60000]
loss: 2.277602  [12864/60000]
loss: 2.270356  [19264/60000]
loss: 2.242823  [25664/60000]
loss: 2.218159  [32064/60000]
loss: 2.219211  [38464/60000]
loss: 2.190093  [44864/60000]
loss: 2.187517  [51264/60000]
loss: 2.149763  [57664/60000]
Test Error:
 Accuracy: 46.4%, Avg loss: 2.145027

Epoch 2
-------------------------------
loss: 2.154697  [   64/60000]
loss: 2.141859  [ 6464/60000]
loss: 2.094461  [12864/60000]
loss: 2.103545  [19264/60000]
loss: 2.033115  [25664/60000]
loss: 1.988875  [32064/60000]
loss: 2.004640  [38464/60000]
loss: 1.932700  [44864/60000]
loss: 1.934014  [51264/60000]
loss: 1.847330  [57664/60000]
Test Error:
 Accuracy: 53.3%, Avg loss: 1.854161

Epoch 3
-------------------------------
loss: 1.890387  [   64/60000]
loss: 1.852810  [ 6464/60000]
loss: 1.753429  [12864/60000]
loss: 1.782882  [19264/60000]
loss: 1.659696  [25664/60000]
loss: 1.637400  [32064/60000]
loss: 1.644098  [38464/60000]
loss: 1.564396  [44864/60000]
loss: 1.585933  [51264/60000]
loss: 1.472795  [57664/60000]
Test Error:
 Accuracy: 59.4%, Avg loss: 1.498513

Epoch 4
-------------------------------
loss: 1.567158  [   64/60000]
loss: 1.530391  [ 6464/60000]
loss: 1.405553  [12864/60000]
loss: 1.458744  [19264/60000]
loss: 1.340361  [25664/60000]
loss: 1.353203  [32064/60000]
loss: 1.352876  [38464/60000]
loss: 1.293391  [44864/60000]
loss: 1.323514  [51264/60000]
loss: 1.224356  [57664/60000]
Test Error:
 Accuracy: 62.5%, Avg loss: 1.249294

Epoch 5
-------------------------------
loss: 1.326112  [   64/60000]
loss: 1.305910  [ 6464/60000]
loss: 1.164245  [12864/60000]
loss: 1.251330  [19264/60000]
loss: 1.130255  [25664/60000]
loss: 1.162398  [32064/60000]
loss: 1.172910  [38464/60000]
loss: 1.120709  [44864/60000]
loss: 1.156476  [51264/60000]
loss: 1.074620  [57664/60000]
Test Error:
 Accuracy: 64.4%, Avg loss: 1.091447

Epoch 6
-------------------------------
loss: 1.162909  [   64/60000]
loss: 1.160670  [ 6464/60000]
loss: 1.002148  [12864/60000]
loss: 1.119797  [19264/60000]
loss: 0.996873  [25664/60000]
loss: 1.031402  [32064/60000]
loss: 1.059174  [38464/60000]
loss: 1.008009  [44864/60000]
loss: 1.044268  [51264/60000]
loss: 0.978360  [57664/60000]
Test Error:
 Accuracy: 65.8%, Avg loss: 0.987129

Epoch 7
-------------------------------
loss: 1.047740  [   64/60000]
loss: 1.063971  [ 6464/60000]
loss: 0.888814  [12864/60000]
loss: 1.030747  [19264/60000]
loss: 0.910381  [25664/60000]
loss: 0.937962  [32064/60000]
loss: 0.983528  [38464/60000]
loss: 0.932642  [44864/60000]
loss: 0.964878  [51264/60000]
loss: 0.912432  [57664/60000]
Test Error:
 Accuracy: 67.2%, Avg loss: 0.914588

Epoch 8
-------------------------------
loss: 0.961732  [   64/60000]
loss: 0.995274  [ 6464/60000]
loss: 0.806199  [12864/60000]
loss: 0.966703  [19264/60000]
loss: 0.851745  [25664/60000]
loss: 0.868244  [32064/60000]
loss: 0.929388  [38464/60000]
loss: 0.880836  [44864/60000]
loss: 0.906538  [51264/60000]
loss: 0.864006  [57664/60000]
Test Error:
 Accuracy: 68.6%, Avg loss: 0.861493

Epoch 9
-------------------------------
loss: 0.894428  [   64/60000]
loss: 0.943068  [ 6464/60000]
loss: 0.743036  [12864/60000]
loss: 0.918068  [19264/60000]
loss: 0.809457  [25664/60000]
loss: 0.814895  [32064/60000]
loss: 0.887935  [38464/60000]
loss: 0.843832  [44864/60000]
loss: 0.862250  [51264/60000]
loss: 0.826229  [57664/60000]
Test Error:
 Accuracy: 69.8%, Avg loss: 0.820720

Epoch 10
-------------------------------
loss: 0.839846  [   64/60000]
loss: 0.900568  [ 6464/60000]
loss: 0.693007  [12864/60000]
loss: 0.879818  [19264/60000]
loss: 0.777006  [25664/60000]
loss: 0.773459  [32064/60000]
loss: 0.854200  [38464/60000]
loss: 0.816182  [44864/60000]
loss: 0.827488  [51264/60000]
loss: 0.795304  [57664/60000]
Test Error:
 Accuracy: 71.0%, Avg loss: 0.788186

Done!

더 읽어보기#

Total running time of the script: (4 minutes 38.237 seconds)