(TensorFlow) CNN
CNN
다음과 같이 그래프를 미리 빌드해 놓은 다음, 신경망에 통과시킬 input image를 실행 시점에 sess.run( , feed_dict={})
를 통해 넘긴다.
1
2
3
4
5
6
7
8
9
10
## 구식 방법
W1 = tf.Variable(tf.random\_normal([3, 3, 1, 32], stddev=0.01)) #H, W, C, FN
b1 = tf.Variable(tf.zeros([32]))
L1 = tf.nn.conv2d(X\_img, W1, strides=[1, 1, 1, 1], padding="SAME")
L1 = tf.nn.relu(L1+b1)
L1 = tf.nn.max\_pool(L1, ksize=[1, 2, 2, 1],
strides=[1, 2, 2, 1], padding="SAME")
L1 = tf.nn.dropout(L1, keep\_prob=keep\_prob)
위 작업을 tf.layers
를 이용해 더 간단히 처리할 수 있다. 보통 다음과 같은 방법을 사용한다. channel은 따로 입력받지 않는다. 어차피 filters == input channel
수 이니까.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
conv1 = tf.layers.conv2d(inputs=X\_img, filters=32, kernel\_size=[3, 3],
padding="SAME", activation=tf.nn.relu)
pool1 = tf.layers.max\_pooling2d(inputs=conv1, pool\_size=[2, 2],
padding="SAME", strides=2)
dropout1 = tf.layers.dropout(inputs=pool1,
rate=0.7, training=self.training)
.....
dense4 = tf.layers.dense(inputs=flat,
units=625, activation=tf.nn.relu)
.....
padding="SAME"
은 stride가 1, 1
일 때 입력 형상이 그대로 출력 형상이 되는 padding을 말한다. conv layer에서는 stride가 1, 1이고, padding="SAME"
이므로 입력 형상이 그대로 출력 형상이 된다. (?, 28, 28, 32) pooling layer에서는 stride가 2, 2이고, padding="SAME"
이므로 출력 형상이 입력 형상의 반이 된다. (?, 14, 14, 32) (stride의 첫 번째와 마지막은 그냥 1로 고정이라고 생각하면 된다)
dropout에서 rate
와 training(boolean)
을 따로 지정한다. 훈련 시는 rate
에 적당한 값을 주고 시험 시는 rate=0
으로 지정하는 것과 결국 같은건데, 그렇게 하지 않는 이유는 각 layer의 dropout에 서로 다른 값이 들어갈 경우 처리가 약간 귀찮기 때문. 그래서 build 시 값을 미리 지정해 두고, dropout을 적용할지 말지는 training flag가 결정한다. False
인 경우 자동으로 rate=0
이 된다.
학습한 파라미터 저장 / 불러오기
1
2
saver = tf.train.Saver(max\_to\_keep=10) # default is 5
save\_path = saver.save(sess, "./Variables/mnist-en{}-{}".format(ensemble, step+restore\_step))
1
2
3
4
if restore\_step:
saver.restore(sess, "./Variables/mnist-en{}-{}".format(ensemble, restore\_step))
else:
sess.run(tf.global\_variables\_initializer())
max\_to_keep
default는 5이며 0을 주면 오래된 파일 안지우고 계속 저장하는데 용량이 꽤 큼. 파일 이름이 같으면 계속 덮어 쓰게 된다. restore
를 try-except
로 처리하거나, 파일 존재 유무를 체크해서 파일이 없어도 돌아가도록 만들 수는 있는데, save_path
를 명시해서 파라미터를 불러오도록 의도했는데 못불러온다는건 꽤 큰 문제이므로 그냥 프로그램 종료되도록 놔두는게 나은 것 같다.