1. 컨볼루션이란?
컨볼루션은 이미지와 필터(고정된 가중치 집합) 간의 내적 연산을 의미합니다. 각 픽셀에 해당하는 값을 곱하고 이를 모두 더하는 방식으로 특성을 추출합니다.
2. CNN 구성 요소
(1) 컨볼루션 레이어
- 기능: 입력 데이터에서 중요한 특징을 추출.
- 매개변수: 필터 크기(예: 3x3, 5x5), 스트라이드(픽셀 이동 단위), 패딩(데이터 손실 방지를 위해 추가).
(2) 풀링 레이어
- 기능: 데이터 차원 축소 및 계산량 감소.
- 종류: 최대값 풀링(최댓값 선택), 평균 풀링(평균값 선택).
(3) 활성화 함수
- 기능: 비선형성을 도입하여 복잡한 패턴 학습 가능.
- 유형: ReLU(음수는 0, 양수는 그대로 출력), Sigmoid(0~1 사이 값 출력), Softmax(분류 문제를 위한 확률 분포 변환).
(4) 완전 연결 레이어
- 기능: 앞서 추출된 특징들을 종합하여 최종 분류 또는 회귀 수행.
3. CNN 구조
입력층 → 컨볼루션층 → 풀링층 → 완전 연결층 → 출력층 순으로 구성됩니다.
4. 코드 예제
(1) 데이터 전처리
import torch
from torchvision import transforms
from PIL import Image
class CustomDataset(torch.utils.data.Dataset):
def __init__(self, file_path, transform=None):
self.file_path = file_path
self.transform = transform
with open(file_path, 'r') as f:
lines = [line.strip().split(' ') for line in f]
self.images, self.labels = zip(*lines)
def __len__(self):
return len(self.images)
def __getitem__(self, idx):
image = Image.open(self.images[idx])
label = int(self.labels[idx])
if self.transform:
image = self.transform(image)
return image, torch.tensor(label)
transform = transforms.Compose([
transforms.Resize((128, 128)),
transforms.ToTensor()
])
dataset = CustomDataset('./data.txt', transform=transform)
dataloader = torch.utils.data.DataLoader(dataset, batch_size=32, shuffle=True)
(2) 모델 정의
import torch.nn as nn
import torch.nn.functional as F
class CNNModel(nn.Module):
def __init__(self):
super(CNNModel, self).__init__()
self.feature_extractor = nn.Sequential(
nn.Conv2d(3, 32, kernel_size=3, padding=1),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2),
nn.Conv2d(32, 64, kernel_size=3, padding=1),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2)
)
self.classifier = nn.Sequential(
nn.Linear(64 * 32 * 32, 128),
nn.ReLU(),
nn.Linear(128, 10)
)
def forward(self, x):
x = self.feature_extractor(x)
x = torch.flatten(x, 1)
x = self.classifier(x)
return x
device = 'cuda' if torch.cuda.is_available() else 'cpu'
model = CNNModel().to(device)
print(model)
(3) 학습 및 평가 함수
def train_model(dataloader, model, loss_fn, optimizer):
model.train()
for images, labels in dataloader:
images, labels = images.to(device), labels.to(device)
outputs = model(images)
loss = loss_fn(outputs, labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
def evaluate_model(dataloader, model, loss_fn):
model.eval()
total_loss, correct = 0, 0
with torch.no_grad():
for images, labels in dataloader:
images, labels = images.to(device), labels.to(device)
outputs = model(images)
total_loss += loss_fn(outputs, labels).item()
_, preds = torch.max(outputs, 1)
correct += (preds == labels).sum().item()
avg_loss = total_loss / len(dataloader)
accuracy = correct / len(dataloader.dataset)
print(f"Accuracy: {accuracy:.4f}, Avg Loss: {avg_loss:.4f}")
(4) 실행
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
epochs = 10
for epoch in range(epochs):
print(f"Epoch {epoch+1}/{epochs}")
train_model(dataloader, model, loss_fn, optimizer)
evaluate_model(dataloader, model, loss_fn)