본문 바로가기
C.W.K.
Stream
Lesson 04 of 05 · published

학습 루프: 순전파, 역전파, 갱신

~8 min · training-loop, optimizer, epoch

Level 0수학 초심자
0 XP0/59 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete

학습 한 걸음의 뼈대

  1. 순전파: 미니배치의 예측과 손실을 계산해.
  2. 역전파: 손실을 각 매개변수로 미분해 기울기를 구해.
  3. 갱신: 최적화기가 기울기와 내부 상태를 이용해 매개변수를 바꿔.

PyTorch에서는 기울기가 기본적으로 누적되므로 다음 미니배치 전에 지워야 해. 반면 MLX처럼 함수형 기울기와 명시적 최적화 상태를 쓰는 프레임워크는 표면 문법이 달라도 같은 계산 의존성을 다뤄.

for batch in dataloader:
    optimizer.zero_grad()
    pred = model(batch.x)
    loss = loss_fn(pred, batch.y)
    loss.backward()
    optimizer.step()

이 다섯 줄은 핵심 골격이지만 실제 학습에는 자료 로딩, mixed precision, 기울기 누적, 분산 동기화, 스케줄, 체크포인트, 평가가 더 붙어. 모두가 단순한 포장인 건 아니고 수치와 시스템 동작을 바꾸기도 해.

최적화기는 과제와 구조에 맞춰 고르기

  • SGD와 momentum: 단순하고 잘 튜닝하면 특히 비전 문제에서 강한 기준선이 돼.
  • Adam: 매개변수별 1·2차 모멘트를 이용해 적응적으로 크기를 조절해.
  • AdamW: weight decay를 모멘트 갱신과 분리해 적용해. Transformer 학습에 흔하지만 언제나 최선인 기본값은 아니야.

같은 최적화기라도 학습률, 배치 크기, warmup, 감쇠 방식에 따라 결과가 크게 달라져. 이름 하나보다 전체 설정을 봐야 해.

순전파는 값을 만들고, 역전파는 민감도를 구하고, 최적화기는 상태를 바꿔. 세 역할을 구분하면 학습이 멈췄을 때 어느 층을 점검할지 알 수 있어.

Code

완전한 학습 루프·python
import torch
import torch.nn as nn
import torch.optim as optim

# 작은 데이터셋
X = torch.randn(100, 4)
y = (X.sum(dim=1) > 0).long()       # 이진 분류

model = nn.Sequential(nn.Linear(4, 16), nn.ReLU(), nn.Linear(16, 2))
optimizer = optim.Adam(model.parameters(), lr=0.01)
loss_fn = nn.CrossEntropyLoss()

# 5줄 학습 루프
for epoch in range(50):
    optimizer.zero_grad()
    pred = model(X)
    loss = loss_fn(pred, y)
    loss.backward()
    optimizer.step()
    if epoch % 10 == 0:
        print(f"epoch {epoch}: loss = {loss.item():.4f}")
MLX의 `value_and_grad`, 갱신, 평가·python
import mlx.core as mx
import mlx.nn as nn
import mlx.optimizers as optim

# 작은 데이터셋 — PyTorch 버전과 같은 모양
mx.random.seed(0)
X = mx.random.normal(shape=(100, 4))
y = (X.sum(axis=1) > 0).astype(mx.int32)

class Net(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(4, 16)
        self.fc2 = nn.Linear(16, 2)
    def __call__(self, x):
        return self.fc2(nn.relu(self.fc1(x)))

net = Net()
optimizer = optim.Adam(learning_rate=0.01)

# 함수형 loss — 모델을 인자로 받음
def loss_fn(model, X, y):
    return nn.losses.cross_entropy(model(X), y, reduction="mean")

# nn.value_and_grad 가 loss_fn 을 (loss, grads) 반환 함수로 wrap
loss_and_grad = nn.value_and_grad(net, loss_fn)

for epoch in range(50):
    loss, grads = loss_and_grad(net, X, y)
    optimizer.update(net, grads)
    mx.eval(net.parameters(), optimizer.state)   # lazy op 강제 materialize
    if epoch % 10 == 0:
        print(f"epoch {epoch}: loss = {loss.item():.4f}")

External links

Exercise

위 학습 루프에서 lr=0.01, 10.0, 0.0001을 각각 사용해 손실 곡선을 비교해. 발산과 진동, 느린 수렴을 구분하고 학습률 외에 결과에 영향을 주는 설정도 두 개 적어.
Hint
큰 학습률은 손실을 폭발시키거나 진동하게 만들 수 있고 작은 값은 제한된 반복 안에서 거의 움직이지 않을 수 있어. Adam의 기본 학습률은 구현상 0.001이지만 과제와 배치 크기, 스케줄에 따라 다시 정해야 해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.