Skip to content

Latest commit

 

History

History
249 lines (197 loc) · 8.65 KB

10-6-2. Advanced CNN(RESNET)2.md

File metadata and controls

249 lines (197 loc) · 8.65 KB

1. 들어가기 전에

  • 주피터 노트북을 메모장으로 켜서 제목을 resnet.py으로 하고 resnet 도큐먼트를 붙여 넣는다.
  • resnet을 이용할 폴더의 같은 위치에 resnet.py가 있어야 실행 된다!
  • 이전에 배웠던 vgg에서 모델을 resnet으로 바꾼 채로 실습을 진행한다.



2. CIFAR10에 ResNet 이용하기

# 1. 라이브러리 로드
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transform as transform

# 2. Visdom 이용
import visdom

vis = visdom.Visdom()
vis.close(env="main")

# 3. Value(Accuracy, Loss) tracker
def value_tracker(value_plot, value, num):
  vis.line(X=num,
           Y=value,
           win=value_plot,
           update='append'
          )

# 4. GPU 설정
device = 'cuda' if torch.cuda.is_available() else 'cpu'

torch.manual_seed(777)
if device == 'cuda':
  torch.cuda.manual_seed_all(777)

# 팁! Normalize 값 계산하는 방법 1 : 데이터 로드
transform = transforms.Compose([
  transforms.ToTensor()
])

trainset = torchvision.datasets.CIFAR10(root='./cifar10', train=True, download=True, transform=transform)

# 팁! Normalize 값 계산하는 방법 2 : 계산
'''
이전의 VGG 예시에서는 transform을 이용 할 때 Normalize 값에 0.5와 같은 값을 무작정 넣었는데,
이번에는 Normalize를 직접 계산해서 transform 할 때 계산 값을 넣어 보자!
'''
train_data_mean = trainset.train_data.mean( axis = (0, 1, 2) ) # 데이터 셋의 각 축에 대한 평균 계산
train_data_std = trainset.train_data.std( axis = (0, 1, 2) ) # 데이터 셋의 가 축에 대한 std 계산
print(train_data_mean, train_data_std)

train_data_mean = train_data_mean / 255 # 앞에서 구한 값을 255로 나눠준다.
train_data_std = train_data_std / 255 # 앞에서 구한 값을 255로 나눠준다.
print(train_data_mean, train_data_std)

# 5. 데이터 로드
transform_train = transforms.Compose([
  transforms.RandomCrop(32, padding=4), # padding을 4로 두른 후, 32만큼 랜덤하게 crop한다. 데이터 셋의 형태를 변형시키는 데 좋은 방법!
  transforms.ToTensor(), # 텐서화
  transforms.Normalize(train_data_mean, train_data_std) # 앞에서 직접 구한 Normalize 값으로 Normalize 해 준다.
])

transform_test = transforms.Compose([
  transforms.ToTensor(),
  transforms.Normalize(train_data_mean, train_Data_std)
])

trainset = torchvision.datasets.CIFAR10(root='./cifar10', train=True, download=True, transform=transform_train)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=256, shuffle=True, num_workers=0)
testset = torchvision.datasets.CIFAR10(root='./cifar10', train=False, download=True, transform=transform_test)
testloader = torch.utils.data.DataLoader(testset, barch_size=256, shuffle=False, num_worksers=0)

classes = ('plane', 'car', 'bird', cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

# 6. ResNet 네트워크 구축
# 라이브러리 로드
import resnet
import torchvision.models.resnet # resnet.py를 저장해서 이용하는 것이 아닌, 이렇게 라이브러리를 불러와서 이용해도 된다.

# resnet 기본 설정
conv1x1 = resnet.conv1x1
Bottleneck = resnet.Bottleneck
BasicBlock = resnet.BasicBlock

# ResNet 네트워크 구축 해 놓기
# 변형!은 기존의 ResNet 예제에서 변형 한 것을 뜻한다.
class ResNet(nn.Module):
  def __init__(self, block, layers, num_classes=1000, zero_init_residual=False):
    super(ResNet, self).__init__()
    self.inplanes = 16 # 변형! 이번에는 inplanes을 16으로 설정했다.
    self.conv1 = nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1, bias=False) # input 3채널, output 16채널 
    
    self.bn1 = nn.BatchNorm2d(16)
    self.relu = nn.ReLU(inplace=True)
    
    self.layer1 = self._make_layer(block, 16, layers[0], stride=1) # 변형! layer의 사이즈를 64-> 16과 같이 간소화. stride도 바꿈
    self.layer2 = self._make_layer(block, 32, layers[1], stride=1)
    self.layer3 = self._make_layer(block, 64, layers[2], stride=2)
    self.layer4 = self._make_layer(block, 128, layers[3], stride=2)
    
    self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
    self.fc = nn.Linear(128 * block.expansion, num_classes) # input : 128 * block.expansion = 512, output : 10
    
    # 변형 x
    for m in self.modules():
      if isinstance(m, nn.Conv2d):
        nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
      elif isinstance(m, nn.BatchNorm2d):
        nn.init.constant_(m.weight, 1)
        nn.init.constant_(m.bias, 0)
        
    # 변형 x  
    if zero_init_residual:
      for m in self.modules():
        if isinstance(m, Bottleneck):
          nn.init.constant_(m.bn3.weight, 0)
        elif isinstance(m, BasicBlock):
          nn.init.constant_(m.bn2.weight, 0)
          
  # 변형 x        
  def _make_layer(self, block, planes, blocks, stride=1):
    downsample = None
    if stride != 1 or self.inplanes != planes * block.expansion:
      downsample = nn.Sequential(
        conv1x1(self.inplanes, planes * block.expansion, stride),
        nn.BatchNorm2d(planes * block.expansion),
      )
    
    layers = []
    layers.append(block(self.inplanes, planes, stride, downsample))
    self.inplanes = planes * block.expansion
    for _ in range(1, blocks):
      layers.append(block(self.inplanes, planes))
      
    return nn.Sequential(*layers)
    
  def forward(self, x):
    x = self.conv1(x) # [1, 16, 32, 32]
    x = self.bn1(x)
    x = self.relu(x)
    
    x = self.layer1(x) # [1, 128, 32, 32]
    x = self.layer2(x) # [1, 256, 32, 32]
    x = self.layer3(x) # [1, 512, 16, 16]
    x = self.layer4(x) # [1, 1024, 8, 8]
    
    x = self.avgpool(x)
    x = x.view(x.size(0), -1)
    x = self.fc(x)
    
    return x

# 7. ResNet 모델 이용하기!
resnet50 = ResNet(resnet.Bottleneck, [3, 4, 6, 3], 10, True).to(device) # Bottleneck과 [3, 4, 6, 3]을 설정하면 resnet 50을 만들 수 있다.
resnet50

# 8. ResNet 모델에 오류 없나 테스트 해보기
a = torch.Tensor(1, 3, 32, 32).to(device)
out = resnet50(a)
print(out)

# 9. Loss, Optimizer, LR
criterion = nn.CrossEntropyLoss().to(device)
optimizer = torch.optim.SGD(resnet50.parameters(), lr = 0.1, momentum = 0.9, weight_decay = 5e-4)
lr_sche = optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.5)

# 10. Loss와 Acc로 Plot으로 만들기
loss_plt = vis.line(Y=torch.Tensor(1).zero_(), opts=dict(title='loss_tracker', legend=['loss'], showlegend=True))
acc_plt = vis.line(Y=torch.Tensor(1).zero_(), opts=dict(title='Accuracy', legend=['Acc'], showlegend=True))

# 11. Accuracy 체크하는 함수
def acc_check(net, test_set, epoch, save=1):
  correct = 0
  total = 0
  with torch.no_grad():
    for data in test_set:
      images, labels = data
      images = images.to(device)
      labels = labels.to(device)
      outputs = net(images)
      
      _, predicted = torch.max(outputs.data, 1)
      
      total += labels.size(0)
      correct += (predicted == labels).sum().item()
      
  acc = (100 * correct / total)
  print('Accuracy of the network on the 10000 test images: %d %%' %acc)
  if save: # save를 True로 설정 할 경우, 지정 된 폴더에 epoch 별 정확도를 저장할 수 있다.
    torch.save(net.state_dict(), "./model/model_epoch_{}_acc_{}.pth".format(epoch, int(acc)))
  return acc

# 12. ResNet으로 학습 시작!
print(len(trainloader))
epochs = 150

for epoch in range(epochs):
  running_loss = 0.0
  lr_sche.step()
  for i, data in enumerate(trainloader, 0):
    inputs, labels = data
    inputs = inputs.to(device)
    labels = labels.to(device)

    outputs = resnet50(inputs)
    loss = criterion(outputs, labels)

    optimizer.zero_grad()    
    loss.backward()
    optimizer.step()
    
    running_loss += loss.item()
    if i % 30 == 29:
      value_tracker(loss_plt, torch.Tensor([running_loss/30]), torch.Tensor([i + epoch * len(trainloader)])) # value tracker
      print('[%d, %5d] loss: %.3f' %
            (epoch + 1, i + 1, running_loss / 30))
      running_loss = 0.0
      
  # epoch이 한 번 돌면, 정확도를 체크하고 정확도를 value_tracker에 저장
  acc = acc_check(resnet50, testloader, epoch, save=1)
  value_tracker(acc_plt, torch.Tensor([acc]), torch.Tensor([epoch]))
  
print('Finished Training')

correct = 0
total = 0

# 13. 모델 평가
with torch.no_grad():
  for data in testloader:
    images, labels = data
    images = images.to(device)
    labels = labels.to(device)
    outputs = resnet50(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))
# Accuracy : 87%