또르르's 개발 Story

[13-1] CNN using PyTorch 본문

부스트캠프 AI 테크 U stage/실습

[13-1] CNN using PyTorch

또르르21 2021. 2. 4. 02:18

1️⃣ 설정

 

1) device 설정 : GPU 사용 or CPU 사용

device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')

 

 

2) Dataset 가지고 오기

 

PyTorch에서는 기본적으로 datasets를 지원합니다.

from torchvision import datasets,transforms

train 시킬 데이터를 tensor형태로 불러옵니다.

mnist_train = datasets.MNIST(root='./data/',train=True,transform=transforms.ToTensor(),download=True)

test 할 데이터를 tensor형태로 불러옵니다.

mnist_test = datasets.MNIST(root='./data/',train=False,transform=transforms.ToTensor(),download=True)

 

 

3) DataLoader : 데이터를 가지고와 generator 형태로 저장

 

DataLoader을 사용해서 mnist_train data를 BATCH_SIZE만큼 섞어서 불러옵니다.

train_iter = torch.utils.data.DataLoader(mnist_train,batch_size=BATCH_SIZE,shuffle=True,num_workers=1)  #num_workers는 멀티프로세스 몇개 사용할 것인지

DataLoader을 사용해서 mnist_test data를 BATCH_SIZE만큼 섞어서 불러옵니다.

test_iter = torch.utils.data.DataLoader(mnist_test,batch_size=BATCH_SIZE,shuffle=True,num_workers=1)

 

2️⃣ CNN 모델

 

1) CNN Model

class ConvolutionalNeuralNetworkClass(nn.Module):

    def __init__(self,name='cnn',xdim=[1,28,28],
    
                 ksize=3,cdims=[32,64],hdims=[1024,128],ydim=10,
                 
                 USE_BATCHNORM=False):
                 
        super(ConvolutionalNeuralNetworkClass,self).__init__()
        
        self.name = name
        
        self.xdim = xdim
        
        self.ksize = ksize
        
        self.cdims = cdims
        
        self.hdims = hdims
        
        self.ydim = ydim
        
        self.USE_BATCHNORM = USE_BATCHNORM
        

        # Convolutional layers - 이 부분이 변경
        
        self.layers = []
        
        prev_cdim = self.xdim[0]
        
        for cdim in self.cdims: # for each hidden layer
        
            self.layers.append(
            
                nn.Conv2d(
                
                    in_channels = prev_cdim,    # previous channel dimensions (현재는 1)
                    
                    out_channels= cdim,         # 현재 내가 가고자하는 channel dimensions (32채널로 감)
                    
                    kernel_size = self.ksize,   # kernel size
                    
                    stride=(1,1),
                    
                    padding=self.ksize//2
                    
                )) # convlution 
                
            if self.USE_BATCHNORM:              # batch norm을 사용할지 말지 결정
            
                self.layers.append(nn.BatchNorm2d(cdim)) # batch-norm
                
            self.layers.append(nn.ReLU(True))  # activation
            
            self.layers.append(nn.MaxPool2d(kernel_size=(2,2), stride=(2,2))) # max-pooling
            
             # kernel_size=(2,2), stride=(2,2) => 절반씩 줄어감
             
            self.layers.append(nn.Dropout2d(p=0.5))  # dropout
            
            prev_cdim = cdim
            

        # Dense layers
        
        self.layers.append(nn.Flatten())    # 한줄로 핌
        
        # 한줄로 폈을 때 몇개의 dimensions가 될지를 prev_hdim이 가지고 있음
        
        # 아래 수식이 어떻게 나오는지 알아보기
        
        prev_hdim = prev_cdim*(self.xdim[1]//(2**len(self.cdims)))*(self.xdim[2]//(2**len(self.cdims)))
        
        for hdim in self.hdims:     # prev_hdims -> 1024 -> ReLU -> 128 -> ReLu 
        
            self.layers.append(nn.Linear(prev_hdim, hdim, bias=True))
            
            self.layers.append(nn.ReLU(True))  # activation 통과
            
            prev_hdim = hdim
            
            
        # Final layer (without activation)
        
        # 마지막 나온 output을 layer에 집어넣어줌
        
        self.layers.append(nn.Linear(prev_hdim,self.ydim,bias=True))
        

        # Concatenate all layers 
        
        # layer는 list이므로 list에 있는 layer들을 하나씩 쌓게됨
        
        self.net = nn.Sequential()    # net은 sequencial로 정의
        
        for l_idx,layer in enumerate(self.layers):
        
            layer_name = "%s_%02d"%(type(layer).__name__.lower(),l_idx)
            
            self.net.add_module(layer_name,layer)   # net의 module에 layer을 쌓음, module의 장점은 layer_name을 정할 수 있음
            
        self.init_param() # initialize parameters
        
        
    def init_param(self):
    
      # parameter는 엄청나게 많아짐
      
        for m in self.modules():
        
            if isinstance(m,nn.Conv2d): # init conv
            
                nn.init.kaiming_normal_(m.weight)
                
                nn.init.zeros_(m.bias)
                
            elif isinstance(m,nn.BatchNorm2d): # init BN
            
                nn.init.constant_(m.weight,1)
                
                nn.init.constant_(m.bias,0)
                
            elif isinstance(m,nn.Linear): # lnit dense
            
                nn.init.kaiming_normal_(m.weight)
                
                nn.init.zeros_(m.bias)
                
            
    def forward(self,x):
    
        return self.net(x)

 

PyTorch에서는 convolution 계산도 알아서 해줍니다.

입력 channel (in_channels)와 출력 channel (out_channels)를 넣고 kernel_size, stride, padding option을 넣으면 됩니다.

 nn.Conv2d(
                
    in_channels = prev_cdim,    # previous channel dimensions (현재는 1)

    out_channels= cdim,         # 현재 내가 가고자하는 channel dimensions (32채널로 감)

    kernel_size = self.ksize,   # kernel size

    stride=(1,1),

    padding=self.ksize//2

)) # convlution 

Convolution 후, ReLU -> MaxPooling -> Dropout을 수행합니다.

self.layers.append(nn.ReLU(True))  # activation
            
self.layers.append(nn.MaxPool2d(kernel_size=(2,2), stride=(2,2))) # max-pooling

# kernel_size=(2,2), stride=(2,2) => 절반씩 줄어감

self.layers.append(nn.Dropout2d(p=0.5))  # dropout

Dense에서는 Flatten을 해서 마지막 convolution parameter를 flat 합니다.

(한 줄로 폈을 때 몇 개의 dimensions이 될지를 prev_hdim이 가지고 있음)

self.layers.append(nn.Flatten())    # 한줄로 핌

prev_hdim = prev_cdim*(self.xdim[1]//(2**len(self.cdims)))*(self.xdim[2]//(2**len(self.cdims)))
for hdim in self.hdims:     # prev_hdims -> 1024 -> ReLU -> 128 -> ReLu 

    self.layers.append(nn.Linear(prev_hdim, hdim, bias=True))
    
    self.layers.append(nn.ReLU(True))  # activation 통과
    
    prev_hdim = hdim

 

 

2) loss & optimizer

 

Class를 선언하고 device (cuda)에 올려줍니다.

이때, x 입력 차원을 [1,28,28], kernel size는 3, convolution dimensions 차원을 14*14*32와 7*7*64, hidden diemensions를 256 -> 128, y 출력 차원을 10으로 선언합니다.

C = ConvolutionalNeuralNetworkClass(

    name='cnn',xdim=[1,28,28],ksize=3,cdims=[32,64],    # 28*28을 14*14*32로 바꾸고 7*7*64로 변경
    
    hdims=[256, 128],ydim=10).to(device)                # hdims는 dense로 256 -> 128 -> 10으로 변경

Loss는 CrossEntroyLoss를 사용합니다. (PyTorch에서 제공)

loss = nn.CrossEntropyLoss()

Optimizer는 Adam을 사용합니다. learn rate를 설정해주어야 합니다.

optm = optim.Adam(M.parameters(),lr=1e-3)

 

 

3) Model Parameter 확인

 

np.set_printoptions(precision=3)    # numpy float 출력옵션 변경

n_param = 0

for p_idx,(param_name,param) in enumerate(C.named_parameters()):

    param_numpy = param.detach().cpu().numpy()    # detach() : 기존 Tensor에서 gradient 전파가 안되는 텐서 생성
    
    n_param += len(param_numpy.reshape(-1))		# 한줄로 만들어줌
    
    print ("[%d] name:[%s] shape:[%s]."%(p_idx,param_name,param_numpy.shape))
    
    print ("    val:%s"%(param_numpy.reshape(-1)[:5]))

print ("Total number of parameters:[%s]."%(format(n_param,',d')))		# 총 parameter 수



[0] name:[net.conv2d_00.weight] shape:[(32, 1, 3, 3)].
    val:[[[[ 0.128 -0.141  0.979]
   [-0.078  0.943  0.342]
   [ 0.715  0.312 -0.558]]]


 [[[ 0.935 -0.753  0.017]
   [-0.075  0.43  -0.476]
   [ 0.412 -0.211  0.118]]]


  ...


 [[[ 0.064 -0.504 -0.854]
   [ 0.499 -0.435  0.555]
   [-0.069  0.214  0.296]]]


 [[[-0.066  0.256 -0.065]
   [ 1.172 -0.035 -0.331]
   [ 0.381  0.172 -0.16 ]]]]
[1] name:[net.conv2d_00.bias] shape:[(32,)].
    val:[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0.]
[2] name:[net.conv2d_04.weight] shape:[(64, 32, 3, 3)].
    val:[[[[ 0.119 -0.011  0.038]
   [ 0.176  0.03   0.06 ]
   [ 0.095  0.043  0.026]]

  [[ 0.141 -0.081 -0.006]
   [ 0.016  0.04   0.039]
   [-0.008 -0.132  0.053]]

  [[ 0.152  0.138 -0.012]
   [-0.08  -0.023 -0.002]
   [-0.023  0.04  -0.058]]

  ...

  [[ 0.167 -0.204 -0.099]
   [ 0.063 -0.12   0.169]
   [-0.061  0.016 -0.121]]

  [[ 0.309  0.117 -0.097]
   [ 0.012 -0.147  0.037]
   [ 0.005 -0.01   0.117]]

  [[-0.064 -0.055  0.175]
   [-0.115  0.053  0.087]
   [ 0.028 -0.051 -0.176]]]


 [[[-0.029  0.145 -0.141]
   [-0.047  0.027  0.105]
   [-0.008  0.033 -0.025]]

  [[ 0.039 -0.014 -0.011]
   [ 0.034 -0.082 -0.005]
   [-0.001  0.062  0.162]]

  [[ 0.074  0.016 -0.06 ]
   [-0.028  0.114  0.091]
   [-0.053  0.125 -0.03 ]]

  ...

  [[ 0.002 -0.052  0.   ]
   [ 0.066  0.065  0.01 ]
   [-0.057  0.082  0.067]]

  [[ 0.032 -0.035  0.216]
   [-0.136  0.022 -0.057]
   [ 0.021  0.24  -0.005]]

  [[ 0.088  0.046  0.073]
   [ 0.047 -0.026  0.151]
   [ 0.066 -0.022 -0.082]]]


 [[[-0.087 -0.171 -0.064]
   [ 0.024 -0.05  -0.036]
   [-0.003  0.007 -0.007]]

  [[ 0.001 -0.112 -0.048]
   [-0.047 -0.14   0.019]
   [-0.051 -0.036 -0.117]]

  [[-0.115  0.001 -0.035]
   [-0.009  0.104  0.041]
   [-0.134 -0.003 -0.063]]

  ...

  [[ 0.158 -0.08  -0.03 ]
   [ 0.078 -0.101  0.17 ]
   [-0.018 -0.019 -0.137]]

  [[-0.039 -0.058 -0.1  ]
   [-0.067  0.149 -0.131]
   [ 0.009 -0.123 -0.032]]

  [[-0.031 -0.067  0.02 ]
   [-0.066 -0.031  0.034]
   [ 0.119 -0.197  0.169]]]


 ...


 [[[-0.103 -0.041 -0.15 ]
   [-0.033 -0.037  0.031]
   [-0.044  0.051 -0.092]]

  [[-0.015 -0.013 -0.058]
   [ 0.067 -0.037  0.073]
   [ 0.044  0.118  0.045]]

  [[ 0.032  0.005  0.042]
   [ 0.113  0.031 -0.023]
   [-0.067 -0.044 -0.107]]

  ...

  [[-0.055 -0.002  0.106]
   [ 0.004 -0.075 -0.075]
   [ 0.011 -0.037  0.047]]

  [[-0.03   0.016  0.106]
   [ 0.143 -0.093 -0.031]
   [ 0.03  -0.148  0.07 ]]

  [[ 0.06  -0.041 -0.119]
   [-0.01  -0.097  0.138]
   [-0.099 -0.02   0.041]]]


 [[[-0.015  0.047 -0.028]
   [-0.007  0.016  0.002]
   [ 0.077  0.009  0.142]]

  [[ 0.05  -0.131  0.163]
   [-0.015  0.112  0.079]
   [-0.025  0.098  0.035]]

  [[-0.037 -0.07  -0.067]
   [-0.023 -0.093  0.067]
   [ 0.088  0.003  0.085]]

  ...

  [[-0.043  0.058  0.002]
   [ 0.031 -0.136 -0.171]
   [ 0.021 -0.077  0.031]]

  [[ 0.051 -0.029 -0.074]
   [-0.067  0.148 -0.007]
   [-0.023  0.004 -0.1  ]]

  [[-0.091  0.024 -0.026]
   [-0.159  0.068 -0.131]
   [ 0.02   0.066  0.098]]]


 [[[ 0.019  0.028  0.06 ]
   [-0.007 -0.054  0.042]
   [-0.105 -0.069  0.008]]

  [[ 0.037 -0.084 -0.002]
   [-0.071  0.043  0.145]
   [ 0.007 -0.052  0.106]]

  [[ 0.01  -0.052 -0.07 ]
   [-0.025 -0.101  0.007]
   [ 0.082  0.001 -0.06 ]]

  ...

  [[ 0.016 -0.028  0.037]
   [ 0.154 -0.077  0.043]
   [-0.031 -0.119 -0.019]]

  [[-0.052 -0.002  0.016]
   [ 0.158  0.067  0.02 ]
   [-0.064  0.043 -0.065]]

  [[-0.024  0.138 -0.036]
   [-0.11  -0.003  0.06 ]
   [ 0.089  0.078 -0.001]]]]
[3] name:[net.conv2d_04.bias] shape:[(64,)].
    val:[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[4] name:[net.linear_09.weight] shape:[(256, 3136)].
    val:[[-0.028  0.011  0.062 ... -0.004  0.026  0.03 ]
 [ 0.091  0.002  0.006 ...  0.023  0.016 -0.012]
 [ 0.031 -0.025 -0.016 ...  0.016  0.074  0.025]
 ...
 [ 0.027 -0.032 -0.015 ...  0.015 -0.031 -0.014]
 [ 0.058 -0.025  0.031 ... -0.013  0.03   0.036]
 [ 0.026  0.007  0.021 ... -0.025  0.001  0.001]]
[5] name:[net.linear_09.bias] shape:[(256,)].
    val:[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[6] name:[net.linear_11.weight] shape:[(128, 256)].
    val:[[ 0.088 -0.026  0.03  ...  0.097  0.09  -0.056]
 [ 0.02   0.046 -0.055 ... -0.065 -0.08   0.013]
 [-0.227  0.113  0.043 ... -0.124  0.031 -0.048]
 ...
 [ 0.097 -0.056  0.031 ...  0.059  0.031 -0.008]
 [-0.103  0.036 -0.135 ... -0.236  0.043  0.028]
 [-0.132  0.016 -0.164 ... -0.009 -0.072  0.032]]
[7] name:[net.linear_11.bias] shape:[(128,)].
    val:[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0.]
[8] name:[net.linear_13.weight] shape:[(10, 128)].
    val:[[-0.058 -0.055  0.058 ...  0.21  -0.063  0.023]
 [-0.102 -0.027 -0.039 ...  0.144 -0.132  0.089]
 [-0.205 -0.012  0.125 ... -0.287 -0.101 -0.139]
 ...
 [ 0.123  0.142 -0.086 ...  0.054  0.057  0.123]
 [-0.206 -0.056  0.063 ... -0.047 -0.098  0.125]
 [ 0.098 -0.038  0.272 ... -0.007 -0.278 -0.126]]
[9] name:[net.linear_13.bias] shape:[(10,)].
    val:[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
Total number of parameters:[856,074].

 

detach() : 기존 Tensor에서 gradient 전파가 안 되는 텐서 생성

(단, storage를 공유하기에 detach로 생성한 Tensor가 변경되면 원본 Tensor도 똑같이 변함)

param_numpy = param.detach().cpu().numpy()    # detach() : 기존 Tensor에서 gradient 전파가 안되는 텐서 생성

 

3️⃣ Evaluation Function

 

1) Evaluation Function

 

def func_eval(model,data_iter,device):

    with torch.no_grad():     # gradients를 업데이트X
    
        model.eval() # evaluate (affects DropOut을 안하고, and BN은 학습되어있는 것을 사용)
        
        n_total,n_correct = 0,0
        
        for batch_in,batch_out in data_iter:    # batch_in=data, batch_out=label
        
            y_trgt = batch_out.to(device)
            
            model_pred = model(batch_in.view(-1, 28*28).to(device))
            
            _,y_pred = torch.max(model_pred.data,1)
            
            n_correct += (
            
                (y_pred==y_trgt)  # y와 y hat 값을 비교 후, sum
                
            ).sum().item()
            
            n_total += batch_in.size(0)
            
        val_accr = (n_correct/n_total)
        
        model.train() # back to train mode 
        
    return val_accr
    
print ("Done")

 

torch.no_grad()는 Gradients를 업데이트하지 않습니다.

with torch.no_grad():     # gradients를 업데이트X

Evaluation Function에서는 model.eval()과 model.train() 모드를 바꿔주는 것이 중요합니다.

model.eval() # evaluate (affects DropOut and BN)

...

model.train() # back to train mode 

DataLoader에서 가지고 온 data_iter이며, train_iter와 test_iter가 존재합니다.

MNIST에서 batch_in은 data (28 x 28 x 256의 tensor)와 batch_out은 label (1 x 256 tensor)로 되어있습니다.

for batch_in,batch_out in data_iter:    # batch_in=data, batch_out=label

batch_out은 정답 label이기 때문에 y_trgt 변수에 저장합니다.

y_trgt = batch_out.to(device)

Linear에서 X input이 28*28 = 784로 고정되어있기 때문에 일자로 펴서 넣어줍니다.

model_pred = model(batch_in.view(-1, 28*28).to(device))

n_correct는 y_pred와 y_trgt 값이 같을 때만 sum을 해서 더해줍니다.

item()을 사용하면 숫자 값을 얻을 수 있습니다.

n_correct += (

	y_pred==y_trgt  # y와 y hat 값을 비교 후, sum
        
            ).sum().item()

n_total에다가 batch_in의 첫 번째 dimension 256을 더해줍니다.

n_total += batch_in.size(0)

val_accr는 전체 데이터에서 몇 개가 맞았는지를 나타냅니다.

val_accr = (n_correct/n_total)

 

 

2) Initial Evaluation

 

초반 init을 해주는 과정이며, 초반에는 train_accr와 test_accr의 비율이 떨어지는 것을 알 수 있습니다.

M.init_param() # initialize parameters

train_accr = func_eval(C,train_iter,device)

test_accr = func_eval(C,test_iter,device)

>>> print ("train_accr:[%.3f] test_accr:[%.3f]."%(train_accr,test_accr))


train_accr:[0.101] test_accr:[0.099].

 

 

4️⃣ Train

 

print ("Start training.")

C.init_param() # initialize parameters

C.train() # train mode로 바꿔주는게 중요한게 batch_normalization과 dropout은 inference와 실제 실행에서는 다르기 때문

EPOCHS,print_every = 10,1

for epoch in range(EPOCHS):

    loss_val_sum = 0
    
    for batch_in,batch_out in train_iter:
    
        # Forward path
        
        y_pred = C.forward(batch_in.view(-1,1,28,28).to(device)) # 2D로 넣어줌
        
        loss_out = loss(y_pred,batch_out.to(device))
        
        # Update
        
        optm.zero_grad()      # reset gradient 
        
        loss_out.backward()      # backpropagate
        
        optm.step()      # optimizer update
        
        loss_val_sum += loss_out
        
    loss_val_avg = loss_val_sum/len(train_iter)
    
    # Print
    
    if ((epoch%print_every)==0) or (epoch==(EPOCHS-1)):
    
        train_accr = func_eval(C,train_iter,device)
        
        test_accr = func_eval(C,test_iter,device)
        
        print ("epoch:[%d] loss:[%.3f] train_accr:[%.3f] test_accr:[%.3f]."%
        
               (epoch,loss_val_avg,train_accr,test_accr))
               
print ("Done")        
Start training.
epoch:[0] loss:[0.378] train_accr:[0.973] test_accr:[0.973].
epoch:[1] loss:[0.125] train_accr:[0.981] test_accr:[0.983].
epoch:[2] loss:[0.089] train_accr:[0.987] test_accr:[0.985].
epoch:[3] loss:[0.073] train_accr:[0.989] test_accr:[0.986].
epoch:[4] loss:[0.062] train_accr:[0.993] test_accr:[0.989].
epoch:[5] loss:[0.053] train_accr:[0.993] test_accr:[0.990].
epoch:[6] loss:[0.045] train_accr:[0.995] test_accr:[0.990].
epoch:[7] loss:[0.040] train_accr:[0.996] test_accr:[0.991].
epoch:[8] loss:[0.039] train_accr:[0.996] test_accr:[0.991].
epoch:[9] loss:[0.037] train_accr:[0.997] test_accr:[0.992].
Done

 

loss는 y_predict값과 실제 y label값의 차이를 구합니다.

loss_out = loss(y_pred,batch_out.to(device))

optimizer를 사용해서 gradient를 update 합니다.

이때, backward()는 loss만 계산해주고 가중치 업데이트는 하지 않기 때문에, optm.step()을 통해 업데이트해주어야 합니다. optm.zero_grad()는 gradient를 reset 하기 위해 사용되며, 한 번의 mini batch의 backpropagation이 끝나면, 업데이트를 해주고 reset 해서 다시 사용(시작점을 동일하게 하기 위해서)합니다.

# Update

optm.zero_grad()		# reset gradient 

loss_out.backward()		# backpropagate

optm.step()			# optimizer update

 

5️⃣ Test

 

n_sample = 25

sample_indices = np.random.choice(len(mnist_test.targets), n_sample, replace=False)

test_x = mnist_test.data[sample_indices]

test_y = mnist_test.targets[sample_indices]

with torch.no_grad():

    C.eval() # to evaluation mode 
    
    y_pred = C.forward(test_x.view(-1,1,28,28).type(torch.float).to(device)/255.)
    
y_pred = y_pred.argmax(axis=1)

plt.figure(figsize=(10,10))

for idx in range(n_sample):

    plt.subplot(5, 5, idx+1)
    
    plt.imshow(test_x[idx], cmap='gray')
    
    plt.axis('off')
    
    plt.title("Pred:%d, Label:%d"%(y_pred[idx],test_y[idx]))
    
plt.show()  

print ("Done")

 

Sample의 idx를 고르는 작업입니다.

sample_indices = np.random.choice(len(mnist_test.targets), n_sample, replace=False)

Model에 forward 후 나온 predict값의 argmax(index를 구함)를 출력합니다.

with torch.no_grad():

    C.eval() # to evaluation mode 
    
    y_pred = C.forward(test_x.view(-1,1,28,28).type(torch.float).to(device)/255.)
    
y_pred = y_pred.argmax(axis=1)

Plt를 사용하여 image를 show 합니다.

plt.figure(figsize=(10,10))

for idx in range(n_sample):

    plt.subplot(5, 5, idx+1)
    
    plt.imshow(test_x[idx], cmap='gray')
    
    plt.axis('off')
    
    plt.title("Pred:%d, Label:%d"%(y_pred[idx],test_y[idx]))
    
plt.show()   

 

Comments