
📓Google Colab에서 실습하기
이 레슨은 PyTorch/GPU가 필요합니다. 노트북을 다운로드 후 Google Colab에서 열어주세요.
학습 내용
이미지 분류 CNN
학습 목표
이 레슨을 완료하면:
- •CNN(합성곱 신경망)이 이미지 처리에 효과적인 이유를 설명합니다
- •Conv2d, MaxPool2d, BatchNorm2d 레이어의 동작을 이해합니다
- •CIFAR-10 데이터셋으로 이미지 분류기를 구현합니다
- •특성 맵(feature map)의 크기 변화를 계산할 수 있습니다
왜 CNN이 필요한가?
비유: MLP로 이미지를 처리하는 것은, 퍼즐 조각을 일렬로 늘어놓고 원래 그림을 맞추라는 것과 같습니다. 조각들의 위치 관계가 사라지니 그림을 파악하기 매우 어렵습니다. CNN은 퍼즐 조각을 원래 위치 그대로 보면서 패턴을 찾습니다.
MLP의 한계
MLP는 이미지를 1차원으로 펼쳐야(flatten) 합니다.
| 문제점 | 설명 | 예시 |
|---|---|---|
| 공간 정보 손실 | 인접 픽셀 관계가 사라짐 | "눈 위에 이마"라는 정보 상실 |
| 파라미터 폭발 | 모든 픽셀이 모든 뉴런에 연결 | 32x32x3 이미지 -> 3,072개 입력 |
| 위치 의존성 | 같은 물체가 다른 위치에 있으면 새로 학습 필요 | 왼쪽 고양이와 오른쪽 고양이를 다르게 인식 |
CNN의 해결책
CNN은 세 가지 핵심 아이디어로 이 문제들을 해결합니다:
| 핵심 아이디어 | 설명 | 효과 |
|---|---|---|
| 지역 연결 (Local Connectivity) | 작은 영역(3x3)만 보고 특징 추출 | 공간 정보 보존 |
| 가중치 공유 (Weight Sharing) | 같은 필터를 이미지 전체에 적용 | 파라미터 수 대폭 감소 |
| 계층적 특징 학습 | 낮은 층은 선/모서리, 높은 층은 복잡한 패턴 | 추상화 수준별 학습 |
CNN 핵심 레이어 상세 설명
1. Conv2d (합성곱 레이어)
비유: 합성곱은 돋보기로 이미지를 훑는 것과 같습니다. 3x3 크기의 돋보기(필터)가 이미지 위를 한 칸씩 이동하면서 "이 부분에 세로선이 있나?", "모서리가 있나?" 같은 질문에 답합니다.
python# Conv2d의 주요 파라미터 nn.Conv2d( in_channels=3, # 입력 채널 수 (RGB 이미지 = 3) out_channels=32, # 출력 채널 수 (= 필터 개수) kernel_size=3, # 필터 크기 (3x3) stride=1, # 이동 보폭 (기본값 1) padding=1 # 테두리 패딩 (출력 크기 유지) )
출력 크기 계산 공식:
출력 크기 = (입력 크기 - 커널 크기 + 2 x 패딩) / 스트라이드 + 1
| 입력 | kernel_size | padding | stride | 출력 |
|---|---|---|---|---|
| 32x32 | 3 | 1 | 1 | 32x32 |
| 32x32 | 3 | 0 | 1 | 30x30 |
| 32x32 | 5 | 2 | 1 | 32x32 |
| 32x32 | 3 | 1 | 2 | 16x16 |
2. MaxPool2d (최대 풀링)
비유: 풀링은 이미지를 요약하는 것입니다. 2x2 영역에서 가장 두드러진 특징(최댓값)만 남기고 나머지는 버립니다. 마치 긴 글을 핵심 문장만 남기고 요약하는 것과 같습니다.
pythonnn.MaxPool2d(kernel_size=2, stride=2) # 2x2 영역에서 최댓값만 선택 # 결과: 크기가 절반으로 줄어듦 (32x32 -> 16x16)
풀링의 효과:
- •계산량 감소 (크기가 1/4로)
- •작은 위치 변화에 강건해짐 (위치 불변성)
- •과적합 방지에 도움
3. BatchNorm2d (배치 정규화)
비유: 배치 정규화는 각 레이어의 출력을 "표준 점수"로 변환하는 것입니다. 학생들의 시험 점수가 과목마다 다른 범위를 가지면 비교가 어렵듯, 레이어 출력의 분포가 들쭉날쭉하면 학습이 불안정해집니다.
pythonnn.BatchNorm2d(32) # 32개 채널 각각을 정규화
효과: 학습이 더 빠르고 안정적으로 진행됩니다.
CIFAR-10 데이터셋
CIFAR-10은 MNIST보다 한 단계 어려운 이미지 분류 데이터셋입니다.
| 항목 | MNIST | CIFAR-10 |
|---|---|---|
| 이미지 크기 | 28x28 | 32x32 |
| 색상 | 흑백 (1채널) | 컬러 (3채널) |
| 클래스 수 | 10 (숫자) | 10 (사물) |
| 훈련 데이터 | 60,000 | 50,000 |
| 테스트 데이터 | 10,000 | 10,000 |
| 난이도 | 쉬움 | 중간 |
CIFAR-10의 10개 클래스: 비행기, 자동차, 새, 고양이, 사슴, 개, 개구리, 말, 배, 트럭
pythonfrom torchvision import datasets, transforms # 훈련용 변환 (데이터 증강 포함) train_transform = transforms.Compose([ transforms.RandomHorizontalFlip(), # 50% 확률로 좌우 반전 transforms.RandomCrop(32, padding=4), # 무작위 자르기 transforms.ToTensor(), transforms.Normalize((0.4914, 0.4822, 0.4465), # CIFAR-10 평균 (0.2470, 0.2435, 0.2616)) # CIFAR-10 표준편차 ]) # 테스트용 변환 (증강 없음 - 공정한 평가를 위해) test_transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2470, 0.2435, 0.2616)) ]) train_dataset = datasets.CIFAR10( root="./data", train=True, download=True, transform=train_transform ) test_dataset = datasets.CIFAR10( root="./data", train=False, download=True, transform=test_transform )
CNN 모델 구현
특성 맵 크기 변화 추적
모델을 설계할 때는 각 레이어를 통과하면서 데이터 크기가 어떻게 변하는지 추적해야 합니다.
| 레이어 | 출력 크기 | 채널 수 | 설명 |
|---|---|---|---|
| 입력 | 32x32 | 3 | RGB 이미지 |
| Conv Block 1 | 32x32 | 32 | 기본 특징 추출 |
| MaxPool | 16x16 | 32 | 다운샘플링 |
| Conv Block 2 | 16x16 | 64 | 중간 특징 추출 |
| MaxPool | 8x8 | 64 | 다운샘플링 |
| Conv Block 3 | 8x8 | 128 | 고수준 특징 추출 |
| MaxPool | 4x4 | 128 | 다운샘플링 |
| Flatten | 2048 | - | 1차원 변환 (128x4x4) |
| FC | 256 | - | 분류 준비 |
| 출력 | 10 | - | 클래스별 점수 |
pythonclass ImageClassifier(nn.Module): def __init__(self, num_classes=10): super().__init__() # 특징 추출부: 이미지에서 패턴을 찾아내는 부분 self.features = nn.Sequential( # Block 1: 선, 모서리 같은 저수준 특징 추출 nn.Conv2d(3, 32, kernel_size=3, padding=1), # 3채널 -> 32채널 nn.BatchNorm2d(32), nn.ReLU(), nn.MaxPool2d(2, 2), # 32x32 -> 16x16 # Block 2: 텍스처, 모양 같은 중수준 특징 추출 nn.Conv2d(32, 64, kernel_size=3, padding=1), # 32채널 -> 64채널 nn.BatchNorm2d(64), nn.ReLU(), nn.MaxPool2d(2, 2), # 16x16 -> 8x8 # Block 3: 부위, 구조 같은 고수준 특징 추출 nn.Conv2d(64, 128, kernel_size=3, padding=1), # 64채널 -> 128채널 nn.BatchNorm2d(128), nn.ReLU(), nn.MaxPool2d(2, 2), # 8x8 -> 4x4 ) # 분류부: 추출된 특징을 바탕으로 클래스를 결정하는 부분 self.classifier = nn.Sequential( nn.Flatten(), # 128x4x4 = 2048차원 벡터로 변환 nn.Linear(128 * 4 * 4, 256), # 2048 -> 256 nn.ReLU(), nn.Dropout(0.5), # 과적합 방지 nn.Linear(256, num_classes) # 256 -> 10 (클래스 수) ) def forward(self, x): x = self.features(x) # 특징 추출 x = self.classifier(x) # 분류 return x model = ImageClassifier(num_classes=10).to(device)
MLP vs CNN 성능 비교
| 항목 | MLP | CNN |
|---|---|---|
| MNIST 정확도 | ~98% | ~99.5% |
| CIFAR-10 정확도 | ~55% | ~85% |
| 파라미터 수 (CIFAR) | 수백만 개 | 수십만 개 |
| 공간 정보 활용 | 불가 | 가능 |
| 위치 불변성 | 없음 | 있음 (풀링 덕분) |
| 적합한 데이터 | 정형 데이터 | 이미지, 공간 데이터 |
CNN이 CIFAR-10에서 MLP보다 30% 이상 높은 정확도를 보이는 이유는, 이미지의 공간적 구조를 그대로 활용하기 때문입니다.
실용적 팁
- •padding=1 + kernel_size=3: 이 조합은 입력과 출력 크기를 동일하게 유지합니다. 크기 줄이기는 MaxPool에 맡기세요.
- •BatchNorm은 Conv 뒤, ReLU 앞에: Conv -> BatchNorm -> ReLU가 표준 순서입니다.
- •채널 수는 점진적으로 증가: 32 -> 64 -> 128처럼 2배씩 늘리는 것이 일반적입니다.
- •Dropout은 FC 레이어에: 합성곱 레이어보다 FC 레이어에서 과적합이 더 심합니다.
핵심 요약
| 개념 | 설명 | 비유 |
|---|---|---|
| Conv2d | 필터로 지역 패턴 추출 | 돋보기로 세부 관찰 |
| MaxPool2d | 크기 축소 + 핵심만 보존 | 긴 글을 요약 |
| BatchNorm2d | 분포 안정화 | 시험 점수 표준화 |
| 특징 추출부 | 이미지에서 패턴 탐색 | 사진에서 단서 찾기 |
| 분류부 | 패턴으로 클래스 결정 | 단서로 정답 판단 |
학습 체크리스트
- • MLP가 이미지 처리에 부적합한 이유 3가지를 말할 수 있다
- • Conv2d의 출력 크기를 계산할 수 있다
- • MaxPool2d가 하는 일과 효과를 설명할 수 있다
- • BatchNorm의 역할을 설명할 수 있다
- • 특징 추출부와 분류부의 역할 차이를 안다
- • CNN이 MLP보다 이미지에서 뛰어난 이유를 설명할 수 있다
레슨 정보
- 레벨
- Level 4: 실전 프로젝트
- 예상 소요 시간
- 약 5분
- 참고 영상
- YouTube 링크
💡실습 환경 안내
이 레벨은 PyTorch/GPU가 필요하여 Google Colab 사용을 권장합니다.
Colab은 무료 GPU를 제공하여 PyTorch, CNN, Transformer 등을 실행할 수 있습니다.