딥러닝은 현재 인공지능 분야에서 가장 중요한 기술 중 하나로 자리 잡고 있으며, 그중에서도 신경망(Neural Networks)은 다양한 문제를 해결하는 데 널리 사용됩니다. 본 강좌에서는 GoogLeNet이라는 CNN(Convolutional Neural Network)을 자세히 살펴보겠습니다. GoogLeNet은 2014년 ILSVRC(Imagenet Large Scale Visual Recognition Challenge)에서 우승하며 큰 주목을 받았습니다.
1. GoogLeNet 개요
GoogLeNet은 ‘Inception v1’이라는 이름으로도 알려져 있으며, 여러 개의 합성곱(Convolution) 레이어가 포함된 독창적인 구조를 가지고 있습니다. GoogLeNet의 주요 특징은 다양한 크기의 필터를 동시에 사용하여 이미지를 처리하는 ‘Inception 모듈’입니다. 이러한 방식은 네트워크가 더 많은 정보를 손실 없이 학습할 수 있도록 도와줍니다.
2. GoogLeNet의 구조
- 입력 레이어: 224×224 크기의 이미지를 입력받습니다.
- Convolution Layer: 다양한 크기의 필터(1×1, 3×3, 5×5)를 사용합니다.
- Pooling Layer: 다운 샘플링을 통해 특징 맵의 크기를 줄입니다.
- Fully Connected Layer: 최종 출력으로 분류 결과를 제공합니다.
2.1 Inception 모듈
Inception 모듈은 여러 개의 필터를 사용하여 서로 다른 수준의 세부 사항을 포착합니다. 각 모듈은 다음과 같이 구성됩니다:
- 1×1 합성곱
- 3×3 합성곱
- 5×5 합성곱
- 3×3 맥스 풀링(Max Pooling)
이 모든 출력을 결합하여 다음 레이어로 전달합니다. 이를 통해 다양한 스케일의 특징을 획득할 수 있습니다.
3. 파이토치에서 GoogLeNet 구현하기
이제 PyTorch에서 GoogLeNet을 구현하는 방법을 살펴보겠습니다. 먼저 PyTorch와 기타 필수 라이브러리를 설치해야 합니다.
pip install torch torchvision
3.1 데이터셋 준비
예제에서는 CIFAR-10 데이터셋을 사용할 것입니다. 이 데이터셋은 10개의 클래스로 이루어진 60,000개의 이미지를 포함하고 있습니다.
import torch
import torchvision
import torchvision.transforms as transforms
# 데이터 변환 정의
transform = transforms.Compose(
[transforms.Resize((224, 224)),
transforms.ToTensor()])
# CIFAR-10 데이터셋 다운로드
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=32,
shuffle=True, num_workers=2)
testset = torchvision.datasets.CIFAR10(root='./data', train=False,
download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=32,
shuffle=False, num_workers=2)
3.2 GoogLeNet 모델 정의
다음으로 GoogLeNet 모델을 정의하겠습니다. 이에 사용될 Inception 모듈을 작성하겠습니다.
import torch.nn as nn
import torch.nn.functional as F
class Inception(nn.Module):
def __init__(self, in_channels):
super(Inception, self).__init__()
self.branch1x1 = nn.Sequential(
nn.Conv2d(in_channels, 64, kernel_size=1),
nn.BatchNorm2d(64),
nn.ReLU(inplace=True)
)
self.branch3x3 = nn.Sequential(
nn.Conv2d(in_channels, 128, kernel_size=1),
nn.BatchNorm2d(128),
nn.ReLU(inplace=True),
nn.Conv2d(128, 128, kernel_size=3, padding=1),
nn.BatchNorm2d(128),
nn.ReLU(inplace=True)
)
self.branch5x5 = nn.Sequential(
nn.Conv2d(in_channels, 32, kernel_size=1),
nn.BatchNorm2d(32),
nn.ReLU(inplace=True),
nn.Conv2d(32, 64, kernel_size=5, padding=2),
nn.BatchNorm2d(64),
nn.ReLU(inplace=True)
)
self.branch_pool = nn.Sequential(
nn.MaxPool2d(kernel_size=3, stride=1, padding=1),
nn.Conv2d(in_channels, 32, kernel_size=1),
nn.BatchNorm2d(32),
nn.ReLU(inplace=True)
)
def forward(self, x):
branch1 = self.branch1x1(x)
branch3 = self.branch3x3(x)
branch5 = self.branch5x5(x)
branch_pool = self.branch_pool(x)
outputs = [branch1, branch3, branch5, branch_pool]
return torch.cat(outputs, 1)
3.3 전체 GoogLeNet 정의
class GoogLeNet(nn.Module):
def __init__(self, num_classes=10):
super(GoogLeNet, self).__init__()
self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3)
self.pool1 = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
self.conv2 = nn.Conv2d(64, 192, kernel_size=3, padding=1)
self.pool2 = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
self.inception1 = Inception(192)
self.inception2 = Inception(256)
self.inception3 = Inception(480)
self.pool3 = nn.AvgPool2d(kernel_size=7)
self.fc = nn.Linear(480, num_classes)
def forward(self, x):
x = F.relu(self.conv1(x))
x = self.pool1(x)
x = F.relu(self.conv2(x))
x = self.pool2(x)
x = self.inception1(x)
x = self.inception2(x)
x = self.inception3(x)
x = self.pool3(x)
x = x.view(x.size(0), -1)
x = self.fc(x)
return x
model = GoogLeNet()
3.4 손실 함수 및 옵티마이저 정의
모델을 훈련할 준비가 되었으므로 손실 함수와 옵티마이저를 정의합니다.
import torch.optim as optim
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
3.5 모델 훈련
이제 모델을 훈련하겠습니다. 주어진 Epoch 동안 손실과 정확도를 추적하겠습니다.
num_epochs = 10
for epoch in range(num_epochs):
model.train()
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
inputs, labels = data
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
if i % 100 == 99: # 매 100 배치마다 출력
print(f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{len(trainloader)}], Loss: {running_loss / 100:.4f}')
running_loss = 0.0
print('훈련 완료')
print('모델 훈련 끝!')
3.6 모델 평가
훈련이 완료되면 테스트 데이터셋을 사용하여 모델의 성능을 평가합니다.
correct = 0
total = 0
model.eval()
with torch.no_grad():
for data in testloader:
images, labels = data
outputs = model(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print(f'정확도: {100 * correct / total:.2f}%')
4. 결론
GoogLeNet은 다양한 스케일의 특징을 활용할 수 있는 강력한 네트워크 구조를 제공합니다. 이번 강좌를 통해 GoogLeNet의 기본 개념 및 PyTorch에서의 구현 방법을 배웠습니다. 이러한 이해를 통해 보다 복잡한 모델에서도 유사한 방식을 적용할 수 있을 것입니다.
추가적으로, GoogLeNet은 많은 변형이 존재합니다. Inception v2, Inception v3 등 모델의 깊이나 구조를 조정하여 성능을 개선할 수 있습니다. 이러한 변형 모델들은 더욱 정확한 예측을 도와줄 것입니다. 다음 강좌에서는 이러한 변형 모델들에 대해서도 다룰 예정입니다.
이상으로 GoogLeNet에 대한 설명을 마치겠습니다. 감사합니다!