Skip to main content
Summary

학습 루프의 큰 그림

이제 지금까지 배운 조각을 하나로 연결할 차례입니다. 모델은 입력을 받아 예측값을 만들고, 손실 함수는 예측이 정답과 얼마나 다른지 계산합니다. 옵티마이저는 그 손실을 줄이는 방향으로 모델의 파라미터를 조금씩 바꿉니다.

이 과정을 한 번만 하면 학습이 거의 일어나지 않습니다. 그래서 전체 데이터를 여러 번 반복해서 봅니다. 전체 데이터셋을 한 번 모두 사용하는 단위를 epoch라고 합니다. 그리고 DataLoader가 나누어 주는 작은 데이터 묶음을 batch라고 합니다.

기본 구조는 다음과 같습니다.

epochs = 5

for epoch in range(epochs):
    print(f'epoch {epoch + 1}')

    for batch, (X, y) in enumerate(train_dataloader):
        # 한 배치 학습
        pass

바깥쪽 for epoch는 같은 데이터셋을 몇 번 반복해서 볼지 정합니다. 안쪽 for batch는 한 epoch 안에서 배치를 하나씩 꺼냅니다. 실제 학습은 대부분 이 안쪽 반복문에서 일어납니다.

학습 루프 직접 작성하기

한 배치에서 일어나는 일

학습을 시작할 때는 먼저 모델을 학습 모드로 바꿉니다.

model.train()

model.train()은 모델에게 "지금은 학습 중"이라고 알려 주는 역할을 합니다. 단순한 신경망에서는 차이가 잘 보이지 않을 수 있지만, 드롭아웃이나 배치 정규화처럼 학습과 평가 때 다르게 동작하는 층이 있으면 반드시 필요합니다.

한 배치의 학습 코드는 다음 순서로 작성합니다.

X = X.to(device)
y = y.to(device)

pred = model(X)
loss = loss_fn(pred, y)

optimizer.zero_grad()
loss.backward()
optimizer.step()

먼저 입력 X와 정답 y를 모델이 있는 장치로 보냅니다. 모델이 GPU에 있는데 데이터가 CPU에 있으면 계산할 수 없기 때문입니다.

그다음 pred = model(X)로 예측값을 만듭니다. 이것을 순전파, 또는 forward라고 부릅니다. 이어서 loss_fn(pred, y)로 예측값과 정답의 차이를 손실값으로 계산합니다.

세 줄이 학습을 만든다

PyTorch 학습 루프에서 가장 중요한 세 줄은 다음입니다.

optimizer.zero_grad()
loss.backward()
optimizer.step()

optimizer.zero_grad()는 이전 배치에서 계산된 기울기를 지웁니다. PyTorch는 기본적으로 기울기를 누적합니다. 따라서 매 배치마다 지우지 않으면 이전 계산이 섞여 원하지 않는 방향으로 학습될 수 있습니다.

loss.backward()는 손실을 줄이기 위해 각 파라미터를 어느 방향으로 바꾸어야 하는지 계산합니다. 이 값은 각 파라미터의 .grad에 저장됩니다.

optimizer.step()은 .grad에 저장된 값을 사용해 실제로 파라미터를 갱신합니다. 즉, 이 줄에서 모델의 가중치가 바뀝니다.

순서는 반드시 지키는 것이 좋습니다. 기울기를 지우고, 새 기울기를 계산하고, 그 기울기로 파라미터를 바꿉니다.

손실값 출력하기

학습이 진행되는지 확인하려면 중간중간 손실값을 출력하면 됩니다. 텐서 형태의 손실값을 파이썬 숫자로 꺼낼 때는 loss.item()을 사용합니다.

if batch % 100 == 0:
    loss_value = loss.item()
    print(f'batch {batch}, loss: {loss_value:.4f}')

손실값이 항상 매끄럽게 내려가지는 않습니다. 배치마다 데이터가 다르기 때문입니다. 하지만 여러 epoch를 지나면서 전체적으로 줄어드는 흐름이 보이면 학습이 되고 있다고 볼 수 있습니다.

train_loop 함수로 분리하기

학습 코드를 매번 직접 쓰면 중복이 많아집니다. 그래서 보통 학습 절차를 함수로 분리합니다.

def train_loop(dataloader, model, loss_fn, optimizer, device):
    model.train()

    for batch, (X, y) in enumerate(dataloader):
        X = X.to(device)
        y = y.to(device)

        pred = model(X)
        loss = loss_fn(pred, y)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        if batch % 100 == 0:
            print(f'batch {batch}, loss: {loss.item():.4f}')

이제 전체 학습은 더 단순해집니다.

epochs = 5

for epoch in range(epochs):
    print(f'epoch {epoch + 1}')
    train_loop(train_dataloader, model, loss_fn, optimizer, device)

이 구조가 PyTorch 학습 코드의 중심입니다. PyTorch는 학습 과정을 숨기지 않습니다. 대신 사용자가 forward, loss 계산, backward, optimizer step을 직접 연결하게 합니다. 처음에는 코드가 길어 보이지만, 이 흐름을 이해하면 어떤 모델을 학습하더라도 같은 뼈대를 사용할 수 있습니다.

책 목록

초보자를 위한 PyTorch 사용법: 텐서부터 이미지 분류 프로젝트까지

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기