elevne's Study Note
딥러닝 파이토치 교과서 (6-4: GoogLeNet, ResNet(1)) 본문
GoogLeNet 은 주어진 하드웨어 자원을 최대한 효율적으로 이용하며 학습 능력은 극대화할 수 있는 깊고 넓은 신경망이라고 한다. GoogLeNet 에는 Inception 모듈이라는 것이 추가되었다. Inception 모듈에서는 특징을 효율적으로 추출하기 위해 1 x 1, 3 x 3, 5 x 5 합성곱 연산을 각각 수행한다. 3 x 3 Max pooling 은 입력과 출력의 높이와 너비가 같아야 하므로 풀링 연산에서는 드물게 패딩을 추가해야 한다고 한다. 이러한 GoogLeNet 에 적용된 해결 방법은 Sparse Connectivity 이다. (이를 위해 사용되는 것이 Inception Module)
CNN 은 Convolution, Pooling, FC 들이 서로 Dense (밀집) 하게 연결되어 있다. Dense 하게 연결된 신경망 대신 Correlation (관련성) 이 높은 노드끼리만 연결하는 방법을 Sparse Connectivity 라고 한다. Sparse Connectivity 를 통해 연산량도 적어지게 되고, 과적합도 해결할 수 있다고 한다.
위 이미지는 GoogLeNet 의 Inception Module 의 구조이다. 여러 개의 연산이 병렬적으로 수행되는 것이다. 기존에 사용하던 VGG 와 같은 모델은, 계층이 넓고 깊어 인식률은 좋지만 과적합이나 기울기 소실 문제를 비롯한 학습 시간 지연, 연산 속도 등의 문제가 발생했었다. 특히 합성곱 신경망에서 이러한 문제들이 많이 발생하였는데, GoogLeNet 은 이러한 구조를 통해 문제를 해결하려고 하였던 것이다.
그 다음으로는 ResNet 이라는 모델에 대해 알아보았다. ResNet 은 마이크로소프트에서 개발한 알고리즘으로, "Deep Residual Learning for Image Recognition" 이라는 논문에서 발표되었다. ResNet 의 핵심은 깊어진 신경망을 효과적으로 학습하기 위한 방법으로 Residual 개념을 추가한 것이다.
일반적으로 신경망 깊이가 깊어질수록 모델의 성능이 향상될 것 같지만, 실제로 그렇지는 않다. 위 논문에 따르면, 신경망은 깊이가 깊어질수록 성능이 좋아지다가 일정한 단계에 다다르면 오히려 성능이 낮아진다고 한다. ResNet 은 이러한 문제를 해결하기 위해 Residual Block 을 도입하였다. 이는 기울기가 잘 전파될 수 있도록 일종의 Shortcut 을 만들어주는 것이다.
또한, ResNet 은 계층의 깊이가 깊어질수록 파라미터가 무제한으로 많아지는 문제를 해결하기 위해 Bottleneck Block 이라는 것을 사용한다. 기본 블록을 사용하는 ResNet34, 병목 블록을 사용하는 ResNet50 의 파라미터 수는 각각 약 39M, 7M 개 이다. ResNet50 의 깊이가 깊어졌음에도 파라미터 수는 감소하였다. 이는 위 그림에서 Conv Layer 1, 3 에 해당하는 부분에서 1 x 1 합성층을 사용함으로써 가능해진다. 1 x 1 합성곱층의 채널 수를 조절하면서 차원을 줄였다가 늘리는 것이 가능하기 때문에 파라미터 수를 줄일 수 있는 것이다. 이렇게 차원을 조절함으로써 파라미터 수가 감소하는 효과를 얻을 수 있는 것이다.
Identity Mapping 혹은 Skip Connection 이라고 불리는 것이 사용된다. 이는 입력 x 가 어떠한 함수를 통과하더라도 다시 x 라는 형태로 출력되도록 하는 것이다. 이를 테면, forward 함수 내에서 x 라는 값을 i 라는 다른 이름의 변수에 저장한 다음 블록에 마지막에서 i 값을 다시 사용하는 것이다. 또, Downsample 이라는 핵심개념이 있다. 이는 Feature map 의 크기를 줄이기 위한 것으로 Pooling 과 같은 역할을 한다고 볼 수 있는 것이다.
정리하자면, ResNet 은 기본적으로 VGG19 의 구조를 뼈대로 하며, 거기에 합성곱층들을 추가해서 깊게 만든 후 Shortcut 들을 추가해준 것이 전부라고 한다. 이제 이 ResNet 을 Pytorch 코드로 실습해본다.
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import torch.utils.data as data
import torchvision
import torchvision.transforms as transforms
import torchvision.datasets as datasets
import torchvision.models as models
import matplotlib.pyplot as plt
import numpy as np
import copy
from collections import namedtuple
import os
import random
import time
import cv2
from torch.utils.data import DataLoader, Dataset
from PIL import Image
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
위에서 collections 모듈의 namedtuple 이라는 것이 사용된다. 이는 Python 의 자료형 중 하나로, Tuple 의 성질을 갖고있는 자료형이지만 인덱스 뿐만 아니라 키 값으로 데이터에 접근할 수 있게끔 한다. 아래와 같이 사용할 수 있다.
BasketballPlayer = namedtuple("BasketballPlayer", ["name", "back_number", "team"])
StephCurry = BasketballPlayer("Steph Curry", 30, "Goldenstate Warriors")
print(StephCurry[0])
print(StephCurry.team)
이제 전과 동일하게 이미지 전처리 및 데이터를 불러오는 코드를 작성해야 한다.
class ImageTransform():
def __init__(self, resize, mean, std):
self.data_transform = {
'train': transforms.Compose([
transforms.RandomResizedCrop(resize, scale=(0.5, 1.0)),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize(mean, std)
]),
'val': transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(resize),
transforms.ToTensor(),
transforms.Normalize(mean, std)
])
}
def __call__(self, img, phase):
return self.data_transform[phase](img)
cat_directory = r'/content/drive/MyDrive/Colab Notebooks/딥러닝파이토치/data/dogs-vs-cats/Cat/'
dog_directory = r'/content/drive/MyDrive/Colab Notebooks/딥러닝파이토치/data/dogs-vs-cats/Dog/'
cat_images_filepaths = sorted([os.path.join(cat_directory, f) for f in os.listdir(cat_directory)])
dog_images_filepaths = sorted([os.path.join(dog_directory, f) for f in os.listdir(dog_directory)])
images_filepaths = [*cat_images_filepaths, *dog_images_filepaths]
correct_images_filepaths = [i for i in images_filepaths if cv2.imread(i) is not None]
random.seed(42)
random.shuffle(correct_images_filepaths)
train_images_filepaths = correct_images_filepaths[:400]
val_images_filepaths = correct_images_filepaths[400:-10]
test_images_filepaths = correct_images_filepaths[-10:]
print(len(train_images_filepaths), len(val_images_filepaths), len(test_images_filepaths))
이전과 코드는 거의 동일하다.
class DogvsCatDataset(Dataset):
def __init__(self, file_list, transform=None, phase='train'):
self.file_list = file_list
self.transform = transform
self.phase = phase
def __len__(self):
return len(self.file_list)
def __getitem__(self, idx):
img_path = self.file_list[idx]
img = Image.open(img_path)
img_transformed = self.transform(img, self.phase)
label = img_path.split('/')[-1].split('.')[0]
if label == 'dog':
label = 1
elif label == 'cat':
label = 0
return img_transformed, label
train_dataset = DogvsCatDataset(train_images_filepaths, transform=ImageTransform(size, mean, std), phase='train')
val_dataset = DogvsCatDataset(val_images_filepaths, transform=ImageTransform(size, mean, std), phase='val')
train_iterator = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
valid_iterator = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)
dataloader_dict = {'train': train_iterator, 'val': valid_iterator}
batch_iterator = iter(train_iterator)
inputs, label = next(batch_iterator)
print(inputs.size())
print(label)
Reference:
딥러닝 파이토치 교과서
'Machine Learning > Pytorch' 카테고리의 다른 글
딥러닝 파이토치 교과서 (6-5: 객체인식을 위한 신경망) (0) | 2023.03.29 |
---|---|
딥러닝 파이토치 교과서 (6-4: ResNet(2)) (0) | 2023.03.28 |
딥러닝 파이토치 교과서 (6-3: VGGNet (2)) (0) | 2023.03.21 |
딥러닝 파이토치 교과서 (6-3: VGGNet (1)) (0) | 2023.03.20 |
딥러닝 파이토치 교과서 (6-2: AlexNet) (0) | 2023.03.16 |