딥러닝 프레임워크 중 가장 많은 인지도를 받고 있는 것은 단연 Tensorflow 이다. 하지만 이렇게 좋은 프레임워크가 있음에도 불구하고 Pytorch, Keras와 같은 딥러닝 프레임워크가 끊임 없이 탄생하고 많은 연구자들의 사랑을 받는 이유는 무엇일까? 많은 이유가 있겠지만 그 중 하나로 들 수 있는것이 바로 단순하고 간결한 인터페이스 덕분일 것이다.
저수준의 Tensorflow API는 연구자들의 입맛에 맛게 자유롭게 구현이 가능하다는 장점도 있지만 자유로운 만큼 그에 따르는 대가도 적지 않다. 연구자들의 자유 속에 만들어진 코드는 개인차가 심할 것이며 코드 중복 및 복잡도가 올라갈 가능성이 아주 높아진다. 이와 같은 이슈로 인해 Tensorflow에서도 고수준 경량 API를 제공하고자 만들어 진 것이 바로 TF-Slim이다.
"무엇이 좋은가요?"
TF-Slim 라이브러리를 사용하면 Tensorflow를 통해 변수를 정의하고 모델을 구현할 때 중복되는 코드를 제거할 수 있으며 네트워크 모델을 더욱 간결하고 쉽게 만들 수 있다. 이렇게 간소화된 코드는 가독성 및 연구자의 실수를 줄여 주는 장점을 가지고 있으며 연구자들이 핵심 영역에 집중할 수 있도록 도와준다.
TF-Slim은 Tensorflow 내에 포함되어 있으므로 별도 설치가 필요하지 않으며 TF-Slim의 추상화는 모두 CNN(Convolution Neural Network)에 관한 것이다.
*CNN은 동일한 합성곱 계층(Convolution Layer)을 여러번 재사용하는 보일러플레이트(boilerplate code)코드가 많다. 이와 같은 코드 구성은 코드를 복잡하게 하고 가독성을 떨어트린다. 그러므로 TF-Slim을 활용할 경우 코드를 많이 간소화 할 수 있다는 장점이 있다.
•보일러플레이트 코드 : https://en.wikipedia.org/wiki/Boilerplate_code
TF-Slim은 처음부터 연구자가 모델을 생성하고 학습할 수 있으며 사전에 학습된 모델을 활용하여 학습할 수 있도록 제공해준다.
"TF-Slim 기능 알아보기"
import tensorflow.contrib.slim as slim
위와 같이 TF-Slim을 임포트 한다.
<Variables>
Tensorflow에서는 변수를 선언하고 초기화, 정규화를 각각 해주었다면 TF-Slim에서는 한번에 이와 같은 일을 처리할 수 있다.
weights = slim.variable('weights',
shape=[10, 10, 3, 3],
initializer=tf.truncated_normal_initializer(stddev=0.1),
regularizer=slim.l2_regularizer(0.05),
device='/CPU:0')
위 소스를 보면 weights 변수를 선언할 때 초기화(initializer), 정규화(regularizer), 디바이스(device), shape 정보를 한번에 전달하여 변수를 정의하고 있다.
*L2 정규화와 CPU를 사용한 절단정규분호(truncated_normal_initializer())로 초기화하고 있음.
<Layer>
TF-Slim 라이브러리를 이용하면 기존의 저수준 API를 사용한 Tensorflow 코드 보다 더욱 간소화된 코드로 Layer 코드를 구현할 수 있다. 아래는 합성곱 계층을 TF-Slim을 이용해 정의한 내용이다.
net = slim.conv2d(inputs, 64, [5, 5], padding='SAME',
weights_initalizer=tf.truncated_normal_initializer(stddev=0.01),
weights_regularizer=slim.l2_regularizer(0.0005), scop='conv1')
위 소스를 보면 알겠지만 가중치 초기화, 정규화, 활성화 함수에 대한 내용을 한번에 설정할 수 있으며 TF-Slim에 repeat을 이용하면 동일한 합성곱 계층을 한줄로 표현할 수도 있다.
net = slim.repeat(net, 5, slim.conv2d, 128, [3, 3], scope='con1')
만약 repeat이 없었다면 위 소스 코드는 아래와 같이 작성되었을 것이다.
net = slim.conv2d(net, 128, [3, 3], scope='con1_1')
net = slim.conv2d(net, 128, [3, 3], scope='con1_2')
net = slim.conv2d(net, 128, [3, 3], scope='con1_3')
net = slim.conv2d(net, 128, [3, 3], scope='con1_4')
net = slim.conv2d(net, 128, [3, 3], scope='con1_5')
단, repeat은 하나의 계층에 사이즈가 동일할 경우만 사용할 수 있으며 만약 서로 다른 사이즈를 사용해야 된다면 stack을 사용하면 된다.
#stack 사용전
net = slim.conv2d(net, 64, [3, 3], scope='con1_1')
net = slim.conv2d(net, 64, [1, 1], scope='con1_2')
net = slim.conv2d(net, 128, [3, 3], scope='con1_3')
net = slim.conv2d(net, 128, [1, 1], scope='con1_4')
net = slim.conv2d(net, 256, [3, 3], scope='con1_5')
#stack 사용
net = slim.stack(net, slim.conv2d, [(64, [3, 3]), (64, [1, 1]),
(128, [3, 3]), (128, [1, 1]),
(256, [3, 3])], scope='con')
slim.conv2d 이외 TF-Slim에서 지원하고 있는 Layer는 아래와 같다.
[표]
Layer | TF-Slim |
---|---|
BiasAdd | slim.bias_add |
BatchNorm | slim.batch_norm |
Conv2d | slim.conv2d |
Conv2dInPlane | slim.conv2d_in_plane |
Conv2dTranspose (Deconv) | slim.conv2d_transpose |
FullyConnected | slim.fully_connected |
AvgPool2D | slim.avg_pool2d |
Dropout | slim.dropout |
Flatten | slim.flatten |
MaxPool2D | slim.max_pool2d |
OneHotEncoding | slim.one_hot_encoding |
SeparableConv2 | slim.separable_conv2d |
UnitNorm | slim.unit_norm |
<arg_scope>
TF-Slim에서 제공하고 있는 slim.arg_scope을 이용하면 slim.arg_scope에서 사전에 인자값을 선언함으로서 동일 scope에 정의되어 있는 여러개의 Layer에 해당 인자값들을 별도로 선언 하지 않고 사용 할 수 있게 된다. 즉, 계층별 중복적으로 선언해야 하는 인자값을 slim.arg_scope에서 한번만 함으로서 중복 코드를 제거해 준 것이다.
with slim.arg_scope([slim.conv2d],
padding='SAME',
activation_fn=tf.nn.elu,
weights_initializer=tf.truncated_normal_initializer(stddev=0.01)):
inputs = tf.reshape(x, [-1, 28, 28, 1])
net = slim.conv2d(inputs=inputs, num_outputs=32, kernel_size=[5, 5], scope='conv1')
net = slim.max_pool2d(inputs=net, kernel_size=[2, 2], scope='pool1')
net = slim.conv2d(net, 64, [5, 5], scope='conv2')
net = slim.max_pool2d(net, [2, 2], scope='pool2')
net = slim.flatten(net, scope='flatten3')
위 소스를 보면 slim.arg_scope을 이용해 padding, 활성화 함수, 초기화, 정규화 값을 slim.conv2d 계층에 동일하게 설정하는 것을 볼 수 있다.
"TF-Slim으로 VGG-16모델 구현하기"
# https://github.com/tensorflow/tensorflow/tree/r1.8/tensorflow/contrib/slim
def vgg16(inputs):
with slim.arg_scope([slim.conv2d, slim.fully_connected],
activation_fn=tf.nn.relu,
weights_initializer=tf.truncated_normal_initializer(0.0, 0.01),
weights_regularizer=slim.l2_regularizer(0.0005)):
net = slim.repeat(inputs, 2, slim.conv2d, 64, [3, 3], scope='conv1')
net = slim.max_pool2d(net, [2, 2], scope='pool1')
net = slim.repeat(net, 2, slim.conv2d, 128, [3, 3], scope='conv2')
net = slim.max_pool2d(net, [2, 2], scope='pool2')
net = slim.repeat(net, 3, slim.conv2d, 256, [3, 3], scope='conv3')
net = slim.max_pool2d(net, [2, 2], scope='pool3')
net = slim.repeat(net, 3, slim.conv2d, 512, [3, 3], scope='conv4')
net = slim.max_pool2d(net, [2, 2], scope='pool4')
net = slim.repeat(net, 3, slim.conv2d, 512, [3, 3], scope='conv5')
net = slim.max_pool2d(net, [2, 2], scope='pool5')
net = slim.fully_connected(net, 4096, scope='fc6')
net = slim.dropout(net, 0.5, scope='dropout6')
net = slim.fully_connected(net, 4096, scope='fc7')
net = slim.dropout(net, 0.5, scope='dropout7')
net = slim.fully_connected(net, 1000, activation_fn=None, scope='fc8')
return net
'인공지능 > Tensorflow' 카테고리의 다른 글
[Tensorflow]TFRecord 파일 생성 방법(텐서플로우 데이타 포맷) (0) | 2018.11.29 |
---|---|
[Tensorflow] MNIST 학습해보기!! (0) | 2018.10.05 |
[Tensorflow] 텐서보드 사용하기 (0) | 2018.09.11 |
[Tensorflow] 학습 모델 저장하고 재사용하기 (0) | 2018.09.10 |