카테고리 없음

MNIST Lab 10편 - train/evaluate로 학습 루프 완성하기

cedis 2026. 5. 30. 22:48

MNIST Lab 기본 구현 10편

이제 모든 부품을 실제 학습 루프로 연결한다. train()은 미니배치를 만들고, forward, loss, backward, optimizer update를 반복하며 epoch별 loss를 기록한다.

1. 학습 루프의 전체 순서

1
데이터 섞기

epoch마다 인덱스를 섞어 batch 순서를 바꾼다.

2
미니배치 만들기

batch_size만큼 입력과 정답을 잘라 가져온다.

3
Forward

model.forward로 예측 확률을 만든다.

4
Loss

cross_entropy_loss로 벌점을 계산한다.

5
Backward 시작점

softmax + cross entropy gradient를 만든다.

6
Backward

model.backward로 각 계층 gradient를 계산한다.

7
Update

optimizer.update로 params를 갱신한다.

2. train 최종 구현 코드

def train(model, optimizer, x_train, y_train, epochs=20, batch_size=128):
    loss_history = []
    num_train = x_train.shape[0]

    for epoch in range(epochs):
        indices = np.random.permutation(num_train)
        epoch_loss = 0
        batch_count = 0

        for start in range(0, num_train, batch_size):
            end = start + batch_size
            batch_indices = indices[start:end]

            x_batch = x_train[batch_indices]
            y_batch = y_train[batch_indices]

            y_pred = model.forward(x_batch, train=True)
            loss = cross_entropy_loss(y_pred, y_batch)

            batch_size_actual = x_batch.shape[0]
            dout = y_pred.copy()
            dout[np.arange(batch_size_actual), y_batch] -= 1
            dout /= batch_size_actual

            model.backward(dout)
            optimizer.update(model.params, model.grads)

            epoch_loss += loss
            batch_count += 1

        loss_history.append(epoch_loss / batch_count)

    return loss_history
가장 중요한 네 줄

dout = y_pred.copy()부터 dout /= batch_size_actual까지가 역전파 시작점이다. Softmax와 Cross Entropy를 합친 미분을 직접 만드는 부분이다.

3. evaluate 최종 구현 코드

def evaluate(model, x, y):
    y_pred = model.predict(x)
    accuracy = np.mean(np.argmax(y_pred, axis=1) == y) * 100
    total_params = sum(p.size for p in model.params.values())
    return accuracy, total_params
evaluate에서는 train=False 흐름을 쓴다

model.predict()는 내부에서 평가 모드 forward를 사용한다. Dropout은 꺼지고, BatchNorm은 running 통계를 사용해야 하기 때문이다.

4. 테스트가 묻는 것

테스트 확인 조건
test_train_returns_loss_history train()이 list 형태의 loss_history를 반환하는가
test_evaluate_returns_acc_and_params evaluate()가 0~100 사이 정확도와 양수 파라미터 수를 반환하는가
비판적 코드 리뷰

현재 epoch_loss는 batch loss의 단순 평균이다. batch 크기가 거의 같아 실전 영향은 작지만, 엄밀하게는 batch 크기 가중 평균으로 계산하는 방법도 있다.

이번 글에서 기억할 것

train()은 모든 부품을 Forward - Loss - Backward - Update 흐름으로 연결해 실제 학습을 완성하는 함수다.

스스로 점검

  1. 왜 마지막 batch 크기를 따로 봐야 하는가?
  2. train에서는 train=True, evaluate에서는 train=False가 필요한 이유는 무엇인가?
  3. Softmax와 Cross Entropy를 합친 gradient는 어떤 형태인가?

다음 글 예고

다음 글에서는 전체 테스트 21개와 실제 학습 결과 98.41%를 해석한다. 테스트 통과와 정확도는 서로 다른 검증이다.

한 줄 정리

train()은 모든 부품을 Forward - Loss - Backward - Update 흐름으로 연결해 실제 학습을 완성하는 함수다.