You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
VGGNet은 Network의 깊이가 어떤 영향을 주는지 연구를 하기 위해서 설계된 Network이다. 따라서 Convolution kernel 사이즈를 한 사이즈로 정하고 Convolution의 개수를 늘리는 방식으로 테스트를 진행했다.
VGG-net은 전부 3 * 3 convolution, stride 1, padding 1으로 이루어져 있다.
이론적으로는 5 * 5 kernel이나 3 * 3 kernel 2개나 성능적으로 동일하지만, VGGNet 팀이 테스트 한 결과 3 * 3 Kernel의 중첩이 더 좋았다. 그 이유는 non-linear 함수인 ReLU가 더 많이 들어가서 decision function이 더 잘 학습되기 때문이다.
실험 결과 network의 깊이가 깊어질 수록 error가 내려간다. 하지만 19 layer부터는 성능이 나빠지는 것을 알 수 있었다.
3. torchvision.models.vgg
torchvision의 모듈을 통해 vgg11 ~ vgg19까지 만들 수 있다.
3(RGB) * 224 * 224의 input을 기준으로 만들도록 되어 있다.
오늘 실습을 통해 input size가 다른 경우에 VGG를 적용해 보자!
4. VGG Network 만들어 보기!
# 1. 라이브러리 로드importtorch.nnasnnimporttorch.utils.model_zooasmodel_zoo# 2. 모델 URL 지정하기 (PyTorch에서 ImageNet 챌린지 데이터셋을 기반으로 pretrain 시켜놓은 모델들)# 변수 선언__all__= [
'VGG', 'vgg11', 'vgg11_bn', 'vgg13', 'vgg13_bn', 'vgg16', 'vgg16_bn', 'vgg19_bn', 'vgg19',
]
# Pretrain 모델 URLmodel_urls= {
'vgg11': 'https://download.pytorch.org/models/vgg11-bbd30ac9.pth',
'vgg13': 'https://download.pytorch.org/models/vgg13-c768596a.pth',
'vgg16': 'https://download.pytorch.org/models/vgg16-397923af.pth',
'vgg19': 'https://download.pytorch.org/models/vgg19-dcbb9e9d.pth',
'vgg11_bn': 'https://download.pytorch.org/models/vgg11_bn-6002323d.pth',
'vgg13_bn': 'https://download.pytorch.org/models/vgg13_bn-abd245e5.pth',
'vgg16_bn': 'https://download.pytorch.org/models/vgg16_bn-6c64b313.pth',
'vgg19_bn': 'https://download.pytorch.org/models/vgg19_bn-c79401a0.pth',
}
# 3. VGG 네트워크 만들어 놓기classVGG(nn.Module): # nn.Module의 클래스를 상속받아서 이용한다.def__init__(self, features, num_classes=1000, init_wieghts=True): # VGG 네트워크를 만들 때 초기 입력 파라미터로 Convolution(features), 분류 클래스 수, weight initialize 여부를 입력한다.super(VGG, self).__init__()
self.features=features# 파라미터로 받은 값을 self.features로 설정.self.avgpool=nn.AdaptiveAvgPool2d((7, 7))
# Classifier : VGG의 Classifier는 fully connected layer 3개로 이루어져 있다.self.classifier=nn.Sequential(
nn.Linear(512*7*7, 4096), # FC Layer 1. 512 * 7 * 7은 도큐먼트에 적혀있는 기본 사이즈이다. 이와 Image 사이즈가 다르거나, (custom layer 등을 이용해서) Convolution의 결과 output이 다른 경우 이 부분을 수정해 줘야 한다!nn.ReLU(True),
nn.Dropout(),
nn.Linear(4096, 4096), # FC Layer 2nn.ReLU(True),
nn.Dropout(),
nn.Linear(4096, num_classes), # FC Layer 3
)
# weight initializeifinit_weights:
self.initialize_weights()
# Forward 정의defforward(self, x):
x=self.features(x) # Convolutionx=self.avgpool(x) # AvgPoolx=x.view(x.size(0), -1) # 일렬로 펼치기x=self.classifier(x) # FC Layerreturnx# 모듈 내용 별로 initialize weights 정의def_initialize_weights(self):
forminself.modules(): # 모듈을 하나씩 꺼내 봤을 때,ifisinstance(m, nn.Conv2d): # 만약 m이 Convolution이면nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') # kaining_normal_로 filter의 weight를 초기화를 하라ifm.biasisnotNone: # VGG Net에서는 bias 값이 0이여야 하므로nn.init.constant_(m.bias, 0) # m.bias를 0으로 초기화elifisinstance(m, nn.BatchNorm2d): # 만약 m이 BatchNorm이면nn.init.constant_(m.weight, 1) # weight는 전부 1로, bias는 전부 0으로 초기화nn.init.constant_(m.bias, 0)
elifisinstance(m, nn.Linear): # 만약 m이 nn.Linear이면nn.init.normal_(m.weight, 0, 0.01) # 다음과 같이 초기화nn.init.constant_(m.bias, 0)
# Convolution Layer(feature)가 쌓이는 구조 정의defmake_layers(cfg, batch_norm=False):
layers= [] # 레이어를 쌓을 빈 리스트 선언in_channels=3# input channel 3forvincfg: # cfg에서 하나씩 빼서 v로 이용ifv=='M': # cfg에서 빼온 값이 M이면layers+= [nn.MaxPool2d(kernel_size=2, stride=2)] # MaxPool 추가else: # cfg에서 빼온 값이 M이 아니면conv2d=nn.Conv2d(in_Channels, v, kernel_size=3, padding=1) # v를 이용해 conv2d 정의ifbatch_norm: # batch_norm = True인 경우layers+= [conv2d, nn.BatchNorm2d(v), nn.ReLU(inplace=True)] # 이 레이어를 layers 리스트에 추가!else: # batch_norm = False인 경우layers+= [conv2d, nn.ReLU(inplace=True)] # 이 레이어를 layers 리스트에 추가!in_channels=v# 중요! in_channels를 v로 바꿔준다. conv 레이어를 통과하고 나면 채널 수가 바뀌기 때문에, 다음 layer의 input에 들어갈 값을 바꿔줘야 한다.returnnn.Sequential(*layers)
# 4. Convolution Layer 쌓을 내용 선언 (make_layers에 넣을 내용)cfg= { # cfg의 값에 따라 Convolution Layer가 쌓이게 된다.'A': [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'], # 이렇게 레이어를 쌓으면 Convolution 8 + Fully Connecyed 3가 나온다. => 11 => vgg11'B': [64, 64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'], # 10 + 3 = vgg 13'D': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M'], #1 3 + 3 = vgg 16'E': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512, 'M'], # 16 +3 = vgg 19'custom' : [64,64,64,'M',128,128,128,'M',256,256,256,'M'] # 그 외 사용자가 임의로 설정 가능!
}
# 5. 만들어 놓은 VGG 네트워크 이용하여 모델 만들기# Convolution Layer 만들기conv=make_layers(cfg['custom'], batch_norm=True) # Convolution layer를 cfg 중 'custom'에 따라 쌓게 한다. batch norm = True로 설정.CNN=VGG(make_layers(cfg['custom']), num_classes=10, init_weights=True) # VGG의 파라미터로 Convolution layer(feature), class 개수, weights initialize를 설정하여 VGG 모델(CNN)을 만든다.CNN
5. VGG Network를 실제 데이터에 이용하기 전에
주피터노트북을 Text로 만들어서 이름을 vgg.py로 설정하고, Pytorch 홈페이지의 Torchvision.model에 있는 VGG를 복사해서 붙여 넣는다.
학습할 위치의 같은 폴더 안에 이 파일이 있어야 VGG를 불러와서 사용할 수 있다!
위의 방법이 귀찮다면 그냥 import torchvision.models.vgg as vgg로도 사용 가능하다.
6. CIFAR10 데이터에 VGG 적용하기
# 1. 라이브러리 로드importtorchimporttorch.nnasnnimporttorch.optimasoptimimporttorchvisionimporttorchvision.transformsastransforms# 2. 비스돔 이용importvisdomvis=visdom.Visdom()
vis.close(env="main") # main에 켜져있는게 있다면 다 끈다.# 3. loss tracker 정의defloss_tracker(loss_plot, loss_value, num):
vis.line(X=num,
Y=loss_value,
win=loss_plot,
update='append'
)
# 4. device 및 seed 설정device='cuda'iftorch.cuda.is_available() else'cpu'torch.manual_seed(777)
ifdevice=='cuda':
torch.cuda.maual_seed_all(777)
# 5. 데이터 정의# transformtransform=transforms.Compose(
[transforms.ToTensor(), # tensor 화transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) # Normalize# 데이터 셋 불러오기trainset=torchvision.datasets.CIFAR10(root='./cifar10', train=True, download=True, transform=transform)
trainloader=torch.utils.data.DataLoader(trainset, batch_size=512, shuffle=True, num_workers=0)
testset=torchvision.datasets.CIFAR10(root='./cifar10', train=False, download=True, transform=transform)
testloader=torch.utils.data.DataLoader(testset, batch_size=4, shuffle=False, num_workers=0)
classes= ('plane', 'car, 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
# 6. visdom에서 이미지 확인importmatplotlib.pyplotaspltimportnumpyasnp%matplotlibinlinedefimshow(img):
img=img/2+0.5npimg=img.numpy()
plt.imshow(np.transpose(npimg, (1, 2, 0)))
plt.show()
dataiter=iter(trainloader)
images, labels=dataiter.next()
vis.images(images/2+0.5) # visdom에 이미지를 띄운다.print(' '.join('%5s'%classes[labels[j]] forjinrange(4)))
# 7. VGG 네트워크 : 라이브러리 로드importvggimporttorchvision.models.vggasvgg# vgg.py를 만들기 귀찮은 경우 이렇게 라이브러리를 불러서 이용해도 된다.# 8. VGG 네트워크 : Convolution Layer 쌓을 내용 선언 (make_layers에 넣을 내용)cfg= [32, 32, 'M', 64, 64, 128, 128, 128, 'M', 256, 256, 256, 512, 512, 512, 'M'] # convolution 13 + FC Layer 3 => vgg16!# 이 네트워크의 아웃풋을 계산해보면, 32 * 32를 MaxPool 3번 하므로 32 => 16 => 8 => 4가 된다. 즉 아웃풋은 4 * 4 이다. # 9. VGG 네트워크 : VGG 네트워크 만들어 놓기classVGG(nn.Module):
def__init__(self, features, num_classes=1000, init_weights=True): # 파라미터로 Convolution(features), class 수(초기값은 1000이지만, 나중에 10으로 넣을 것임), weight initialize 설정.super(VGG, self).__init__()
self.features=featuresself.classifier=nn.Sequential(
nn.Linear(512*4*4, 4096), # 수정! Conv의 아웃풋을 계산 해 보면 4 * 4 이므로 거기에 맞게 수정했다.nn.ReLU(True),
nn.Dropout(),
nn.Linear(4096, 4096),
nn.ReLU(True),
nn.Dropout(),
nn.Linear(4096, num_classes),
)
ifinit_weights:
self._initialize_weights()
defforward(self, x):
x=self.features(x)
x=x.view(x.size(0), -1)
x=self.classifier(x)
returnxdef_initialize_weights(self):
forminself.modules():
ifisinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
ifm.biasisnotNone:
nn.init.constant_(m.bias, 0)
elifisinstance(m, nn.BatchNorm2d):
nn.init.constant_(m.weight, 1)
nn.init.constant_(m.bias, 0)
elifisinstance(m, nn.Linear):
nn.init.normal_(m.weight, 0, 0.01)
nn.init.constant_(m.bias, 0)
# 10. VGG 네트워크 : 만들어 놓은 VGG 네트워크 이용하여 모델 만들기vgg16=VGG(vgg.make_layers(cfg), 10, True).to(device) # Convolution(feature), Class 수, weight initialize를 설정해서 모델 정의. # 만든 VGG 모델에 오류 없나 테스트하기a=torch.Tensor(1, 3, 32, 32).to(device)
out=vgg16(a)
print(out)
# 11. VGG 네트워크 : Loss, Optimizer, Lr 설정criterion=nn.CrossEntropyLoss().to(device)
optimizer=torch.optim.SGD(vgg16.parameters(), lr=0.005, momentum=0.9)
# NEW! lr을 고정해 놓지 않고 학습에 따라 lr을 조금씩 감소시키며 학습하는 방법이다. 정밀한 학습에 도움이 된다.lr_sche=optim.lr_scheduler.StepLR(optimizer, step_size=5, gamma=0.9) # 5번의 step마다 Optimizer의 lr에 0.9를 곱해 준다.# loss를 plot으로 만들기loss_plt=vis.line(Y=torch.Tensor(1).zero_(), opts=dict(title='loss_tracker', legend=['loss'], showlegend=True))
# 12. VGG 네트워크 : 학습하기print(len(trainloader))
epochs=50forepochinrange(epochs):
running_loss=0.0lr_sche.step() # epoch이 5번 돌 때마다 lr이 0.9배씩 줄어든다.fori, datainenumerate(trainloader, 0):
inputs, labels=datainputs=inputs.to(device)
labels=labels.to(device)
outputs=vgg16(inputs)
loss=criterion(outputs, labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
running_loss+=loss.item()
ifi%30==29: # 30회마다 loss tracker!loss_tracker(loss_plt, torch.Tensor([running_loss/30]), torch.Tensor([i+epoch*len(trainloader)
print('[%d, %5d] loss: %3.f'%(epoch+1, i+1, running_loss/30))
running_loss=0.0print('Finished Training')
# 13. VGG 네트워크 : 시각화를 통해 Test 데이터 잘 맞추나 확인하기dataiter=iter(testloader)
images, labels=dataiter.next()
# GT 이미지 4개 출력해보기imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ', ' '.join('%5s'%classes[labels[j]] forjinrange(4)))
# GT와 outputs의 결과가 같나 확인하기outputs=vgg16(images.to(device))
_, predicted=torch.max(outputs, 1)
print('Predicted: ', ' '.join('%5s'%classes[predicted[j]] forjinrange(4)))
# 14. VGG 네트워크 : Test 셋으로 모델 평가하기correct=0total=0withtorch.no_grad():
fordataintestloader:
imgaes, labels=dataimages=images.to(device)
labels=labels.to(device)
outputs=vgg16(images)
_, predicted=torch.max(outputs.data, 1)
total+=labels.size(0)
correct+= (predicted==labels).sum().item()
print('Accuracy of the network on the 10000 test images: %d %%'%(100*correct/total)) # 정확도 76%