Level 4: 실전 프로젝트
🚀

Level 4

학습 루프

train/eval 모드, optimizer, loss 함수

약 4분
학습 루프 강의 영상
강의 영상 보기 (새 탭에서 재생)YouTube

📓Google Colab에서 실습하기

이 레슨은 PyTorch/GPU가 필요합니다. 노트북을 다운로드 후 Google Colab에서 열어주세요.

학습 내용

학습 루프

학습 목표

이 레슨을 완료하면:

  • 학습 루프의 5단계를 코드와 함께 설명할 수 있습니다
  • 검증 루프의 구현과 학습 루프와의 차이를 이해합니다
  • 학습 과정을 모니터링하고 시각화하는 방법을 익힙니다
  • 조기 종료(Early Stopping)를 구현합니다

학습 루프란?

비유: 학습 루프는 "시험공부 사이클"과 같습니다. 문제를 풀고(순전파), 정답과 비교하고(손실 계산), 어디서 틀렸는지 분석하고(역전파), 약점을 보완합니다(가중치 업데이트). 이 사이클을 수천 번 반복하면 실력이 향상됩니다.

학습 루프는 딥러닝의 핵심입니다. 모델이 아무리 잘 설계되어도, 학습 루프가 올바르지 않으면 제대로 학습되지 않습니다.


학습 루프 5단계 상세

단계코드역할왜 필요한가?
1optimizer.zero_grad()그래디언트 초기화이전 배치의 영향 제거
2output = model(data)순전파현재 가중치로 예측 수행
3loss = criterion(output, target)손실 계산예측과 정답의 차이 측정
4loss.backward()역전파각 가중치의 개선 방향 계산
5optimizer.step()가중치 업데이트실제로 가중치를 수정

각 단계를 자세히 살펴봅시다

1단계: optimizer.zero_grad() - 왜 매번 초기화해야 하는가?

비유: 칠판에 이전 수업의 내용이 남아있으면 새 수업 내용과 섞여서 혼란스럽습니다. 매 수업(배치) 전에 칠판을 깨끗이 지우는 것과 같습니다.

PyTorch는 .backward()를 호출할 때마다 그래디언트를 기존 값에 더합니다(누적). 이 설계는 특별한 경우(그래디언트 축적)에 유용하지만, 일반적인 학습에서는 반드시 초기화해야 합니다.

2단계: output = model(data) - 순전파

모델의 forward() 메서드가 자동으로 호출됩니다. 데이터가 레이어를 순서대로 통과하면서 예측값을 만들어냅니다.

3단계: loss = criterion(output, target) - 손실 계산

예측값과 실제 정답의 차이를 하나의 숫자(스칼라)로 요약합니다. 이 숫자가 작을수록 모델이 정답에 가깝게 예측한 것입니다.

4단계: loss.backward() - 역전파

비유: 시험에서 틀린 문제를 분석하는 것입니다. "이 가중치를 이 방향으로 조금 바꾸면 손실이 줄어들 것"이라는 정보(그래디언트)를 계산합니다.

5단계: optimizer.step() - 가중치 업데이트

4단계에서 계산한 방향으로 실제로 가중치를 수정합니다. 학습률이 이 수정의 크기를 결정합니다.


기본 학습 함수 구현

python
def train_one_epoch(model, loader, criterion, optimizer, device): """한 에폭의 학습을 수행하는 함수""" model.train() # 학습 모드 (Dropout, BatchNorm 활성화) total_loss = 0 correct = 0 total = 0 for data, target in loader: # 데이터를 디바이스(GPU/CPU)로 이동 data, target = data.to(device), target.to(device) # === 학습 루프 5단계 === optimizer.zero_grad() # 1. 그래디언트 초기화 output = model(data) # 2. 순전파 loss = criterion(output, target) # 3. 손실 계산 loss.backward() # 4. 역전파 optimizer.step() # 5. 가중치 업데이트 # === 통계 기록 === total_loss += loss.item() # .item()으로 Python 숫자로 변환 _, predicted = output.max(1) # 가장 높은 점수의 클래스 선택 total += target.size(0) # 전체 샘플 수 누적 correct += predicted.eq(target).sum().item() # 맞은 개수 누적 avg_loss = total_loss / len(loader) accuracy = 100.0 * correct / total return avg_loss, accuracy

왜 loss.item()을 사용하는가?

loss는 PyTorch 텐서로, 전체 계산 그래프에 연결되어 있습니다. 그냥 loss를 누적하면 계산 그래프가 계속 메모리에 남아서 메모리가 폭발합니다. .item()은 순수한 Python 숫자를 반환하므로 안전합니다.


검증 함수 구현

검증(Validation)은 모델이 학습 데이터에만 잘 맞는 것이 아니라, 처음 보는 데이터에도 잘 동작하는지 확인하는 과정입니다.

python
def validate(model, loader, criterion, device): """검증/테스트를 수행하는 함수""" model.eval() # 평가 모드 (Dropout 비활성화, BatchNorm 고정) total_loss = 0 correct = 0 total = 0 with torch.no_grad(): # 그래디언트 계산 비활성화 for data, target in loader: data, target = data.to(device), target.to(device) output = model(data) loss = criterion(output, target) total_loss += loss.item() _, predicted = output.max(1) total += target.size(0) correct += predicted.eq(target).sum().item() avg_loss = total_loss / len(loader) accuracy = 100.0 * correct / total return avg_loss, accuracy

학습 루프 vs 검증 루프 차이점

항목학습 루프검증 루프
model.train/evalmodel.train()model.eval()
torch.no_grad()사용 안 함사용함
zero_grad()매 배치마다 호출불필요
loss.backward()호출불필요
optimizer.step()호출불필요
Dropout활성화비활성화
목적모델 가중치 수정현재 성능 측정

전체 학습 과정: 에폭 루프

python
# 하이퍼파라미터 epochs = 50 best_val_loss = float("inf") # 최고 성능 추적 patience = 7 # 조기 종료 인내심 patience_counter = 0 # 학습 기록 저장 history = { "train_loss": [], "val_loss": [], "train_acc": [], "val_acc": [] } for epoch in range(epochs): # 학습 train_loss, train_acc = train_one_epoch( model, train_loader, criterion, optimizer, device ) # 검증 val_loss, val_acc = validate( model, val_loader, criterion, device ) # 기록 저장 history["train_loss"].append(train_loss) history["val_loss"].append(val_loss) history["train_acc"].append(train_acc) history["val_acc"].append(val_acc) # 현재 상태 출력 print("Epoch {}/{}".format(epoch + 1, epochs)) print(" Train - Loss: {:.4f}, Acc: {:.2f}%".format(train_loss, train_acc)) print(" Val - Loss: {:.4f}, Acc: {:.2f}%".format(val_loss, val_acc)) # === 조기 종료 (Early Stopping) === if val_loss < best_val_loss: best_val_loss = val_loss # 최고 성능 모델 저장 torch.save(model.state_dict(), "best_model.pt") patience_counter = 0 print(" >> Best model saved!") else: patience_counter += 1 print(" >> No improvement ({}/{})".format(patience_counter, patience)) if patience_counter >= patience: print("Early stopping triggered!") break

조기 종료 (Early Stopping) 상세 설명

비유: 조기 종료는 "더 이상 공부해도 성적이 오르지 않으면 공부를 멈추는 것"입니다. 과도한 공부(학습)는 오히려 응용력(일반화)을 떨어뜨릴 수 있습니다.

과적합의 3가지 상태

상태Train LossVal Loss의미조치
정상 학습감소감소모델이 잘 배우는 중학습 계속
과적합 시작감소증가훈련 데이터만 외우기 시작조기 종료
과소적합높음높음모델 용량이 부족모델 크기 증가

조기 종료의 핵심 파라미터: patience

  • patience가 너무 작으면 (예: 2): 일시적인 변동에도 학습이 멈춤
  • patience가 너무 크면 (예: 50): 과적합이 심해진 후에야 멈춤
  • 일반적인 권장값: 5~10

학습률 스케줄러

학습이 진행될수록 학습률을 줄이면 더 세밀한 최적화가 가능합니다.

python
# 방법 1: StepLR - 일정 에폭마다 학습률을 감소 scheduler = torch.optim.lr_scheduler.StepLR( optimizer, step_size=10, gamma=0.1 ) # 10 에폭마다 학습률을 1/10로 줄임 # 방법 2: ReduceLROnPlateau - 성능 정체 시 학습률 감소 scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau( optimizer, mode="min", patience=3, factor=0.5 ) # val_loss가 3 에폭 동안 개선되지 않으면 학습률을 절반으로 # 에폭 루프에서 사용 for epoch in range(epochs): train_loss, train_acc = train_one_epoch(...) val_loss, val_acc = validate(...) # StepLR의 경우 scheduler.step() # ReduceLROnPlateau의 경우 # scheduler.step(val_loss)
스케줄러전략적합한 경우
StepLR고정 간격으로 감소학습 패턴을 미리 아는 경우
ReduceLROnPlateau성능 정체 시 감소가장 범용적 (추천)
CosineAnnealingLR코사인 곡선으로 감소긴 학습에서 주기적 재시작

학습 과정 시각화

학습이 제대로 진행되고 있는지 그래프로 확인하는 것은 매우 중요합니다. 아래는 학습 기록 데이터로 그래프를 그리는 개념적 예시입니다.

python
import matplotlib.pyplot as plt import numpy as np # 학습 기록 시뮬레이션 (실제로는 위의 history 딕셔너리 사용) epochs_range = np.arange(1, 21) train_loss = 2.0 * np.exp(-0.2 * epochs_range) + 0.1 val_loss = 2.0 * np.exp(-0.15 * epochs_range) + 0.2 + 0.05 * np.maximum(0, epochs_range - 12) fig, ax = plt.subplots(1, 1, figsize=(8, 5)) ax.plot(epochs_range, train_loss, "b-o", label="Train Loss", markersize=4) ax.plot(epochs_range, val_loss, "r-s", label="Val Loss", markersize=4) ax.axvline(x=12, color="gray", linestyle="--", alpha=0.7, label="Overfitting starts") ax.set_xlabel("Epoch") ax.set_ylabel("Loss") ax.set_title("Training vs Validation Loss") ax.legend() ax.grid(True, alpha=0.3) plt.tight_layout() plt.show()

그래프 읽는 법:

  • 두 곡선이 함께 내려가면: 정상 학습 중
  • Train은 내려가는데 Val이 올라가면: 과적합 발생
  • 두 곡선 사이 간격이 크면: 과적합 심각

핵심 요약

개념설명비유
학습 루프 5단계zero_grad -> forward -> loss -> backward -> step시험공부 사이클
검증 루프eval() + no_grad()로 성능 측정모의시험
조기 종료Val Loss가 개선되지 않으면 학습 중단더 공부해도 성적 안 오르면 멈추기
학습률 스케줄러학습 진행에 따라 학습률 조정처음엔 큰 보폭, 나중엔 세밀하게
학습 기록loss/accuracy를 에폭별로 기록성적 추이 기록

학습 체크리스트

  • 학습 루프 5단계를 순서대로 설명할 수 있다
  • zero_grad()가 필요한 이유를 안다
  • loss.item()을 사용하는 이유를 안다
  • 학습 루프와 검증 루프의 차이점 5가지를 나열할 수 있다
  • 과적합의 징후를 그래프에서 식별할 수 있다
  • 조기 종료의 patience가 하는 역할을 설명할 수 있다

레슨 정보

레벨
Level 4: 실전 프로젝트
예상 소요 시간
약 4분
참고 영상
YouTube 링크

💡실습 환경 안내

이 레벨은 PyTorch/GPU가 필요하여 Google Colab 사용을 권장합니다.

Colab은 무료 GPU를 제공하여 PyTorch, CNN, Transformer 등을 실행할 수 있습니다.