AlexNet 구조 파악 및 PyTorch로 코드 구현해보기
본 글은 “ImageNet Classification with Deep Convolutional Neural Networks” 논문을 파악하고, 이를 파이토치로 구현해보는 내용입니다.
하나하나 분해해봅시다.
논문 : AlexNet
코드 : kaggle - Fashion MNIST with AlexNet in Pytorch
블로그 글 코드 : alexnet_pytorch.ipynb
파이토치 튜토리얼 : pytorch.org
AlexNet 이란?
- 2012년 ILSVRC 우승 모델
- top-5 error rate : 17%
- 특징
- F9, F10에서 드롭아웃(Dropout) 50% 사용
- 데이터 증식(Data Augmentation) 사용
- 정규화는 LRN(local response normalization) 사용
- LRN : 뉴런의 출력값을 보다 경쟁적으로 만드는 정규화 기법
AlexNet 구조
파이토치로 구현해보기
Dataset : Fashion MNIST
이미지출처 1
- Fashion MNIST 데이터셋 : 운동화, 코트, 가방 등의 작은 이미지 모음 2
- training set : 60000
- test set : 10000
- 28x28 grayscale image
-
labels of 10 classes
Label Class 0 T-shirt/top 1 Trouser 2 Pullover 3 Dress 4 Coat 5 Sandal 6 Shirt 7 Sneaker 8 Bag 9 Ankle boot - feature : PIL image format
- label : integer
라이브러리 불러오기
import os # 파이썬을 이용해 파일을 복사하거나 디렉터리를 생성하고 특정 디렉터리 내의 파일 목록을 구하고자 할 때 사용
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import torch
import torchvision # torchvision package : 컴퓨터 비전을 위한 유명 데이터셋, 모델 아키텍처, 이미지 변형등을 포함
import torch.nn as nn # nn : neural netwroks (define class) attribute를 활용해 state를 저장하고 활용
import torch.optim as optim # 최적화 알고리즘
import torch.nn.functional as F # (define function) 인스턴스화 시킬 필요없이 사용 가능
from PIL import Image
from torchvision import transforms, datasets # transforms : 데이터를 조작하고 학습에 적합하게 만듦.
from torch.utils.data import Dataset, DataLoader
# dataset : 샘플과 정답(label)을 저장
# DataLoader : Dataset 을 샘플에 쉽게 접근할 수 있도록 순회 가능한 객체(iterable)로 감싼다.
에포크, 배치 크기, 디바이스 정의
epochs = 10 # 훈련 반복수
batch_size = 512 # 배치 크기
device = ("cuda" if torch.cuda.is_available() else "cpu") # device 정의
class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',
'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot'] # 총 10개의 클래스
print(torch.__version__)
print(device)
# 결과
# 1.10.0+cu111
# cuda
데이터셋 준비
transform = transforms.Compose([
transforms.Resize(227), # Compose : transforms 리스트 구성
# 227x227 : input image(in alexnet) but fashionMNIST's input image : 28x28
transforms.ToTensor()]) # ToTensor : PIL image or numpy.ndarray를 tensor로 바꿈
training_data = datasets.FashionMNIST(
root="data", # data가 저장될 경로(path)
train=True, # training dataset
download=True, # 인터넷으로부터 데이터 다운
transform=transform # feature 및 label 변환(transformation) 지정
)
validation_data = datasets.FashionMNIST(
root="data",
train=False, # test dataset
download=True,
transform=transform
)
데이터로더 (DataLoader)
- 데이터 로더(DataLoader)는 데이터를 배치(batch) 단위로 모델에 밀어 넣어주는 역할이다.
- 전체 데이터 가운데 일부 인스턴스를 뽑아(sample) 배치를 구성한다.
# (class) DataLoader(dataset, batch_size, shuffle, ...)
training_loader = DataLoader(training_data, batch_size=64, shuffle=True)
validation_loader = DataLoader(validation_data, batch_size=64, shuffle=True)
이미지 보기
# helper function to show an image
def matplotlib_imshow(img):
img = img.mean(dim=0)
img = img / 2 + 0.5
npimg = img.numpy()
plt.imshow(npimg, cmap="Greys")
# get some random training images
dataiter = iter(training_loader) # iter(호출가능한객체, 반복을끝낼값)
images, labels = dataiter.next() # next() : 반복할 수 있을 때는 해당 값을 출력하고, 반복이 끝났을 때는 기본값을 출력
# create grid of images
img_grid = torchvision.utils.make_grid(images[0]) # make_grid : 이미지의 그리드 생성
# show images & labels
matplotlib_imshow(img_grid)
print(class_names[labels[0]])
알렉스넷(AlexNet) 모델 구현
- 5개의 convolution layer, 3개의 fully-connected layer => 총 8개 3
class fashion_mnist_alexnet(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Sequential(
nn.Conv2d(in_channels=1, out_channels=96, kernel_size=11, stride=4, padding=0),
# 4D tensor : [number_of_kernels, input_channels, kernel_width, kernel_height]
# = 96x1x11x11
# input size : 1x227x227
# input size 정의 : (N, C, H, W) or (C, H, W)
# W' = (W-F+2P)/S + 1
# 55x55x96 feature map 생성 (55는 (227-11+1)/4)
# 최종적으로 227 -> 55
nn.ReLU(), # 96x55x55
nn.MaxPool2d(kernel_size=3, stride=2)
# 55 -> (55-3+1)/2 = 26.5 = 27
# 96x27x27 feature map 생성
)
self.conv2 = nn.Sequential(
nn.Conv2d(96, 256, 5, 1, 2), # in_channels: 96, out_channels: 256, kernel_size=5x5, stride=1, padding=2
# kernel 수 = 48x5x5 (드롭아웃을 사용했기 때문에 96/2=48) 형태의 256개
# 256x27x27
nn.ReLU(),
nn.MaxPool2d(3, 2) # 27 -> 13
# 256x13x13
)
self.conv3 = nn.Sequential(
nn.Conv2d(256, 384, 3, 1, 1),
nn.ReLU() # 13 유지
# 384x13x13
)
self.conv4 = nn.Sequential(
nn.Conv2d(384, 384, 3, 1, 1),
nn.ReLU() # 13 유지
# 384x13x13
)
self.conv5 = nn.Sequential(
nn.Conv2d(384, 256, 3, 1, 1),
nn.ReLU(),
nn.MaxPool2d(3, 2) # 13 -> 6
# 256x6x6
)
self.fc1 = nn.Linear(256 * 6 * 6, 4096)
self.fc2 = nn.Linear(4096, 4096)
self.fc3 = nn.Linear(4096, 10)
def forward(self, x): # input size = 3x227x227
out = self.conv1(x)
out = self.conv2(out)
out = self.conv3(out)
out = self.conv4(out)
out = self.conv5(out) # 64x4096x1x1
out = out.view(out.size(0), -1) # 64x4096
out = F.relu(self.fc1(out))
out = F.dropout(out, 0.5)
out = F.relu(self.fc2(out))
out = F.dropout(out, 0.5)
out = self.fc3(out)
out = F.log_softmax(out, dim=1)
return out
모델 생성
model = fashion_mnist_alexnet().to(device) # to()로 모델에 gpu 사용
criterion = F.nll_loss # nll_loss : negative log likelihood loss
optimizer = optim.Adam(model.parameters()) # model(신경망) 파라미터를 optimizer에 전달해줄 때 nn.Module의 parameters() 메소드를 사용
- 모델의 Summary()
from torchsummary import summary as summary_
summary_(model, (1,227,227), batch_size)
# summary_: (model, input_size, batch_size)
train 정의
def train(model, device, train_loader, optimizer, epoch):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
# enumberate() : 인덱스와 원소로 이루어진 튜플(tuple)을 만들어줌
target = target.type(torch.LongTensor)
data, target = data.to(device), target.to(device)
optimizer.zero_grad() # 항상 backpropagation 하기전에 미분(gradient)을 zero로 만들어주고 시작해야 한다.
output = model(data)
loss = criterion(output, target) # criterion = loss_fn
loss.backward() # Computes the gradient of current tensor w.r.t. graph leaves
optimizer.step() # step() : 파라미터를 업데이트함
if (batch_idx + 1) % 30 == 0:
print("Train Epoch:{} [{}/{} ({:.0f}%)]\tLoss: {:.6f}".format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
test 정의
def test(model, device, test_loader):
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += criterion(output, target, reduction='sum').item()
pred = output.max(1, keepdim=True)[1]
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset) # -> mean
print("\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n".format(
test_loss, correct, len(test_loader.dataset), 100. * correct / len(test_loader.dataset)))
print('='*50)
학습 시작하기
for epoch in range(1, epochs+1):
train(model, device, training_loader, optimizer, epoch)
test(model, device, validation_loader)
- 결과 : 10 에포크로 90% 정확도 달성
댓글남기기