Kim Seon Deok
AlexNet 본문
https://proceedings.neurips.cc/paper/2012/file/c399862d3b9d6b76c8436e924a68c45b-Paper.pdf
AlexNet[Krizhevskv et al., 2012]
목표
더 깊은 네트워크를 만들면서 성능을 높여간다.
5개의 Convolution층과 3개의 Fully-connected layer가 연결된 아키텍처로 구성
Fully- connected layer의 마지막 부분은 softmax 함수를 사용해 1000개의 output을 출력
3. Architecture
3.1 ReLU Nonlinearity
활성화 함수로 ReLU함수를 적용
이전까지 사용한 tanh함수는[-1,1] 범위에서 존재하는 saturating function이다.
-1에서 1까지 bounded되어 있기 때문에 saturating 되어, 기울기의 미분값이 0이 되기 때문에 gradient vanishing 현상이 생겨 gradient의 update를 더디게 만든다. 따라서 ReLU보다 훨씬 느리다.
반면 ReLU함수는 [0, ∞]의 범위에 존재한다. 따라서 non-saturating한 함수이다.
빠른 학습은 큰 데이터 집합에 대해 훈련된 큰 모델의 성능에 큰 영향을 미친다.
ReLU함수는 기존의 tahn함수보다 훨씬 빠른속도를 보였다.
3.2 Training on Multiple GPUs
1대의 GTX 580GPU에는 3GB의 메모리만 탑재되어 네트워크의 최대 사이즈가 제한된다.
따라서 2개의 GPU에 네트워크를 분산시킨다. 커널의 절반을 각 GPU에 배치하고, GPU는 특정 계층에서만 통신한다.
GPU는 호스트 컴퓨터 메모리를 거치지 않고 서로의 메모리에서 직접 읽고 쓸 수 있으므로 병렬 처리에 특히 적합하다.
2개의 GPU 사용은 1개의 GPU를 사용하는 것보다 시간이 단축된다.
3.3 Local Responce Normalization(LRN)
LRN은 batch normalization으로 알려진 정규화 계열의 방법이다.
lateral inhibition : 강한 자극이 주변에 약한 자극으로 전달되는 것을 막는 효과
LRN은 학습과정에서 lateral inhibition을 약화하고, 좀 더 학습을 generalization하기 위해 고안되었다.
tanh함수와 같은 Saturating함수는 양 끝이 평평해지는 현상을 막기 위해 normalization이 필요하다.
AlexNet은 non-saturating함수인 ReLU를 사용하므로, normalization이 특별히 필요하지 않다.
하지만 특정 필터의 한 픽셀의 가중치가 높으면 영향을 받은 특징맵은 자연스럽게 그 수치가 높을 수 있다.
따라서 필터의 정규화가 필요하다.
b(i,x,y) : LRN이 적용된 커널값
a(i,x,y) : Activation 결과값
N : 계층에 있는 커널의 총 개수
n : 고려해야 하는 커널 개수
k, n, α , β : 하이퍼 파라미터
이웃한 커널의 해당 (x,y)포지션 값들을 모두 squared sum하면 이 값이 클수록 LRN된 필터값은 원래 값보다 상대적으로 작아지고, 이 값이 작을수록 LRN된 필터값은 원래 값보다 상대적으로 커질 것이다. 따라서 강한 자극을 약하게 만드는 것이다.
normalization layer : pooling을 진행하고 출력된 ouput값의 범위를 조절
3.4 Overlapping Pooling의 사용
non-overlapping 풀링을 하면 중첩 없이 진행된다.
반면 overlapping 풀링을 하면 풀링 커널이 중첩되면서 지나가게 되어, overfitting과 에러율을 줄이는 데 효과가 있다.
4. overfitting 감소
4.1Data Augmentation(데이터 증대)
Augmentation은 CNN모델에서 데이터를 다양하게 증대시키는 방법이다.
하나의 이미지를 가지고 여러 장의 비슷한 이미지를 만들어 내는 것.
1.단순 수평반전(Horizontal reflection)
이미지를 수평으로 뒤집고, 이를 random하게 crop하여 이미지를 증대
2.PCA
RGB 채널의 강도를 변경
ImageNet 데이터 셋 전체에 RGB 픽셀 값 집합에 대한 PCA를 수행. 평균이 0, 표준편차가 0.1인 가우시안 분포에서 얻은 랜덤변수와 PCA 결과를 곱해 각 이미지에 추가
pi와 λi : RGB 픽셀의 3 × 3 공분산 행렬의 ih 고유 벡터 및 고유값
αi : 랜덤 변수
PCA Color Augementation : 데이터 셋의 색상(RGB)를 임의로 바꾸는 것
PCA를 통해 이미지의 특성은 유지하고 RGB 컬러 값을 이동 및 변경
4.2 Dropout
assemble기법과 같이 다양한 모델의 예측값을 조합하는 것은 test error을 줄이는 데 효과적인 방법이다. 하지만 몇 일이 걸리는 큰 신경네트워크에는 비용이 매우 많이 든다.
Dropout
단순 Dense 노드 값을 0으로 바꾸어 노드의 영향력을 제거하는 기법이다.
각각의 hidden neureon의 output을 0.5의 확률로 영점화 하는 설정으로 구성. 이런 식으로 빠진 뉴런들은 forward에 기여하지 않고, backpropagation에도 기여하지 않는다.
각 뉴런들의 출력결과에 0.5를 곱하여 기하급수적으로 많은 dropout 네트워크에 의해 생성되는 예측 분포의 평균을 취하는 데 합리적인 근사치가 된다.
5.Details of learning
- init learning rate: 0.01
- batch size: 128
- momentum: 0.9
- weight decay: 0.0005
- epoch: 90
- learning rate decay: validation error 가 개선되지 않을 때, 10 으로 나눠준다. (3번)
- conv init: gaussian (0, 0.01) - cf) He, Xavier 와 같이 더 좋은 init 방법이 있다.
weight decay : overfitting을 방지하기 위한 방법. 학습된 모델의 복잡도를 줄이기 위해 가중치가 너무 큰 값을 가지지 않도록 손실 함수에 가중치가 커질 때에 대한 패널티 항목을 넣어주는 것.
적은 양의 weight decay는 모델의 훈련 오류를 감소시킨다.
6. Results
커널들이 학습을 통해 색에 영향을 받는 커널과 영향을 받지 않는 커널로 구분되어 생성되었다.
7. Discussion
Conv layer 수가 줄어들면 성능이 저하된다.
네트워크의 깊이는 결과를 위해 매우 중요하다.
의의
GPU, ReLU함수를 사용하면서 깊은 네트워크(8 Layer)를 학습하여 성능 상승.
요약
-ReLU함수를 사용
-GPU 2대 사용
-overlapping pooling을 하여 overfitting 방지
-LRN으로 정규화
-Horiziontal reflection, PCA방법으로 data augmentation
-Drop out 방법을 사용해 overfitting 방지
<구현>
input : 227*227*3
- Conv1: kernel 11*11 96개 stride = 4 >> output : 55*55*96
- MaxPooling1 : kernel 3*3, stride = 2 >> output : 27*27*96
- Norm1 : 27*27*96
- Conv2 : kernel 5*5 256개, stride = 1, padding = 2 >> output : 27*27*256
- MaxPooling2 : kernel 3*3, stride = 2 >> output : 13*13*256
- Norm2 : 13*13*256
Conv3 : kernel 3*3 384개, stride =1, padding = 1 >> output : 13*13*384
Conv4 : kernel 3*3 384개, stride = 1, padding = 1 >> output : 13*13*384
- Conv5 : kernel 3*3 256개, stride = 1, padding = 1 >> output : 13*13*256
- MaxPool3: kernel 3*3, stride = 2 >> output : 6*6*256
FC1 : input : 6*6*256 >> output : 4096
FC2 : output : 4096
output layer : input : 4096 >> output : 1000
from google.colab import drive
drive.mount('alexnet')
# Alexnet 구현
import torch
import torch.nn as nn
from torchvision import datasets, transforms
devices = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
print(devices)
# transforms : 이미지 데이터를 로딩할 때 모듈의 입력값으로 사용할 수 있도록 변환한다.
transform = transforms.Compose([
transforms.Resize(256),
transforms.RandomCrop(227),
transforms.ToTensor(),
transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))
])
# Trans and Test set 로딩
# 데이터로 CIFAR-10 사용
trainset = datasets.CIFAR10('~/.data', download=True, train=True, transform =transform)
trainset = torch.utils.data.DataLoader(trainset, batch_size = 256, shuffle = True)
testset = datasets.CIFAR10('~/.data', download = True, train = False, transform = transform)
testset = torch.utils.data.DataLoader(testset, batch_size = 256, shuffle = True)
# 모델 구축
class AlexNet(nn.Module):
def __init__(self, input_size = 227, num_classes = 10):
super(AlexNet, self).__init__()
self.cnnLayer = nn.Sequential(
# 1st Conv : conv, relu, lrm, pool
# in_channels = 입력 채널 수, out_channels = 출력 채널 수, kernel_size = 필터 크기
nn.Conv2d(in_channels = 3, out_channels = 96, kernel_size = 11, padding = 0, stride = 4),
nn.ReLU(inplace = True),
nn.LocalResponseNorm(size = 5, alpha = 0.0001, beta = 0.75, k=2),
nn.MaxPool2d(kernel_size = 3, stride = 2), # 55 -> 27
# 2st Conv : conv, relu, lrm, pool
nn.Conv2d(in_channels = 96, out_channels = 256, kernel_size = 5, padding = 2, stride = 1),
nn.ReLU(inplace = True),
nn.LocalResponseNorm(size = 5, alpha = 0.0001, beta = 0.75, k = 2),
nn.MaxPool2d(kernel_size = 3, stride = 2),
# 3st Conv : conv, relu
nn.Conv2d(in_channels = 256, out_channels = 384, kernel_size = 3, padding = 1, stride = 1),
nn.ReLU(inplace = True),
# 4st Conv : cont, relu
nn.Conv2d(in_channels = 384, out_channels = 384, kernel_size = 3, padding = 1, stride = 1),
nn.ReLU(inplace = True),
# 5th Conv : conv, relu, lrm, pool
nn.Conv2d(in_channels = 384, out_channels = 256, kernel_size = 3, padding = 1 , stride = 1),
nn.ReLU(inplace = True),
nn.LocalResponseNorm(size = 5, alpha = 0.0001, beta = 0.75, k = 2),
nn.MaxPool2d(kernel_size = 3, stride = 2),
)
self.fcLayer = nn.Sequential(
# FC Layer
nn.Linear(6*6*256, 4096),
nn.ReLU(inplace = True),
nn.Dropout(p = 0.5),
nn.Linear(4096, 4096),
nn.ReLU(inplace = True),
nn.Dropout(p = 0.5),
nn.Linear(4096, num_classes)
)
# 표준화 및 bias 초기화
for layer in self.cnnLayer:
if isinstance(layer, nn.Conv2d) :
# conv레이어들을 가우시안 분포로 표준화 하고 bias는 0
nn.init.normal_(layer.weight, mean = 0, std = 0.01)
nn.init.constant_(layer.bias, 0)
# 그런데 2,4,5 conv 는 1로 초기화
nn.init.constant_(self.cnnLayer[4].bias, 1)
nn.init.constant_(self.cnnLayer[10].bias, 1)
nn.init.constant_(self.cnnLayer[12].bias, 1)
def forward(self, train):
# 멀티 GPU는 당장 불가능하기때문에 제외
output = self.cnnLayer(train)
output = output.view(-1, 256*6*6)
output = self.fcLayer(output)
return output
alexnet = AlexNet(277,10)
alexnet.to(devices)
# weight decay = 0.0005, momentum = 0.9, lr = 0.01
optimizer = torch.optim.SGD(alexnet.parameters(), lr = 0.01, momentum = 0.9, weight_decay = 0.0005)
criterion = nn.CrossEntropyLoss().to(devices)
# 학습
from tqdm.notebook import tqdm
epochs = 5
for epoch in range(epochs):
epoch_loss = 0
for data, classes in tqdm(trainset) :
inputs, labels = data.to(devices), classes.to(devices)
optimizer.zero_grad()
outputs = alexnet(inputs)
# 순전파, 역전파 최적화
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
epoch_loss += loss.item()
correct = list(0. for i in range(1000))
total = list(0. for i in range(1000))
with torch.no_grad():
for data, classes in testset:
inputs, labels = data.to(devices), classes.to(devices)
outputs = alexnet(inputs)
_, predicted = torch.max(outputs, 1)
c = (predicted == labels).squeeze()
for i in range(labels.size()[0]):
label = labels[i]
correct[label] += c[i].item()
total[label] += 1
print('{0} : loss {1:.3f}, val_acc {2:.3f}'.format(epoch+1, epoch_loss, (sum(correct) / sum(total))))
코드 참고 출처 : https://123okk2.tistory.com/171