본문 바로가기

Deep Learning/Keras & Tensorflow

RNN과 CNN 동시 사용 모델(RCNN / CRNN)의 개념 및 구현

320x100
320x100

 

 

 

 RCNN 개념과 CNN 개념을 하나로 연결해서 설계된 모델이 있다. 연결 순서에 따라 아래와 같이 나뉜다.

  1. Convolutional Recurrent Neural Network (CRNN)
  2. Recurrent Convolutional Neural Network (RCNN)

 

1. CRNN

 CNN을 연산을 먼저 한 뒤에 각 채널을 나눠서 RNN에 입력하는 구조이다. CNN을 통해 Feature를 추출하고, 이를 RNN으로 분류하는 흐름이라 생각하면 된다.

 

 

[ An End-to-End Trainable Neural Network for Image-based Sequence Recognition and Its Application to Scene Text Recognition ]

 

 

2. RCNN

 여러 conv연산이 하나의 커널 가중치를 공유해 여러번 반복해서 연산을 진행하는 구조다. 이 구조가 이번 글에서 내가 정리하고자 하는 개념에 해당된다.

 

[ Recurrent Convolutional Neural Network for Object Recognition ]

 

 

 MNIST Tutorial을 공부할 때만 해도 CNN을 설계를 위해 아래 그림 (a)와 같이 Conv연산을 차곡차곡 쌓아올리기만 해도 충분히 높은 성능을 얻을 수 있었다. 

 

 

 하지만 딥러닝 모델로 해결하고자 하는 문제가 어렵고 복잡해지면서 새로운 방법들이 시도되었고, 그 방법들 중 하나가 Recurrent Convolutional Neural Network (RCNN) 이다. RCNN은 현재 층의 출력을 다시 입력으로 받는 흐름으로 구성되어있다.

 

 

 RCNN-64를 보면 적은 모델 파라미터를 갖고도 두번째로 낮은 Testing Error를 기록한 걸 확인할 수 있다. 이유까진 자세하게 알아보진 않았지만 어쨌거나 성능 향상에 도움이 되는 구조란 건 증명됐나보다.

 

 


 

 

그럼 어떻게 구현할까?

 구글링해보니 두 가지 방식으로 구현할 수 있는 것 같다.

  1. tf.variable_scope( ) 사용
  2. tf.layers.conv2d( )에서 reuse param 사용

 나는 후자의 방식이 간단해서 후자로 구현해봤다.

def RCNN_Block(name, input_tensor, n_filters, kernel_size = (3, 3), strides = (1, 1), padding = "same",
               dropout_ratio = 0.1, training = False, initializer = tf.contrib.layers.xavier_initializer(),
               hidden_layer = True):

    if hidden_layer:           
        input_tensor = tf.layers.max_pooling2d(inputs = input_tensor,
                                               pool_size = (2,2), strides = (2,2), padding = padding)
    # channel 수 맞추기 위한 과정
    input_tensor = tf.layers.conv2d(input_tensor, n_filters, kernel_size, strides, padding, 
                                    kernel_initializer = initializer)
    input_tensor_bn = tf.layers.batch_normalization(input_tensor, training = training)
    input_tensor_relu = tf.nn.relu(input_tensor_bn)
    input_tensor_drop = tf.layers.dropout(input_tensor_relu, rate = dropout_ratio, training = training)

    # recurrent cnn  
   # rcnn layer1
    conv1 = tf.layers.conv2d(input_tensor_drop, n_filters, kernel_size, strides, padding, 
                             kernel_initializer = initializer, name = name)
    add1 = input_tensor + conv1
    bn1 = tf.layers.batch_normalization(add1, training = training)
    relu1 = tf.nn.relu(bn1)
    drop1 = tf.layers.dropout(relu1, rate = dropout_ratio, training = training)

   # rcnn layer2
    conv2 = tf.layers.conv2d(drop1, n_filters, kernel_size, strides, padding, 
                             kernel_initializer = initializer, name = name, reuse = tf.AUTO_REUSE)
    add2 = input_tensor + conv2
    bn2 = tf.layers.batch_normalization(add2, training = training)
    relu2 = tf.nn.relu(bn2)
    drop2 = tf.layers.dropout(relu2, rate = dropout_ratio, training = training)
    
   # rcnn layer3
    conv3 = tf.layers.conv2d(drop2, n_filters, kernel_size, strides, padding, 
                             kernel_initializer = initializer, name = name, reuse = tf.AUTO_REUSE)
    add3 = input_tensor + conv3
    bn3 = tf.layers.batch_normalization(add3, training = training)
    relu3 = tf.nn.relu(bn3)
    drop3 = tf.layers.dropout(relu3, rate = dropout_ratio, training = training)

   # rcnn layer4 
    conv4 = tf.layers.conv2d(drop3, n_filters, kernel_size, strides, padding, 
                             kernel_initializer = initializer, name = name, reuse = tf.AUTO_REUSE)
    add4 = input_tensor + conv4
    bn4 = tf.layers.batch_normalization(add4, training = training) 
    relu4= tf.nn.relu(bn4 )
    output = tf.layers.dropout(relu4, rate = dropout_ratio, training = training)
    
    return output​
  • Line 17, Line 24, Line 31, Line 38
    RCNN을 구현하려면 conv1와 conv2, conv3, conv4에서 커널 가중치를 공유(weight share)하게 만들어야한다.
    그렇게 하기 위해서는
    먼저 conv1와 conv2, conv3, conv4의 tf.layers.conv2d( ) operation의 이름을 같게 설정하고,
    conv2, conv3, conv4의 tf.layers.conv2d( )에서 reuse = tf.AUTO_REUSE 로 설정해주면 된다. 
  • Line 16~18,  Line 23~25, Line 30~32, Line 37~39
    각 rcnn layer(conv → add → bn → relu → drop)에서
    conv의 입력으로 drop이 입력된다는 것과
    add에는 전층의 conv연산값과 현재층의 conv연산값이 사용된다는 점을 확인하자. 
 

 

 Retinal Vessel Segmentation Model을 공부 중에 접하게된 개념이다. 구현이 100% 맞는 거라곤 장담 못하겠지만 정리해두면 좋을 것 같아 정리했다.

 

 

[ 참고 사이트 ]

https://medium.com/@mldevhong/%EB%85%BC%EB%AC%B8-%EB%B2%88%EC%97%AD-rcnn-an-end-to-end-trainable-neural-network-for-image-based-sequence-recognition-and-its-f6456886d6f8

 

https://arxiv.org/ftp/arxiv/papers/1802/1802.06955.pdf

 

https://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Liang_Recurrent_Convolutional_Neural_2015_CVPR_paper.pdf

 

https://m.blog.naver.com/PostView.nhn?blogId=sogangori&logNo=221092445500&proxyReferer=https%3A%2F%2Fwww.google.com%2F

 

https://stackoverflow.com/questions/50030163/how-to-share-weights-using-tf-layers-conv2d

 

https://stackoverflow.com/questions/42862300/tensorflow-reuse-variable-with-tf-layers-conv2d

 

https://github.com/Insiyaa/IRCNN-keras/blob/master/IRCNN.ipynb