XOR 문제로 MLP의 원리를 배웠다면, 이제는 실제 데이터로 딥러닝을 경험할 차례입니다. 그 시작점이 바로 MNIST 데이터셋입니다.
Modified National Institute of Standards and Technology database
실제 MNIST 데이터는 이렇게 생겼습니다. 사람들이 직접 손으로 쓴 숫자들이죠.
💡 왜 28×28일까?
1998년 당시 컴퓨터 성능을 고려한 적절한 크기입니다.
너무 크면 계산이 오래 걸리고, 너무 작으면 숫자를 알아보기 어렵죠.
28×28 = 784개의 픽셀이 우리 신경망의 입력이 됩니다!
MNIST는 지도학습의 대표적인 예시입니다. 왜냐하면:
XOR에서는 4개의 데이터로 학습했지만, MNIST는 60,000개의 예제로 학습합니다. 이렇게 많은 데이터로 학습하면 모델은 다양한 필체의 숫자를 인식할 수 있게 됩니다.
실제로 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 데이터는 이렇게 구성되어 있습니다:
📊 텐서(Tensor) 형태:
• 이미지: [1, 28, 28] 형태의 3D 텐서
└ 1: 채널 수 (흑백이므로 1, 컬러면 3: RGB)
└ 28: 이미지 높이
└ 28: 이미지 너비
• 레이블: 0~9 사이의 정수 하나
└ 예: 이미지가 "7"이면 레이블은 정수 7
머신러닝에서는 데이터를 세 부분으로 나누는 것이 일반적입니다. 각각의 역할을 이해하는 것이 매우 중요합니다.
목적: 모델을 학습시키는 데 사용
비율: 보통 전체의 60~80%
역할: 이 데이터로 가중치를 업데이트하며 학습
비유: 학생이 공부하는 교과서와 문제집
목적: 학습 중 모델 성능을 모니터링
비율: 보통 전체의 10~20%
역할: 하이퍼파라미터 튜닝, 조기 종료 결정
비유: 모의고사 - 실력을 확인하고 학습 방향 조정
목적: 최종 모델 성능 평가
비율: 보통 전체의 10~20%
역할: 한 번만 사용해서 최종 성능 측정
비유: 수능 - 진짜 실력을 평가하는 최종 시험
만약 훈련 데이터로만 평가한다면 어떻게 될까요?
❌ 문제: 과적합 (Overfitting)
학생이 문제집의 답을 외워버린 상황과 같습니다.
문제집 문제는 100점이지만, 실제 시험에서는 형편없는 점수를 받죠.
마찬가지로 모델이 훈련 데이터는 완벽하게 맞추지만,
새로운 데이터는 전혀 맞추지 못하는 현상이 발생합니다.
✅ 해결책: 데이터 분할
• Train: 문제 푸는 방법을 배움
• Validation: 중간에 이해도 확인하고 학습 방향 조정
• Test: 최종 실력 평가
이렇게 하면 모델이 정말로 "이해"했는지, 아니면 그냥 "암기"했는지 알 수 있습니다!
60,000개의 훈련 데이터를 어떻게 학습시킬까요? 한 번에 다 넣을 수는 없습니다. 여기서 Epoch와 Batch 개념이 등장합니다.
전체 훈련 데이터를 한 번 다 본 것을 1 epoch이라고 합니다.
예를 들어 10 epochs 학습한다면?
→ 60,000개의 데이터를 10번 반복해서 학습합니다.
비유: 교과서를 10번 반복해서 읽는 것
한 번 읽을 때마다 이해도가 조금씩 높아지죠!
한 번에 처리하는 데이터의 개수를 배치 크기(Batch Size)라고 합니다.
왜 배치로 나눌까?
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 데이터로 최종 성능 평가
MNIST는 작은 데이터셋이지만, 이미지 하나당 784개의 픽셀을 처리해야 합니다. 60,000개를 여러 epoch 반복하면... 어마어마한 계산량이 발생합니다.
• 복잡한 연산 처리
• 순차적 작업에 강함
• 코어 수: 보통 4~16개
• 단순 반복 연산
• 병렬 처리에 강함
• 코어 수: 수천~수만 개
🧮 왜 딥러닝에는 GPU가 필요할까?
딥러닝의 핵심 연산은 행렬 곱셈입니다:
• 784개 픽셀 × 128개 뉴런 → 100,352번의 곱셈
• 이런 연산을 배치 64개, 여러 층에서 동시에!
CPU: 4개 코어가 순차적으로 처리 → 느림 🐢
GPU: 수천 개 코어가 동시에 처리 → 빠름 🚀
실제 속도 차이:
• CPU: MNIST 학습 1 epoch → 약 30초
• GPU: MNIST 학습 1 epoch → 약 3초
→ 약 10배 빠릅니다! (더 큰 모델에선 100배 이상 차이)
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 선택
이제 실제로 PyTorch를 이용해 MNIST 손글씨를 학습시킨 결과를 확인해봅시다. 아래 노트북에서는 다음 내용을 확인할 수 있습니다:
여러분은 방금 실제 이미지 데이터로 딥러닝 모델을 학습시켰습니다!
지금까지 배운 내용을 정리하면:
• Step 1-3: 퍼셉트론의 한계와 XOR 문제
• Step 4: MLP와 역전파로 XOR 해결
• Step 5: 실전 데이터(MNIST)로 이미지 분류 성공! ✅
다음 단계에서는 더 복잡한 이미지를 다루는 합성곱 신경망(CNN)을 배워봅시다.