MNIST Lab 기본 구현 10편
이제 모든 부품을 실제 학습 루프로 연결한다. train()은 미니배치를 만들고, forward, loss, backward, optimizer update를 반복하며 epoch별 loss를 기록한다.
1. 학습 루프의 전체 순서
epoch마다 인덱스를 섞어 batch 순서를 바꾼다.
batch_size만큼 입력과 정답을 잘라 가져온다.
model.forward로 예측 확률을 만든다.
cross_entropy_loss로 벌점을 계산한다.
softmax + cross entropy gradient를 만든다.
model.backward로 각 계층 gradient를 계산한다.
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
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 흐름으로 연결해 실제 학습을 완성하는 함수다.
스스로 점검
- 왜 마지막 batch 크기를 따로 봐야 하는가?
- train에서는 train=True, evaluate에서는 train=False가 필요한 이유는 무엇인가?
- Softmax와 Cross Entropy를 합친 gradient는 어떤 형태인가?
다음 글 예고
다음 글에서는 전체 테스트 21개와 실제 학습 결과 98.41%를 해석한다. 테스트 통과와 정확도는 서로 다른 검증이다.