[밑바닥딥러닝] 17. 합성곱 신경망(CNN) (3)

2021. 10. 19. 20:19Deep Learning

본 게시글은 한빛미디어 『밑바닥부터 시작하는 딥러닝, 사이토 고키, 2020』의 내용을 참조하였음을 밝힙니다.

 

 

이번 장에서는 지난 장에서 구현했던 합성곱, 풀링 계층을 이용해서 합성곱 신경망을 구현하고, 

 

성능 평가를 진행하도록 하자. 

 

 

 

합성곱 신경망 구현


class SimpleConvNet:
    def __init__(self, input_dim=(1, 28, 28),
                 conv_param={'filter_num':30, 'filter_size':5, 'pad':0, 'stride':1},
                 hidden_size=100, output_size=10, weight_init_std=0.01):
        filter_num = conv_param['filter_num']
        filter_size = conv_param['filter_size']
        filter_pad = conv_param['pad']
        filter_stride = conv_param['stride']
        input_size = input_dim[1]
        conv_output_size = (input_size - filter_size + 2*filter_pad) / filter_stride + 1
        pool_output_size = int(filter_num * (conv_output_size/2) * (conv_output_size/2))

        self.params = {}
        self.params['W1'] = weight_init_std * \
                            np.random.randn(filter_num, input_dim[0], filter_size, filter_size)
        self.params['b1'] = np.zeros(filter_num)
        self.params['W2'] = weight_init_std * \
                            np.random.randn(pool_output_size, hidden_size)
        self.params['b2'] = np.zeros(hidden_size)
        self.params['W3'] = weight_init_std * \
                            np.random.randn(hidden_size, output_size)
        self.params['b3'] = np.zeros(output_size)

        # 계층 생성
        self.layers = OrderedDict()
        self.layers['Conv1'] = Convolution(self.params['W1'], self.params['b1'],
                                           conv_param['stride'], conv_param['pad'])
        self.layers['Relu1'] = Relu()
        self.layers['Pool1'] = Pooling(pool_h=2, pool_w=2, stride=2)
        self.layers['Affine1'] = Affine(self.params['W2'], self.params['b2'])
        self.layers['Relu2'] = Relu()
        self.layers['Affine2'] = Affine(self.params['W3'], self.params['b3'])

        self.last_layer = SoftmaxWithLoss()

구현할 합성곱 신경망에서는 입력 데이터의 형상과 필터 크기, 패딩, 스트라이드 정보, 그리고 각 신경층의 

 

노드 수들을 입력 받는다.

 

필터 정보와 입력데이터 형상, 그리고 은닉층 크기 정보들을 바탕으로 각 가중치들을 초기화하고 

 

합성곱(Convolution) - ReLU - 풀링 - 어파인(Affine) - ReLU - 어파인(Affine) - 소프트맥스 순으로 

 

신경망을 설계한다. 

 

class SimpleConvNet:
    def __init__(self, input_dim=(1, 28, 28),
                 conv_param={'filter_num':30, 'filter_size':5, 'pad':0, 'stride':1},
                 hidden_size=100, output_size=10, weight_init_std=0.01):
                 .
                 .

    def predict(self, x):
    .

    def loss(self, x, t):
    .

    def accuracy(self, x, t):
    .
    .

    def gradient(self, x, t):
        self.loss(x, t)

        dout = 1
        dout = self.last_layer.backward(dout)

        layers = list(self.layers.values())
        layers.reverse()
        for layer in layers:
            dout = layer.backward(dout)

        grads = {}
        grads['W1'] = self.layers['Conv1'].dW
        grads['b1'] = self.layers['Conv1'].db
        grads['W2'] = self.layers['Affine1'].dW
        grads['b2'] = self.layers['Affine1'].db
        grads['W3'] = self.layers['Affine2'].dW
        grads['b3'] = self.layers['Affine2'].db
        return grads

predict, loss, accuracy 함수들을 기존 신경망들과 구현법이 동일하고, 

 

gradient 함수에서 각 신경망층을 역순으로 순회하며 가중치마다의 그레디언트 딕셔너리 grads를 반환한다. 

 

 

 

 

성능 평가


    trainer_conv = Trainer_Conv()
    trainer_conv.train(3000)
    trainer_conv.display_loss_chart()
    plt.show()
    trainer_conv.display_accuracy_chart()
    plt.show()

모델을 입력받아 훈련만을 처리하는 Trainer_Conv 객체를 통해 신경망을 훈련시키고 손실, 정확도 그래프를 출력해본다.

훈련 데이터에서의 손실

훈련 데이터의 손실은 훈련이 진행됨에 따라서 점차 작아지며, 손실값 변화가 더 요동치는 것을 확인할 수 있다. 

훈련 vs 테스트 데이터에서의 정확도 비교

훈련 데이터와 테스트 데이터에서의 정확도 차이는 거의 없을 정도로 비슷한 양상을 보였고, 

 

반복횟수 1000회까지 정확도 20% 이하를 밑돌다가 1000회를 기점으로 정확도가 비약적으로 상승한 것을 

 

관찰할 수 있었다.