Residual Networks

  • ¾Õ¼± ³×Æ®¿öÅ©¿¡¼­ ¼³¸íÇÑ ºÎºÐÀº »ý·«Çß½À´Ï´Ù.
  • 2015 ILSVRC 1st place
  • ResNet-50

alt text

In [1]:
# ·±Å¸ÀÓ À¯Çü GPU ¸ðµå·Î º¯°æ
!pip install torch torchvision

!pip install pillow==4.1.1
%reload_ext autoreload
%autoreload
Requirement already satisfied: torch in /usr/local/lib/python3.6/dist-packages (1.1.0)
Requirement already satisfied: torchvision in /usr/local/lib/python3.6/dist-packages (0.3.0)
Requirement already satisfied: numpy in /usr/local/lib/python3.6/dist-packages (from torch) (1.16.4)
Requirement already satisfied: six in /usr/local/lib/python3.6/dist-packages (from torchvision) (1.12.0)
Requirement already satisfied: pillow>=4.1.1 in /usr/local/lib/python3.6/dist-packages (from torchvision) (4.1.1)
Requirement already satisfied: olefile in /usr/local/lib/python3.6/dist-packages (from pillow>=4.1.1->torchvision) (0.46)
Requirement already satisfied: pillow==4.1.1 in /usr/local/lib/python3.6/dist-packages (4.1.1)
Requirement already satisfied: olefile in /usr/local/lib/python3.6/dist-packages (from pillow==4.1.1) (0.46)
In [2]:
!rm -r images
import os 

try:
  os.mkdir("images")
  os.mkdir("images/dogs")
  os.mkdir("images/cats")
except:
  pass

!wget https://i.kinja-img.com/gawker-media/image/upload/s--WFkXeene--/c_scale,f_auto,fl_progressive,q_80,w_800/ol9ceoqxidudap8owlwn.jpg -P images/dogs
!wget https://www.rspcansw.org.au/wp-content/uploads/2017/08/50_a-feature_dogs-and-puppies_mobile.jpg -P images/dogs
  
!wget https://www.catster.com/wp-content/uploads/2018/05/A-gray-cat-crying-looking-upset.jpg -P images/cats
!wget https://www.scarymommy.com/wp-content/uploads/2018/01/c1.jpg?w=700 -P images/cats
--2019-08-26 13:56:33--  https://i.kinja-img.com/gawker-media/image/upload/s--WFkXeene--/c_scale,f_auto,fl_progressive,q_80,w_800/ol9ceoqxidudap8owlwn.jpg
Resolving i.kinja-img.com (i.kinja-img.com)... 151.101.130.166, 151.101.194.166, 151.101.2.166, ...
Connecting to i.kinja-img.com (i.kinja-img.com)|151.101.130.166|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 32099 (31K) [image/jpeg]
Saving to: ¡®images/dogs/ol9ceoqxidudap8owlwn.jpg¡¯

ol9ceoqxidudap8owlw 100%[===================>]  31.35K  --.-KB/s    in 0.005s  

2019-08-26 13:56:33 (5.59 MB/s) - ¡®images/dogs/ol9ceoqxidudap8owlwn.jpg¡¯ saved [32099/32099]

--2019-08-26 13:56:36--  https://www.rspcansw.org.au/wp-content/uploads/2017/08/50_a-feature_dogs-and-puppies_mobile.jpg
Resolving www.rspcansw.org.au (www.rspcansw.org.au)... 101.0.77.122
Connecting to www.rspcansw.org.au (www.rspcansw.org.au)|101.0.77.122|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 130940 (128K) [image/jpeg]
Saving to: ¡®images/dogs/50_a-feature_dogs-and-puppies_mobile.jpg¡¯

50_a-feature_dogs-a 100%[===================>] 127.87K   156KB/s    in 0.8s    

2019-08-26 13:56:38 (156 KB/s) - ¡®images/dogs/50_a-feature_dogs-and-puppies_mobile.jpg¡¯ saved [130940/130940]

--2019-08-26 13:56:39--  https://www.catster.com/wp-content/uploads/2018/05/A-gray-cat-crying-looking-upset.jpg
Resolving www.catster.com (www.catster.com)... 192.124.249.102
Connecting to www.catster.com (www.catster.com)|192.124.249.102|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 165145 (161K) [image/jpeg]
Saving to: ¡®images/cats/A-gray-cat-crying-looking-upset.jpg¡¯

A-gray-cat-crying-l 100%[===================>] 161.27K  --.-KB/s    in 0.03s   

2019-08-26 13:56:39 (5.80 MB/s) - ¡®images/cats/A-gray-cat-crying-looking-upset.jpg¡¯ saved [165145/165145]

--2019-08-26 13:56:41--  https://www.scarymommy.com/wp-content/uploads/2018/01/c1.jpg?w=700
Resolving www.scarymommy.com (www.scarymommy.com)... 104.18.164.96, 104.18.166.96, 104.18.168.96, ...
Connecting to www.scarymommy.com (www.scarymommy.com)|104.18.164.96|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 2169547 (2.1M) [image/jpeg]
Saving to: ¡®images/cats/c1.jpg?w=700¡¯

c1.jpg?w=700        100%[===================>]   2.07M  --.-KB/s    in 0.03s   

2019-08-26 13:56:42 (64.9 MB/s) - ¡®images/cats/c1.jpg?w=700¡¯ saved [2169547/2169547]

1. Settings

1) Import required libraries

In [0]:
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.init as init
import torch.utils.data as data
import torchvision.datasets as dset
import torchvision.transforms as transforms
from torch.utils.data import DataLoader

2) Hyperparameter

In [0]:
batch_size= 1
learning_rate = 0.0002
num_epoch = 100

2. Data Loader

In [0]:
img_dir = "./images"
img_data = dset.ImageFolder(img_dir, transforms.Compose([
                                      transforms.Resize(256),                   
                                      transforms.RandomResizedCrop(224),        
                                      transforms.RandomHorizontalFlip(),        
                                      transforms.ToTensor(),                    
            ]))

train_loader = data.DataLoader(img_data, batch_size=batch_size,
                            shuffle=True, num_workers=2)

3. Model

1) Basic Block

  • ÄÁº¼·ç¼Ç ¿¬»ê°ú È°¼ºÈ­ÇÔ¼ö´Â Ç×»ó ºÙ¾î Àֱ⠶§¹®¿¡ À̸¦ ÇÔ¼ö·Î ¸¸µé¾ú½À´Ï´Ù.
In [0]:
def conv_block_1(in_dim,out_dim,act_fn,stride=1):
    model = nn.Sequential(
        nn.Conv2d(in_dim,out_dim, kernel_size=1, stride=stride),
        act_fn,
    )
    return model


def conv_block_3(in_dim,out_dim,act_fn):
    model = nn.Sequential(
        nn.Conv2d(in_dim,out_dim, kernel_size=3, stride=1, padding=1),
        act_fn,
    )
    return model

2) Bottle Neck Module

  • Bottle Neck ¸ðµâÀº [1x1 ÄÁº¼·ç¼Ç -> 3x3 ÄÁº¼·ç¼Ç -> 1x1 ÄÁº¼·ç¼Ç]À¸·Î ÀÌ·ç¾îÁý´Ï´Ù.
  • ¸Ç À§¿¡ ³×Æ®¿öÅ© ±¸Á¶¿¡¼­µµ º¼ ¼ö ÀÖµíÀÌ ½Ç¼±Àº Å©±â°¡ º¯ÇÏÁö ¾Ê´Â °æ¿ì, Á¡¼±Àº Å©±â°¡ ÁÙ¾îµå´Â °æ¿ìÀÔ´Ï´Ù.
  • À̸¦ Çѹø¿¡ ±¸ÇöÇϱâ À§ÇØ downÀ̶ó´Â º¯¼ö·Î Å©±â °¨¼Ò ¿©ºÎ¸¦ Ç¥½ÃÇÏ°í Á¶°Ç¹®À¸·Î °æ¿ìÀÇ ¼ö¸¦ ³ª´² ±¸ÇöÇß½À´Ï´Ù.
  • ¶ÇÇÑ ResNetÀÇ Skip-connectionÀº ´Ü¼ø ´õÇϱâ·Î Á¤ÀǵǾî Àֱ⠶§¹®¿¡ Ư¼ºÁöµµÀÇ Å©±â¸¦ ÀÏÄ¡½ÃÄÑ¾ß ÇÕ´Ï´Ù.
  • À̸¦ À§ÇØ Â÷¿øÀ» ¸ÂÃçÁÖ´Â ¿ªÇÒ·Î dim_equalizer¶ó´Â °ÍÀ» Á¤ÀÇÇß½À´Ï´Ù.
In [0]:
class BottleNeck(nn.Module):
    def __init__(self,in_dim,mid_dim,out_dim,act_fn,down=False):
        super(BottleNeck,self).__init__()
        self.down=down
        
        # Ư¼ºÁöµµÀÇ Å©±â°¡ °¨¼ÒÇÏ´Â °æ¿ì
        if self.down:
            self.layer = nn.Sequential(
              conv_block_1(in_dim,mid_dim,act_fn,2),
              conv_block_3(mid_dim,mid_dim,act_fn),
              conv_block_1(mid_dim,out_dim,act_fn),
            )
            self.downsample = nn.Conv2d(in_dim,out_dim,1,2)
            
        # Ư¼ºÁöµµÀÇ Å©±â°¡ ±×´ë·ÎÀÎ °æ¿ì
        else:
            self.layer = nn.Sequential(
                conv_block_1(in_dim,mid_dim,act_fn),
                conv_block_3(mid_dim,mid_dim,act_fn),
                conv_block_1(mid_dim,out_dim,act_fn),
            )
            
        # ´õÇϱ⸦ À§ÇØ Â÷¿øÀ» ¸ÂÃçÁÖ´Â ºÎºÐ
        self.dim_equalizer = nn.Conv2d(in_dim,out_dim,kernel_size=1)
                  
    def forward(self,x):
        if self.down:
            downsample = self.downsample(x)
            out = self.layer(x)
            out = out + downsample
        else:
            out = self.layer(x)
            if x.size() is not out.size():
                x = self.dim_equalizer(x)
            out = out + x
        return out

2) ResNet Model

In [0]:
class ResNet(nn.Module):

    def __init__(self, base_dim, num_classes=2):
        super(ResNet, self).__init__()
        self.act_fn = nn.ReLU()
        self.layer_1 = nn.Sequential(
            nn.Conv2d(3,base_dim,7,2,3),
            nn.ReLU(),
            nn.MaxPool2d(3,2,1),
        )
        self.layer_2 = nn.Sequential(
            BottleNeck(base_dim,base_dim,base_dim*4,self.act_fn),
            BottleNeck(base_dim*4,base_dim,base_dim*4,self.act_fn),
            BottleNeck(base_dim*4,base_dim,base_dim*4,self.act_fn,down=True),
        )   
        self.layer_3 = nn.Sequential(
            BottleNeck(base_dim*4,base_dim*2,base_dim*8,self.act_fn),
            BottleNeck(base_dim*8,base_dim*2,base_dim*8,self.act_fn),
            BottleNeck(base_dim*8,base_dim*2,base_dim*8,self.act_fn),
            BottleNeck(base_dim*8,base_dim*2,base_dim*8,self.act_fn,down=True),
        )
        self.layer_4 = nn.Sequential(
            BottleNeck(base_dim*8,base_dim*4,base_dim*16,self.act_fn),
            BottleNeck(base_dim*16,base_dim*4,base_dim*16,self.act_fn),
            BottleNeck(base_dim*16,base_dim*4,base_dim*16,self.act_fn),            
            BottleNeck(base_dim*16,base_dim*4,base_dim*16,self.act_fn),
            BottleNeck(base_dim*16,base_dim*4,base_dim*16,self.act_fn),
            BottleNeck(base_dim*16,base_dim*4,base_dim*16,self.act_fn,down=True),
        )
        self.layer_5 = nn.Sequential(
            BottleNeck(base_dim*16,base_dim*8,base_dim*32,self.act_fn),
            BottleNeck(base_dim*32,base_dim*8,base_dim*32,self.act_fn),
            BottleNeck(base_dim*32,base_dim*8,base_dim*32,self.act_fn),
        )
        self.avgpool = nn.AvgPool2d(7,1) 
        self.fc_layer = nn.Linear(base_dim*32,num_classes)
        
    def forward(self, x):
        out = self.layer_1(x)
        out = self.layer_2(out)
        out = self.layer_3(out)
        out = self.layer_4(out)
        out = self.layer_5(out)
        out = self.avgpool(out)
        out = out.view(batch_size,-1)
        out = self.fc_layer(out)
        
        return out

4. Optimizer & Loss

In [11]:
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(device)
    
model = ResNet(base_dim=64).to(device)

'''
for i in model.children():
    print(i)
'''

loss_func = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(),lr=learning_rate)
cuda:0

5. Train

In [12]:
for i in range(num_epoch):
    for j,[image,label] in enumerate(train_loader):
        x = image.to(device)
        y_= label.to(device)
        
        optimizer.zero_grad()
        output = model.forward(x)
        loss = loss_func(output,y_)
        loss.backward()
        optimizer.step()

    if i % 10 ==0:
        print(loss)
tensor(1.9276, device='cuda:0', grad_fn=<NllLossBackward>)
tensor(1.1285, device='cuda:0', grad_fn=<NllLossBackward>)
tensor(0.4470, device='cuda:0', grad_fn=<NllLossBackward>)
tensor(1.1557, device='cuda:0', grad_fn=<NllLossBackward>)
tensor(0.4266, device='cuda:0', grad_fn=<NllLossBackward>)
tensor(0.4082, device='cuda:0', grad_fn=<NllLossBackward>)
tensor(1.1532, device='cuda:0', grad_fn=<NllLossBackward>)
tensor(1.0690, device='cuda:0', grad_fn=<NllLossBackward>)
tensor(1.1242, device='cuda:0', grad_fn=<NllLossBackward>)
tensor(1.0983, device='cuda:0', grad_fn=<NllLossBackward>)
In [0]: