[Pytorch를 통한 인공신경망 모델 구현] 1. AlexNet

김성욱·2023년 5월 5일
0

구현을 시도하는 많은 사람들은 ImageNet 대회의 수상작들을 차례로 시도한다.
나도 처음에는 같은 방식을 택하려고 한다.

1. 논문 읽기 / 모르는 내용 정리

non-saturating neurons - 학습을 빠르게 하기 위해 사용한 saturating 하지 않는 뉴런
ex) ReLU ( x-> inf => y-> inf)
overlapping pooling - 말 그대로 stride를 줄여서 겹치는 부분이 생기게 하는 것이다.
local response normalization - 처음보는 내용이라 근거가 없어서 당황했지만 신경생물학에 있는 현상을 모방한 모델링이라고 한다. 요즘은 거의 사용하지 않는다.

뉴런은 정말 뇌를 모방할 수 있을까?

2. 핵심 내용 , insight 요약

  • 데이터셋이 커지고, 구별해야할 object가 많아지면서 더 큰 모델이 필요함
  • 그러나 크기만 하면 overfitting / 학습 속도 저하의 문제가 생김
  • 따라서 훈련을 빠르게 하기 위한 3가지 , overfitting을 막기 위한 2가지 테크닉 사용

ReLU

  • tanh / sigmoid와 같은 saturating function에 비해 효과가 좋기에 적용

병렬 GPU

  • 모델의 구조를 두 GPU에 나눠서 train하는 방식인데, 이 포스팅의 취지와는 맞지 않으므로 구현은 하지 않으려고 한다. 나중에 필요하면 다시 찾아보겠다.

local response normalization

  • 다른 커널에 존재하는 같은 위치의 값들로 normalization

data augmentation

  • CV에서는 매우 자주 사용되는 이미지 증강 기법을 적용
  • 또한 TTA( test time augmentation ) 적용

dropout

  • p의 확률로 해당 뉴런을 비활성화 시켜 학습에 참여하지 않게 만드는 방법
  • 학습 속도와 일반화에 많은 도움을 준다
  • test time에는 모든 뉴런을 활성화 시키므로 출력값에 *p를 해준다는데,
    합리적이긴 하지만 pytorch에 있는 dropout도 그렇게 하고 있었는지 찾아봐야 할 듯 하다.

3. 구조 파악 / 필요하다면 sketch

여기서 왜 사람들이 AlexNet부터 시작하는지, 프로젝트를 오래 진행하지 포기하는지 알게 되었다.

모든 내용이 다 적혀 있어서 받아쓰기를 하듯이 구현하면 어려움이 없을 것 같다.
그러나 요즘 논문은 이유는 알 수 없지만 생략이 많고 이 정도로 자세하게 모델 구조를 기술한 논문을 본 적이 없다. 이 당시에는 모델의 구조 자체도 혁신이었기 때문 일까

4. 구현

import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np


class AlexNet(nn.Module):
    ## 5개의 conv , 3개의 fcl
    def __init__(self):
        super(AlexNet,self).__init__()
        
        self.conv_layers = nn.Sequential(
            nn.Conv2d(3, 96, kernel_size=11, stride=4),
            nn.ReLU(),
            nn.LocalResponseNorm(5),
            nn.MaxPool2d(3,2),
            nn.Conv2d(96, 256, kernel_size=5, stride=1,padding=2),
            nn.ReLU(),
            nn.LocalResponseNorm(5),
            nn.MaxPool2d(3,2),
            nn.Conv2d(256,384, kernel_size=3, stride=1,padding=1),
            nn.ReLU(),
            nn.Conv2d(384,384, kernel_size=3, stride=1,padding=1),
            nn.ReLU(),
            nn.Conv2d(384,256, kernel_size=3, stride=1,padding=1),
            nn.ReLU(),
            nn.MaxPool2d(3,2)
        )
        
        self.fc = nn.Sequential(
            nn.Dropout(p=0.5),
            nn.Linear(9216,4096),
            nn.ReLU(),
            nn.Dropout(p=0.5),
            nn.Linear(4096,4096),
            nn.ReLU(),
            nn.Linear(4096,1000)
        )
        
        self.init_weights()
    
    def init_weights(self):
        for layer in self.conv_layers:
            if isinstance(layer, nn.Conv2d):
                nn.init.normal_(layer.weight, mean=0, std=0.01)   
                nn.init.constant_(layer.bias, 0) 
        for fc in self.fc:
            if isinstance(fc,nn.Linear):
                nn.init.constant_(fc.bias,1)
            
        nn.init.constant_(self.conv_layers[4].bias, 1)
        nn.init.constant_(self.conv_layers[10].bias, 1)
        nn.init.constant_(self.conv_layers[12].bias, 1)
    def forward(self,x):
        x = self.conv_layers(x)
        x = x.view(-1)
        x = self.fc(x)
        
        return x

    
x = torch.rand(3,227,227)
model = AlexNet()
model(x)

5. 어려웠던 점 / 찾아본 내용

코드를 작성하기 전에는 모든 정보를 다 줬다고 생각했는데 , stride와 padding 은 feature map 크기를 보고 유추해야 했다. 만약 feature map size도 주어지지 않은 모델이었다면 구현이 불가능 했을 수도 있겠다.

  • weights 초기화 관련 내용을 searching 했다.
  • nn.init 하위 메소드들을 통해 bias나 weights를 효과적으로 초기화 할 수 있다.

6. 간단한 실험


다음 모델은 GoogleNet(2014) / citation : 40000+ 이다.

profile
someone

0개의 댓글