RCNN 개념과 CNN 개념을 하나로 연결해서 설계된 모델이 있다. 연결 순서에 따라 아래와 같이 나뉜다.
- Convolutional Recurrent Neural Network (CRNN)
- 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를 기록한 걸 확인할 수 있다. 이유까진 자세하게 알아보진 않았지만 어쨌거나 성능 향상에 도움이 되는 구조란 건 증명됐나보다.
그럼 어떻게 구현할까?
구글링해보니 두 가지 방식으로 구현할 수 있는 것 같다.
- tf.variable_scope( ) 사용
- 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://arxiv.org/ftp/arxiv/papers/1802/1802.06955.pdf
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
'Deep Learning > Keras & Tensorflow' 카테고리의 다른 글
ImgaeDataGenerator.flow_from_directory을 이용해 이미지 증식하는 방법 (0) | 2021.08.31 |
---|---|
keras.models.Model( ) (0) | 2021.08.31 |
MNIST 분류 모델, 조금 다르게 실행해보자 / get_tensor_by_name( ) (0) | 2021.08.31 |
Tensorflow 개념 정리) 텐서, 변수, 오퍼레이션, 계산 그래프 (0) | 2021.08.31 |
tf.gfile.GFile( )은 무엇일까 (2) | 2019.07.03 |