작년에 왔던 각설이 죽지도 않고 또 온것처럼 CV 얘기에선 정말 빠질 수 없는 근-본모델..
이제는 도메인 별 기초를 배우기 때문에 심화학습으로 배웠고, 과제로 풀었던 코드까지 살짝 리뷰하며 정리
LeNet
가장 최초의 CNN 모델이라고 볼 수있는 LeNet.
Conv - Pool - Conv - Pool - FC - FC 구조로 이루어져 있으며, 5x5 with strde 1 필터를 사용한다.
pooling으로는 2x2 max pooling with stride 2 사용.
AlexNet
위에서 LeNet을 언급한 것은 비교하기 위함.
단순히만 봐도 Conv - pool - Conv - pool - Conv - Conv - Conv - Pool - FC - FC - FC 로 모델이 훨씬 복잡해졌음.
또한 학습한 데이터 역시 ImageNet으로, 그 당시 최대의 이미지 데이터를 학습했으며 ReLU를 사용하고, dropout 기법 적용등 다양한 방법론이 추가되어 성능이 좋아졌음.
Local Response Normalization(LRN)
AlexNet의 첫 Conv - Pool 이후에는 LRN이라는 기법이 사용되었음. 구시대적 방법론. 측면 억제라는 뜻을 가졌으며, ReLU의 영향 때문에 고안되었음.
ReLU의 특성상 양수의 방향으로는 입력값을 그대로 사용하기 때문에 Conv, Pool 과정에서 매우 높은 픽셀값이 주변의 픽셀에 영향을 미칠 수 있기 때문에 다른 activation map의 같은 위치에 있는 픽셀끼리 정규화를 하는 과정이다.
커널간의 경쟁을 부추기는 느낌으로, 한 뉴런이 주변의 뉴런에 비해 강하게 활성화 된 경우 LRN을 적용하면 그 뉴런이 더 돋보이게 되며, 한 뉴런과 그 주변의 뉴런이 모두 강하게 활성화 돼있다면 LRN 이후 모두 값이 비교적 작아진다.
VGGNet
레이어를 더욱 깊게 쌓는 방법론으로 VGG11의 경우 11개의 층으로 이루어짐.
VGG16, VGG19는 16, 19층으로 더욱 깊어진 모델이며, 위에서 사용한 LRN 기법이 사용되지 않았다.(AlexNet 이후 어떤 모델에서도 사용되지 않는듯..)
3x3 filter만 사용하며, 2x2 max pooling을 사용한다. 따라서 모델의 구조가 더 단순하다.
Input으로는 224x224 RGP Image를 사용하므로 3채널의 (3, 224, 224) 텐서가 들어간다.
import torch
import torch.nn as nn
class VGG11BasicBlock(nn.Module):
def __init__(self):
super(VGG11BackBone, self).__init__()
self.relu = nn.ReLU(inplace=True)
self.conv1 = nn.Conv2d(3, 64, kernel_size=3, padding=1)
self.bn1 = nn.BatchNorm2d(64)
self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)
self.conv2 = nn.Conv2d(64, 128, kernel_size=3, padding=1)
self.bn2 = nn.BatchNorm2d(128)
self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)
self.conv3_1 = nn.Conv2d(128, 256, kernel_size=3, padding=1)
self.bn3_1 = nn.BatchNorm2d(256)
self.conv3_2 = nn.Conv2d(256, 256, kernel_size=3, padding=1)
self.bn3_2 = nn.BatchNorm2d(256)
self.pool3 = nn.MaxPool2d(kernel_size=2, stride=2)
self.conv4_1 = nn.Conv2d(256, 512, kernel_size=3, padding=1)
self.bn4_1 = nn.BatchNorm2d(512)
self.conv4_2 = nn.Conv2d(512, 512, kernel_size=3, padding=1)
self.bn4_2 = nn.BatchNorm2d(512)
self.pool4 = nn.MaxPool2d(kernel_size=2, stride=2)
self.conv5_1 = nn.Conv2d(512, 512, kernel_size=3, padding=1)
self.bn5_1 = nn.BatchNorm2d(512)
self.conv5_2 = nn.Conv2d(512, 512, kernel_size=3, padding=1)
self.bn5_2 = nn.BatchNorm2d(512)
def forward(self, x):
x = self.conv1(x)
x = self.bn1(x)
x = self.relu(x)
x = self.pool1(x)
x = self.conv2(x)
x = self.bn2(x)
x = self.relu(x)
x = self.pool2(x)
x = self.conv3_1(x)
x = self.bn3_1(x)
x = self.relu(x)
x = self.conv3_2(x)
x = self.bn3_2(x)
x = self.relu(x)
x = self.pool3(x)
x = self.conv4_1(x)
x = self.bn4_1(x)
x = self.relu(x)
x = self.conv4_2(x)
x = self.bn4_2(x)
x = self.relu(x)
x = self.pool4(x)
x = self.conv5_1(x)
x = self.bn5_1(x)
x = self.relu(x)
x = self.conv5_2(x)
x = self.bn5_2(x)
x = self.relu(x)
return x
단순하고도 복잡한 구조로 논문의 모형을 그대로 구현한다면 각 conv layer 사이마다 batch-normalization, ReLU를 적용해주며 conv와 pool을 반복하며 깊게 쌓아서 BasicBlock를 구성한다.
class VGG11Net(nn.Module):
def __init__(self, num_classes = 7):
super(VGG11Net, self).__init__()
self.block = VGG11BasicBlock()
self.pool5 = nn.MaxPool2d(kernel_size=2, stride=2)
self.gap = nn.AdaptiveAvgPool2d(1)
self.fc_out = nn.Linear(512, num_classes)
def forward(self, x):
x = self.block(x)
x = self.pool5(x)
x = self.gap(x)
x = torch.flatten(x, 1)
x = self.fc_out(x)
return x
이후 basicblock을 사용해 마지막 maxpool, avgpool을 적용 후 FC layer 1개를 사용해 linear를 수행하면 VGGNet11이 구현된다. 노가다성이 좀 짙은 모델..
from torchvision import models
from torchsummary import summary
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
vgg = VGG11Net().to(device)
summary(vgg, (3, 224, 224))
팀원에게 얻은 꿀팁으로는 torchsummary를 사용하면 모델의 생김새를 이쁘게 볼 수 있다. 위 코드의 결과는
이렇게 나온다.
'네이버 부스트캠프 학습 정리 > 4주차' 카테고리의 다른 글
4주차 회고 (0) | 2023.04.02 |
---|---|
[CV basic] Object Detection (0) | 2023.04.02 |
[CV basic] Semantic Segmentation (0) | 2023.04.01 |
[CV basic] GoogLeNet/ResNet (0) | 2023.03.31 |
[CV basic] Data Efficient Learning (0) | 2023.03.31 |