CNN with Python
by DINHO
오늘은 Python으로 CNN을 복습해볼까 합니다. 인공지능을 공부하는 사람이라면 모를 수가 없는데요. 학부 시절 풀었던 과제를 바탕으로 포스팅하겠습니다.
이 내용은 광운대학교 전기공학과 인공지능응용 수업 과제입니다. 혹시라도 후배님들이 이 글을 보게 된다면 비밀로 하고 과제를 진행해주세요.
CNN (Convolutional Neural Network) 설명
1. 핵심 개념
CNN(Convolutional Neural Network)은 주로 이미지 데이터를 처리하기 위해 설계된 딥러닝 모델입니다. (현재는 이미지 뿐만 아니라 여러 도메인에서 사용됩니다.)
핵심 아이디어는 다음과 같습니다.
이미지의 공간적 구조(위치 관계)를 유지하면서 특징을 추출한다.
일반적인 Fully Connected Layer는 이미지를 1차원으로 펼치기 때문에 공간 정보가 사라집니다.
반면 CNN은 필터(Filter)를 사용하여 이미지의 지역적 특징(local feature)을 추출합니다.
2. CNN의 기본 구조
CNN은 일반적으로 다음 세 가지 계층으로 구성됩니다.
-
Convolution Layer
-
Pooling Layer
-
Fully Connected Layer
3. Convolution Layer (핵심)
개념
이미지 위를 작은 필터가 이동하면서 연산을 수행합니다.
[ \text{Output} = \text{Input} * \text{Filter} ]
여기서 *는 convolution 연산을 의미합니다.
직관적 이해
필터는 특정 특징을 탐지하는 역할을 합니다.
예를 들어:
-
수직선 검출
-
수평선 검출
-
모서리(edge) 검출
즉, CNN은 이미지에서 패턴을 자동으로 학습합니다.
주요 파라미터
-
Filter size (예: 3×3)
-
Stride (이동 간격)
-
Padding (테두리 처리 방식)
4. Pooling Layer
역할
-
Feature map 크기 축소
-
연산량 감소
-
Overfitting 방지
대표 방식
-
Max Pooling: 가장 큰 값 선택
-
Average Pooling: 평균값 선택
5. Fully Connected Layer
마지막 단계에서는 추출된 feature를 기반으로 classification을 수행합니다.
이 부분은 일반적인 Neural Network와 동일한 구조입니다.
6. 전체 흐름
-
이미지 입력
-
Convolution을 통해 특징 추출
-
Pooling을 통해 크기 축소 및 정리
-
위 과정을 반복 (깊어질수록 고차원 특징 학습)
-
Fully Connected Layer에서 최종 출력
7. 중요한 특징
(1) Local Receptive Field
전체 이미지가 아닌 일부 영역만을 보고 특징을 추출합니다.
(2) Weight Sharing
같은 필터를 전체 이미지에 적용합니다.
이를 통해 파라미터 수를 크게 줄일 수 있습니다.
(3) Translation Invariance
객체의 위치가 변하더라도 동일한 특징을 인식할 수 있습니다.
8. 한계
-
많은 양의 데이터 필요
-
계산 비용이 큼 (특히 깊은 CNN)
-
모델 구조 설계가 복잡함
9. 정리
CNN은 이미지에서 특징을 자동으로 추출하는 필터 기반 신경망입니다.
실습
다음과 같은 CNN 모델을 작성해보자
-
Input
-
Input type: torch.Tensor
-
Input shape: (?, 1, 28, 28)
- 여러장의, 흑백, 28x28 size의 이미지라고 가정하자
-
-
Layers
-
Layer 1
-
Conv2d » C: 32, Kernel size (필터 크기): 3, Stride: 1, Padding: 1
-
ReLU
-
MaxPool » Kernel size: 2, Stride: 2
-
입-출력 (?, 1, 28, 28) » (?, 32, 14, 14)
-
-
Layer 2
-
Conv2d » C: 64, Kernel size (필터 크기): 3, Stride: 1, Padding: 1
-
ReLU
-
MaxPool » Kernel size: 2, Stride: 2
-
입-출력 (?, 32, 14, 14) » (?, 64, 7, 7)
-
-
Layer 3
-
Conv2d » C: 128, Kernel size (필터 크기): 3, Stride: 1, Padding: 1
-
ReLU
-
MaxPool » Kernel size: 2, Stride: 2, Padding: 1
-
입-출력 (?, 64, 7, 7) » (?, 128, 4, 4)
-
-
Layer 4
-
Linear » input: 4x4x128 output: 625
-
ReLU
-
Dropout
-
입-출력 (4x4x128) » (625)
-
-
Layer 5
-
Linear » input: 625 output: 10
-
Softmax (pytorch의 Cross Entropy Loss 함수를 사용하는 것을 감안한다)
-
-
import torch
import torchvision.datasets as dsets
import torchvision.transforms as transforms
import torch.nn.init
device = 'cuda' if torch.cuda.is_available() else 'cpu'
# for reproducibility
torch.manual_seed(777)
if device == 'cuda':
torch.cuda.manual_seed_all(777)
class CustomCNN(nn.Module):
def __init__(self):
super(CustomCNN, self).__init__()
# Layer 1: Conv2d -> ReLU -> MaxPool
# 입력 (?, 1, 28, 28) -> 출력 (?, 32, 14, 14)
self.layer1 = nn.Sequential(
nn.Conv2d(in_channels=1, out_channels=32, kernel_size=3, stride=1, padding=1),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2)
)
# Layer 2: Conv2d -> ReLU -> MaxPool
# 입력 (?, 32, 14, 14) -> 출력 (?, 64, 7, 7)
self.layer2 = nn.Sequential(
nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, stride=1, padding=1),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2)
)
# Layer 3: Conv2d -> ReLU -> MaxPool
# 입력 (?, 64, 7, 7) -> 출력 (?, 128, 4, 4)
self.layer3 = nn.Sequential(
nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=1),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2, padding=1)
)
# Layer 4: Linear -> ReLU -> Dropout
# 입력 (4*4*128) -> 출력 (625)
self.fc1 = nn.Linear(4*4*128, 625)
self.relu = nn.ReLU()
self.dropout = nn.Dropout(p=0.5)
# Layer 5: Linear -> Output
# 입력 (625) -> 출력 (10)
self.fc2 = nn.Linear(625, 10) # CrossEntropyLoss를 사용할 경우, Softmax를 명시적으로 추가할 필요가 없습니다.
def forward(self, x):
# Forward pass through layer 1
x = self.layer1(x)
# Forward pass through layer 2
x = self.layer2(x)
# Forward pass through layer 3
x = self.layer3(x)
# Flatten the output for the fully connected layer
x = x.view(x.size(0), -1)
# Forward pass through layer 4
x = self.fc1(x)
x = self.relu(x)
x = self.dropout(x)
# Forward pass through layer 5
x = self.fc2(x)
return x
# 모델 생성
model = CustomCNN()
print(model)
Follow my github