Step 5 MNIST 손글씨 인식 - 딥러닝의 첫 실전

지도학습과 실전 데이터셋으로 배우는 이미지 분류

1. MNIST: 딥러닝의 "Hello World"

XOR 문제로 MLP의 원리를 배웠다면, 이제는 실제 데이터로 딥러닝을 경험할 차례입니다. 그 시작점이 바로 MNIST 데이터셋입니다.

📝 MNIST란?

Modified National Institute of Standards and Technology database

🎨 MNIST 샘플 이미지

실제 MNIST 데이터는 이렇게 생겼습니다. 사람들이 직접 손으로 쓴 숫자들이죠.

0 Label: 0 1 Label: 1 2 Label: 2 3 Label: 3 4 Label: 4 5 Label: 5 6 Label: 6 7 Label: 7 8 Label: 8 9 Label: 9 각 이미지는 28×28 픽셀 (784개의 픽셀값) 픽셀값: 0 (흰색) ~ 255 (검은색)

💡 왜 28×28일까?
1998년 당시 컴퓨터 성능을 고려한 적절한 크기입니다. 너무 크면 계산이 오래 걸리고, 너무 작으면 숫자를 알아보기 어렵죠. 28×28 = 784개의 픽셀이 우리 신경망의 입력이 됩니다!

🎯 이것은 지도학습 (Supervised Learning) 문제입니다

MNIST는 지도학습의 대표적인 예시입니다. 왜냐하면:

XOR에서는 4개의 데이터로 학습했지만, MNIST는 60,000개의 예제로 학습합니다. 이렇게 많은 데이터로 학습하면 모델은 다양한 필체의 숫자를 인식할 수 있게 됩니다.

2. MNIST 데이터 구조 이해하기

실제로 MNIST 데이터가 어떻게 구성되어 있는지 자세히 살펴봅시다.

📦 PyTorch에서 MNIST 다운로드하기

PyTorch는 MNIST를 자동으로 다운로드하고 로드하는 기능을 제공합니다.

from torchvision import datasets, transforms

# 데이터 변환 정의 (이미지를 텐서로 변환)
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5,), (0.5,))  # 정규화
])

# 훈련 데이터 다운로드
train_dataset = datasets.MNIST(
    root='./data',
    train=True,
    download=True,
    transform=transform
)

# 테스트 데이터 다운로드
test_dataset = datasets.MNIST(
    root='./data',
    train=False,
    download=True,
    transform=transform
)

코드 설명:
ToTensor(): 이미지를 PyTorch 텐서로 변환 (0~255 → 0~1)
Normalize(0.5, 0.5): 픽셀값을 -1~1 범위로 정규화
train=True: 훈련용 60,000개 다운로드
train=False: 테스트용 10,000개 다운로드

🔍 데이터의 실제 구조

하나의 MNIST 데이터는 이렇게 구성되어 있습니다:

이미지 데이터 7 28 × 28 = 784 픽셀 정답 레이블 7 텐서 형태 [1, 28, 28] 채널, 높이, 너비 정수 (0~9)

📊 텐서(Tensor) 형태:
이미지: [1, 28, 28] 형태의 3D 텐서
└ 1: 채널 수 (흑백이므로 1, 컬러면 3: RGB)
└ 28: 이미지 높이
└ 28: 이미지 너비

레이블: 0~9 사이의 정수 하나
└ 예: 이미지가 "7"이면 레이블은 정수 7

3. 데이터셋 분할: Train, Validation, Test

머신러닝에서는 데이터를 세 부분으로 나누는 것이 일반적입니다. 각각의 역할을 이해하는 것이 매우 중요합니다.

📚 데이터셋의 3대 구성 요소

전체 데이터 (70,000개) 100% Train 60% 54,000개 (훈련용) Val 10% 6,000개 Test 10,000개

🎓 Training Set (훈련 세트)

목적: 모델을 학습시키는 데 사용
비율: 보통 전체의 60~80%
역할: 이 데이터로 가중치를 업데이트하며 학습
비유: 학생이 공부하는 교과서와 문제집

🔍 Validation Set (검증 세트)

목적: 학습 중 모델 성능을 모니터링
비율: 보통 전체의 10~20%
역할: 하이퍼파라미터 튜닝, 조기 종료 결정
비유: 모의고사 - 실력을 확인하고 학습 방향 조정

🎯 Test Set (테스트 세트)

목적: 최종 모델 성능 평가
비율: 보통 전체의 10~20%
역할: 한 번만 사용해서 최종 성능 측정
비유: 수능 - 진짜 실력을 평가하는 최종 시험

⚠️ 왜 세 개로 나눠야 할까?

만약 훈련 데이터로만 평가한다면 어떻게 될까요?

❌ 문제: 과적합 (Overfitting)

학생이 문제집의 답을 외워버린 상황과 같습니다.
문제집 문제는 100점이지만, 실제 시험에서는 형편없는 점수를 받죠.

마찬가지로 모델이 훈련 데이터는 완벽하게 맞추지만,
새로운 데이터는 전혀 맞추지 못하는 현상이 발생합니다.

✅ 해결책: 데이터 분할

Train: 문제 푸는 방법을 배움
Validation: 중간에 이해도 확인하고 학습 방향 조정
Test: 최종 실력 평가

이렇게 하면 모델이 정말로 "이해"했는지, 아니면 그냥 "암기"했는지 알 수 있습니다!

4. 학습 과정: Epoch과 배치(Batch)

60,000개의 훈련 데이터를 어떻게 학습시킬까요? 한 번에 다 넣을 수는 없습니다. 여기서 EpochBatch 개념이 등장합니다.

🔄 Epoch (에포크)

전체 훈련 데이터를 한 번 다 본 것을 1 epoch이라고 합니다.

예를 들어 10 epochs 학습한다면?
→ 60,000개의 데이터를 10번 반복해서 학습합니다.

비유: 교과서를 10번 반복해서 읽는 것
한 번 읽을 때마다 이해도가 조금씩 높아지죠!

📦 Batch (배치)

한 번에 처리하는 데이터의 개수를 배치 크기(Batch Size)라고 합니다.

60,000개 훈련 데이터 (1 Epoch) Batch 1 64개 Batch 2 64개 Batch 3 64개 ... Batch N 64개 배치 크기 = 64일 때, 총 937개의 배치 (60,000 ÷ 64 ≈ 937) 각 배치마다 순전파 → 역전파 → 가중치 업데이트

왜 배치로 나눌까?

1. 메모리 문제: 60,000개를 한 번에 GPU에 올리면 메모리 부족!
2. 학습 안정성: 작은 배치로 자주 업데이트하면 더 부드럽게 학습
3. 일반화 성능: 너무 큰 배치는 과적합을 유발할 수 있음

일반적인 배치 크기: 32, 64, 128, 256 등 2의 거듭제곱 사용
(GPU 연산 효율 때문)

⚙️ 학습 루프의 전체 흐름

Epoch 1:
  Batch 1 (64개) → 순전파 → 오차 계산 → 역전파 → 가중치 업데이트
  Batch 2 (64개) → 순전파 → 오차 계산 → 역전파 → 가중치 업데이트
  ...
  Batch 937 (64개) → 순전파 → 오차 계산 → 역전파 → 가중치 업데이트
  → Epoch 1 완료! Validation으로 성능 확인

Epoch 2:
  다시 처음부터... (가중치는 Epoch 1에서 학습된 상태로 시작)

...

Epoch 10:
  최종 학습 완료!
  → Test 데이터로 최종 성능 평가

5. GPU의 필요성: 왜 그래픽카드로 학습할까?

MNIST는 작은 데이터셋이지만, 이미지 하나당 784개의 픽셀을 처리해야 합니다. 60,000개를 여러 epoch 반복하면... 어마어마한 계산량이 발생합니다.

🖥️ CPU vs GPU: 무엇이 다를까?

CPU (중앙처리장치)

Core 1 Core 2 Core 3 Core 4 적은 수의 강력한 코어

• 복잡한 연산 처리
• 순차적 작업에 강함
• 코어 수: 보통 4~16개

GPU (그래픽처리장치)

... 수천 개의 작은 코어

• 단순 반복 연산
• 병렬 처리에 강함
• 코어 수: 수천~수만 개

🧮 왜 딥러닝에는 GPU가 필요할까?

딥러닝의 핵심 연산은 행렬 곱셈입니다:
• 784개 픽셀 × 128개 뉴런 → 100,352번의 곱셈
• 이런 연산을 배치 64개, 여러 층에서 동시에!

CPU: 4개 코어가 순차적으로 처리 → 느림 🐢
GPU: 수천 개 코어가 동시에 처리 → 빠름 🚀

실제 속도 차이:
• CPU: MNIST 학습 1 epoch → 약 30초
• GPU: MNIST 학습 1 epoch → 약 3초
약 10배 빠릅니다! (더 큰 모델에선 100배 이상 차이)

⚡ PyTorch에서 GPU 사용하기

import torch

# GPU 사용 가능 여부 확인
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f'Using device: {device}')

# 모델을 GPU로 이동
model = model.to(device)

# 데이터도 GPU로 이동
for inputs, labels in train_loader:
    inputs = inputs.to(device)
    labels = labels.to(device)
    
    # 이제 GPU에서 빠르게 계산!
    outputs = model(inputs)

💡 Tip: Google Colab을 사용하면 무료로 GPU를 사용할 수 있습니다!
런타임 → 런타임 유형 변경 → GPU 선택

6. MNIST 실습 결과

이제 실제로 PyTorch를 이용해 MNIST 손글씨를 학습시킨 결과를 확인해봅시다. 아래 노트북에서는 다음 내용을 확인할 수 있습니다:

🎉 축하합니다!

여러분은 방금 실제 이미지 데이터로 딥러닝 모델을 학습시켰습니다!

지금까지 배운 내용을 정리하면:
Step 1-3: 퍼셉트론의 한계와 XOR 문제
Step 4: MLP와 역전파로 XOR 해결
Step 5: 실전 데이터(MNIST)로 이미지 분류 성공! ✅

다음 단계에서는 더 복잡한 이미지를 다루는 합성곱 신경망(CNN)을 배워봅시다.