딥러닝 이야기 / Convolutional Neural Network (CNN) & Residual Network (ResNet) / 2. CNN을 이용한 MNIST 분류

작성일: 2022.07.25
시작하기 앞서 틀린 부분이 있을 수 있으니, 틀린 부분이 있다면 지적해주시면 감사하겠습니다.
이전글에서는 convolutional neural network (CNN)에 대해 설명하였습니다.
이번글에서는 CNN 모델을 구성하여 데이터 분류기를 학습해보도록 하겠습니다.
학습에 사용한 데이터는 MNIST 데이터를 사용하였으며, 구현은 python의 PyTorch를 이용하였습니다. 그리고 CNN을 학습하면서 training set과 validation set의 loss의 변화도 함께 살펴보겠습니다.
그리고 CNN에 대한 설명은 이전글을 참고하시기 바랍니다.
이렇게 구현한 CNN의 코드는 GitHub에 올려놓았으니 아래 링크를 참고하시기 바랍니다(본 글에서는 모델의 구현에 초점을 맞추고 있기 때문에, 데이터 전처리 및 학습을 위한 전체 코드는 아래 GitHub 링크를 참고하시기 바랍니다).
오늘의 컨텐츠입니다.
- CNN 구현
- CNN 분류기 학습
- CNN 분류기 학습 결과
CNN 데이터 분류기 모델
”
여기서는 기본적인 CNN의 구현 코드를 살펴보겠습니다. 코드는 PyTorch로 작성 되었으며, 데이터 분류기 모델을 구성해야하기 때문에 2개의 convolutional layer와 label 예측을 위한 fully-connected layer를 제작해야 합니다. CNN 분류기 모델 자체는 매우 단순한 구조를 가지고 있지만, input 데이터에 대해 convolutional layer를 거친 후 나오는 결과의 크기를 계산할 수 있어야 이후 layer를 구성할 수 있다는 점 참고하시기 바랍니다.
class CNN(nn.Module):
def __init__(self, config:Config, color_channel:int):
super(CNN, self).__init__()
self.height = config.height
self.width = config.width
self.label = config.label
self.color_channel = color_channel
self.conv1 = nn.Sequential(
nn.Conv2d(in_channels=self.color_channel, out_channels=32, kernel_size=7, stride=1, padding=0),
nn.BatchNorm2d(32),
nn.ReLU()
)
self.conv2 = nn.Sequential(
nn.Conv2d(in_channels=32, out_channels=64, kernel_size=7, stride=1, padding=0),
nn.BatchNorm2d(64),
nn.ReLU()
)
self.fc = nn.Linear(64 * (self.height-12) * (self.width-12), self.label)
def forward(self, x):
output = self.conv1(x)
output = self.conv2(output)
output = torch.flatten(output, 1)
output = self.fc(output)
return output
CNN
위 코드에서 나오는 config 부분은 GitHub 코드에 보면 config.json이라는 파일에 존재하는 변수 값들을 모델에 적용하여 초기화 하는 것입니다.
- 4, 5번째 줄: 학습 이미지를 모두 같은 크기로 전처리 하였을 때의 세로 가로 크기.
- 6번째 줄: 분류해야하는 label 종류 개수. 여기서는 MNIST 데이터를 사용하므로 10.
- 7번째 줄: 이미지 전처리를 하였을 때, color channel 수(흑백으로 처리를 했다면 1, 칼라로 처리 했다면 3).
- 9 ~ 18번째 줄: 두 개의 convolutional layer 정의. 각 레이어에는 overfitting을 방지하기 위해 batch normalization이 사용됨. 또한 첫 번째 convolutional layer의 결과 크기를 알아야 두 번째 레이어를 구성할 수 있음.
- 19번째 줄: Convolutional layer를 나온 결과를 바탕으로 데이터를 분류해야하므로, label 수만큼 결과가 나오도록 fully-connected layer 구성.
- 22 ~ 27번째 줄: 실제 학습할 때 거치는 부분. 25번째 줄에서 (batch * channel * height * width)의 4차원 데이터를 (batch * (channel * height * width))의 2차원 데이터로 변환.
”
이제 분류기 학습 코드를 통해 어떻게 학습이 이루어지는지 살펴보겠습니다. 아래 코드에 self. 이라고 나와있는 부분은 GitHub 코드에 보면 알겠지만 학습하는 코드가 class 내부의 변수이기 때문에 있는 것입니다. 여기서는 무시해도 좋습니다.
self.model = CNN(self.config, self.color_channel).to(self.device)
self.criterion = nn.CrossEntropyLoss()
self.optimizer = optim.Adam(self.model.parameters(), lr=self.lr)
for epoch in range(self.epochs):
print(epoch+1, '/', self.epochs)
print('-'*10)
for phase in ['train', 'val']:
if phase == 'train':
self.model.train()
else:
self.model.eval()
for i, (x, y) in enumerate(self.dataloaders[phase]):
batch = x.size(0)
x, y = x.to(self.device), y.to(self.device)
self.optimizer.zero_grad()
with torch.set_grad_enabled(phase=='train'):
output = self.model(x)
loss = self.criterion(output, y)
if phase == 'train':
loss.backward()
self.optimizer.step()
학습에 필요한 것들 선언
먼저 위에 코드에서 정의한 모델을 불러오고 학습에 필요한 loss function, optimizer 등을 선언하는 부분입니다.
- 1 ~ 3번째 줄: Loss function 및 모델 선언 및 각각의 optimizer 선언.
분류기 학습
다음은 분류기 학습 부분입니다. 코드상에서는 5 ~ 26번째 줄에 해당하는 부분입니다.
- 17번째 줄: x는 MNIST 이미지, y는 각 이미지에 해당하는 label을 의미.
- 20 ~ 26번째 줄: loss를 계산하고, loss를 바탕으로 모델을 업데이트 하는 부분.
”
아래는 20 epoch을 학습하는 동안의 training set과 validation set의 loss 곡선입니다. Validation set에 대해 loss가 최저일 때 epoch이 12 epoch임을 확인할 수 있습니다.
데이터 분류기 학습 loss
아래는 12 epoch 모델을 가지고 실제 MNIST를 예측해본 결과입니다.
아래 맨 오른쪽 그림의 데이터는 틀리게 예측하는 것을 확인할 수 있습니다.
MNIST 예측 결과
지금까지 CNN을 통한 데이터 분류기 구현 코드를 살펴보았습니다.
학습 과정에 대한 전체 코드는 GitHub에 있으니 참고하시면 될 것 같습니다.
다음에는 깊은 CNN 모델이 가지고 있었던 graident vanishing 문제를 residual connection으로 해결하여, CNN의 혁명적인 모델이었던 Residual Network (ResNet)에 대해 이야기 해보겠습니다.