❓CIFAR10 Dataset을 VGG로 학습시키면 성능이 떨어지는 이유
- VGG19는 depth가 가장 큰 모델, 큰 이미지 데이터에 맞게 설계된 모델
- CIFAR10은 크기가 작은 데이터기 때문에 VGG19 학습시키기 위한 데이터셋으론 적절하지 않음
- Pooling Layer를 거칠 시 이미지가 절반으로 줄어들기 때문에 VGG19로 학습시켰을 때 높은 Accuracy를 달성하기 어려움
- Pooling layer를 줄이거나, LeNet, VGG를 커스텀한다면 개선이 가능
1️⃣GoogLeNet
❓GoogLeNet?
- Sparsely connected sturucture Network Model
- = 넓게 포진되게 연결된 구조의 네트워크 모델
Layer가 깊어질수록 Backpropagation이 불안정(미분계수 소실)
- backpropagation이 진행되는 동시에 병렬적으로 발생 유도
Convolutional Layer를 통과할 때 모든 filter size가 같을 필요가 없다는 아이디어 - 사이즈가 다른 filter를 동시에 convolution하는 건 어떨까?
- Inception Model
❗️배경
depth 증가 = 많은 채널 수 사용(width 증가)
이에 따라 데이터 과학습(Overfitting)이 발생하기 쉬움
- 많은 연산 자원을 필요로 하게 됨
2️⃣ Naive Inception Module
❗️Branch Implementation
- 이전 레이어 Input은 (192, 100, 100)이라고 가정
- branch를 지날 때 이미지 사이즈는 줄어들지 않는다
- GoogLeNet에서는 이미지 사이즈가 유지되어야 함
branch1: kernel_size=1, padding=0, stride=1, output_size=(64, 100, 100)
branch2: kernel_size=3, padding=1, stride=1, output_size=(128, 100, 100)
branch3: kernel_size=5, padding=2, stride=1, output_size=(32, 100, 100)
branch3(max pooling): kernel_size=3, padding=1, stride=1, output_size=(192, 100, 100)
유의점
- branch를 만들고 난 후 torch.concat을 통해 모든 레이어의 output을 더해준다
- 배치사이즈가 없는 경우 axis = 0 / 있는 경우 axix=1을 기억할 것
- 없을 시 torch.size는 (CHW) 형태를 취함.
- axis=0이라면 channel 방향으로 붙이는 것
- axis=1이라면 output_size는 (416, 100, 100)
✍🏻Branch Implementation#1. batch없는 3차 tensor
import torch import torch.nn as nn #이전 레이어 input = (192, 100, 100) class ConvBlock(nn.Module): def __init__(self, in_channels, out_channels, kernel_size, padding=0, stride=1): super(ConvBlock, self).__init__() self.conv = nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=kernel_size, padding=padding, stride=stride) self.relu = nn.ReLU() def forward(self, x): x = self.conv(x) x = self.relu(x) return x
✍🏻Branch Implementation#2. batch가 있는 4차 tensor
class InceptionNaive(nn.Module): def __init__(self, in_channels, ch1x1, ch3x3, ch5x5): super(InceptionNaive, self).__init__() self.branch1 = ConvBlock(in_channels=in_channels, out_channels=ch1x1, kernel_size=1, padding=0, stride=1) self.branch2 = ConvBlock(in_channels=in_channels, out_channels=ch3x3, kernel_size=3, padding=1, stride=1) self.branch3 = ConvBlock(in_channels=in_channels, out_channels=ch5x5, kernel_size=5, padding=2, stride=1) self.branch4 = nn.MaxPool2d(kernel_size=3, padding=1, stride=1) def forward(self, x): out_branch1 = self.branch1(x) out_branch2 = self.branch2(x) out_branch3 = self.branch3(x) out_branch4 = self.branch4(x) out_inception = torch.concat([out_branch1, out_branch2, out_branch3, out_branch4], axis=1) return out_inception.shape BATCH_SIZE = 32 input_tensor = torch.randn(size=(BATCH_SIZE, 192, 100, 100)) model = InceptionNaive(in_channels=192, ch1x1=64, ch3x3=128, ch5x5=32) model.forward(input_tensor)
3️⃣Inception module with dimension reductions
왜 (1, 1) Convolution을 추가했을까?
- 채널 수를 조정
→ 효과 : (1, 1) Convolution을 추가해줌으로써 이미지 사이즈는 변하지 않고 채널 수만 조정해줄 수 있음 - (1, 1) Convolution은 결국 Fully Connected 와 같은 역할을 함
→ 효과 : Non-Linear 을 추가하는 효과로 복잡도를 증가시킬 수 있음
✍🏻ConvBlock, Inception class 활용 branch shape 출력
import torch import torch.nn as nn C = 192 H = 100 W = 100 input_tensor = torch.randn(size= (C,H,W)) branch1 = ConvBlock(in_channels=192, out_channels=64, kernel_size=1, padding=0, stride=1) out_branch1 = branch1.forward(input_tensor) branch2 = ConvBlock(in_channels=192, out_channels=128, kernel_size=3, padding=1, stride=1) out_branch2 = branch2.forward(input_tensor) branch3 = ConvBlock(in_channels=192, out_channels=32, kernel_size=5, padding=2, stride=1) out_branch3 = branch3.forward(input_tensor) branch4 = ConvBlock(in_channels=192, out_channels=192, kernel_size=3, padding=1, stride=1) out_branch4 = branch4.forward(input_tensor) out_inception = torch.concat([out_branch1, out_branch2, out_branch3, out_branch4],axis=0) print(out_branch1.shape) print(out_branch2.shape) print(out_branch3.shape) print(out_branch4.shape) print(out_inception.shape)
- out_branch를 통해 각 branch의 shape를 알아볼 수 있다
✍🏻ConvBlock 활용 branch shape 출력 class
C,H,W = 192, 100, 100 input_tensor = torch.randn(size= (C,H,W)) class InceptionNaive(nn.Module): def __init__(self, in_channels, ch1x1, ch3x3, ch5x5): super(InceptionNaive, self).__init__() self.branch1 = ConvBlock(in_channels=192, out_channels=ch1x1, kernel_size=1, padding=0, stride=1) self.branch2 = ConvBlock(in_channels=192, out_channels=ch3x3, kernel_size=3, padding=1, stride=1) self.branch3 = ConvBlock(in_channels=192, out_channels=ch5x5, kernel_size=5, padding=2, stride=1) self.branch4 = nn.MaxPool2d(kernel_size=3, padding=1, stride=1) def forward(self, x): out_branch1 = self.branch1(x) out_branch2 = self.branch2(x) out_branch3 = self.branch3(x) out_branch4 = self.branch4(x) x = torch.concat([out_branch1, out_branch1, out_branch3, out_branch4],axis=0) return x import torch BATCH_SIZE = 32 H, W = 100, 100 channels = 192 input_tensor = torch.randn(size=(BATCH_SIZE, channels, H, W)) print("Input: ", input_tensor) inception = InceptionNaive(in_channels=192, c1x1=64, c3x3=128, ch5x5=32) output_tensor = inception(input_tensor) print("Output: ", output_tensor)
- ConvBlock을 통해 branch object를 제작할 수 있음
- out_channel을 개별로 선언해주어 코드의 효율성을 높인다
✍🏻GoogLeNet class 제작(ConvBlock, Inception class 활용)
class GoogLeNet(nn.Module): def __init__(self): super(GoogLeNet, self).__init__() self.conv1 = nn.Conv2d(in_channels=3, out_channels=64, kernel_size=7, stride=2, padding=3) self.conv1_pool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) self.conv2 = nn.Conv2d(in_channels=64, out_channels=192, kernel_size=3, padding=1, stride=1) self.conv2_pool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) self.inception3a = InceptionModule(in_channels=192, ch1x1=64, ch3x3red=96, ch3x3=128, ch5x5red=16, ch5x5=32, pool_proj=32) self.inception3b = InceptionModule(in_channels=256, ch1x1=128, ch3x3red=128, ch3x3=192, ch5x5red=32, ch5x5=96, pool_proj=64) self.inception3_pool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) self.inception4a = InceptionModule(in_channels=480, ch1x1=192, ch3x3red=96, ch3x3=208, ch5x5red=16, ch5x5=48, pool_proj=64) self.inception4b = InceptionModule(in_channels=512, ch1x1=160, ch3x3red=112, ch3x3=224, ch5x5red=24, ch5x5=64, pool_proj=64) self.inception4c = InceptionModule(in_channels=512, ch1x1=128, ch3x3red=128, ch3x3=256, ch5x5red=24, ch5x5=64, pool_proj=64) self.inception4d = InceptionModule(in_channels=512, ch1x1=112, ch3x3red=144, ch3x3=288, ch5x5red=32, ch5x5=64, pool_proj=64) self.inception4e = InceptionModule(in_channels=528, ch1x1=256, ch3x3red=160, ch3x3=320, ch5x5red=32, ch5x5=128, pool_proj=128) self.inception4_pool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) self.inception5a = InceptionModule(in_channels=832, ch1x1=256, ch3x3red=160, ch3x3=320, ch5x5red=32, ch5x5=128, pool_proj=128) self.inception5b = InceptionModule(in_channels=832, ch1x1=384, ch3x3red=192, ch3x3=384, ch5x5red=48, ch5x5=128, pool_proj=128) self.inception5_pool = nn.AvgPool2d(kernel_size=7, stride=1, padding=0) self.fc1 = nn.Linear(in_features=1024 , out_features=1000) self.fc2 = nn.Linear(in_features=1000 , out_features=1000) def forward(self, x): x = self.conv1(x) print(f"conv1: {x.shape}") x = self.conv1_pool(x) print(f"conv1_pool: {x.shape}") x = self.conv2(x) print(f"conv2: {x.shape}") x = self.conv2_pool(x) print(f"conv2_pool: {x.shape}") x = self.inception3a(x) print(f"inception3a: {x.shape}") x = self.inception3b(x) print(f"inception3b: {x.shape}") x = self.inception3_pool(x) x = self.inception4a(x) print(f"inception4a: {x.shape}") x = self.inception4b(x) print(f"inception4b: {x.shape}") x = self.inception4c(x) print(f"inception4c: {x.shape}") x = self.inception4d(x) print(f"inception4d: {x.shape}") x = self.inception4e(x) print(f"inception4e: {x.shape}") x = self.inception4_pool(x) print(f"inception4_pool: {x.shape}") x = self.inception5a(x) print(f"inception5a: {x.shape}") x = self.inception5b(x) print(f"inception5b: {x.shape}") x = self.inception5_pool(x) print(f"inception5_pool: {x.shape}") x = x.view(x.shape[0],-1) x = self.fc1(x) print(f"fc1: {x.shape}") x = self.fc2(x) print(f"fc2: {x.shape}") return x BATCH_SIZE = 32 H, W = 224, 224 channels = 3 input_tensor = torch.randn(size=(BATCH_SIZE, channels, H, W)) model = GoogLeNet() model(input_tensor)
- channel로 받아들이는 Branch -> features로 받아들이는 Linear이기 때문에
x = x.view(x.shape[0],-1)
로 데이터 형식을 바꾸어준다
'AI데이터 엔지니어, 새싹' 카테고리의 다른 글
62th_121_Overfitting (0) | 2023.12.04 |
---|---|
61th_11_30(Thu)_ResNet (1) | 2023.11.30 |
59th_11_28(Tue)_VGG Implementation (0) | 2023.11.29 |
58th_11_27(Mon)_Convolutinal Neural Networks (1) | 2023.11.27 |
3주차(2) - [pandas] 데이터 전처리 & 데이터 핸들링 (0) | 2023.09.13 |