Dropoutのハイパーパラメータの決定について
ここ1ヶ月ほど、会社のアルバイトで6クラスの画像分類モデルをTensorflowを使って作ることを任された
色々詰まった、考えさせられたことがあったのでメモ
画像分類だったらCNNで、ネットワークはVGG16だったりAlexNetで作りましたべろ〜んwって感じでいいと思うが、そのモデルをProtocolBufferファイルにしてラズパイなどに乗せて動かすということなのでネットワークの層が深すぎるとProtocolBufferファイルが400~700MBになって重すぎて敵わん...となってしまうので6層ほどで適当に作ることにした
全然6層じゃなかった
畳み込みのkernel_sizeは3~5で、Dropoutは0.5で調整した
いざ学習させたらなぜか学習が進まなかった。損失は減っているが精度が0.23~0.25間で向上しなかった。
別の2層の畳み込み層と2層の全結合層のモデルは学習が進んでいたことから、自分のモデルのパラメータの設定がおかしいのだろうかと悩んだ
畳み込み層、学習係数のハイパーパラメータは同じだったので、全結合層のパラメータやLocal response Normalization
層が原因なのかと考えたりした
試行錯誤した結果、上記のせいで学習が滞っていたのではなく、Dropoutのパラメータが原因だったということがわかった
上のネットワーク図からわかるように2つの隠れ層である全結合層にはDropoutを適用している。どちらもkeep_prob
は0.5に設定した
このkeep_prob
をどちらも0.8に変更した結果、学習が進んだ
Dropoutについては青いイルカさんの本とかでは仕組みや嬉しいことの説明はあったが最適なパラメータの設定値の説明はなかった。なのでいつも0.5で設定していた
調べたところ最初に来る全結合層に対してDropoutを適用するとき、そのパラメータを0.5にするのはあまり良くないそうだ
というわけで学習が進まない問題は回避できてなんとかなった
その後、全結合層のノード数とDropoutのkeep_probパラメータの間に何か関係があるのか気になったので調べることにした
使うデータはTensorflowのチュートリアルで有名なMNISTで全結合層のみのネットワークを構築した
mport tensorflow as tf import numpy as np from tensorflow.examples.tutorials.mnist import input_data mnist = input_data.read_data_sets('MNIST_data', one_hot=True) t = tf.placeholder(tf.float32, shape=(None,10)) X = tf.placeholder(tf.float32, shape=(None,28*28),name="input") keep_prob_1 = tf.placeholder(tf.float32) # ドロップアウトする割合 keep_prob_2 = tf.placeholder(tf.float32) # ドロップアウトする割合 stddev = np.sqrt(2.0 / 28*28) h_W1 = tf.Variable(tf.truncated_normal([28*28,256], stddev=stddev)) h_b1 = tf.Variable(tf.constant(0.1, shape=[256])) h_fc1 = tf.nn.relu(tf.matmul(X,h_W1) + h_b1) h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob_1) stddev = np.sqrt(2.0 / 256) h_W_fc2 = tf.Variable(tf.truncated_normal([256,512], stddev=stddev)) h_b_fc2 = tf.Variable(tf.constant(0.1, shape=[512])) h_fc2 = tf.nn.relu(tf.matmul(h_fc1_drop, h_W_fc2) + h_b_fc2) h_fc2_drop = tf.nn.dropout(h_fc2, keep_prob_2) stddev = np.sqrt(2.0 / 512) W_fc3 = tf.Variable(tf.truncated_normal([512,10], stddev=stddev)) b_fc3 = tf.Variable(tf.constant(0.1, shape=[10])) fc3 = tf.matmul(h_fc2_drop, W_fc3) + b_fc3 p = tf.nn.softmax(fc3,name="output") loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=fc3, labels=t)) optimizer = tf.train.AdamOptimizer(1e-4) train_step = optimizer.minimize(loss) correct_prediction = tf.equal(tf.argmax(fc3,1), tf.argmax(t,1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) saver = tf.train.Saver() ### 学習の実行 sess = tf.Session() sess.run(tf.global_variables_initializer()) i = 0 for i in range(0,10000): batch = mnist.train.next_batch(30) sess.run(train_step, feed_dict={X:batch[0],t:batch[1],keep_prob_1:0.5,keep_prob_2:0.5}) if i % 1000 == 0: train_acc, train_loss = sess.run([accuracy,loss], feed_dict={X:batch[0],t:batch[1],keep_prob_1:1.0,keep_prob_2:1.0}) print("[Train] step: %d, loss: %f, acc: %f" % (i, train_loss, train_acc)) # テストデータによるモデルの評価 test_acc, test_loss = sess.run([accuracy,loss], feed_dict={X:mnist.test.images,t:mnist.test.labels,keep_prob_1:1.0,keep_prob_2:1.0}) print("[Test] loss: %f, acc: %f" % (test_loss, test_acc)) saver.save(sess, "./ckpt/mnist") sess.close()
h_fc1
とh_fc2
にdropoutを適用したりしなかったり、keep_prob_1
とkeep_prob_2
の値を変えて訓練結果を確認した
まずh_fc1
とh_fc2
のノード数が、48,96でdropoutを適用しなかった時の結果(多いので2000step毎だけ)
[Train] step: 0, loss: 15.007373, acc: 0.066667 [Test] loss: 12.547914, acc: 0.097100 [Train] step: 2000, loss: 0.960308, acc: 0.666667 [Test] loss: 0.894258, acc: 0.735600 [Train] step: 4000, loss: 0.381689, acc: 0.800000 [Test] loss: 0.603150, acc: 0.818700 [Train] step: 6000, loss: 0.460051, acc: 0.866667 [Test] loss: 0.484288, acc: 0.855700 [Train] step: 8000, loss: 0.595815, acc: 0.866667 [Test] loss: 0.419885, acc: 0.873900
精度はいまいち...次にノード数はそのままでdropoutを適用し、keep_prob_1
、keep_prob_2
が共に0.5に設定したとき
[Train] step: 0, loss: 18.346718, acc: 0.033333 [Test] loss: 12.756997, acc: 0.122000 [Train] step: 2000, loss: 1.504397, acc: 0.500000 [Test] loss: 1.692765, acc: 0.451800 [Train] step: 4000, loss: 1.380499, acc: 0.566667 [Test] loss: 1.422444, acc: 0.552400 [Train] step: 6000, loss: 1.460326, acc: 0.600000 [Test] loss: 1.400168, acc: 0.609900 [Train] step: 8000, loss: 1.548799, acc: 0.533333 [Test] loss: 1.289341, acc: 0.667200
適用なしの時より悪化しているのがわかる
色々試した結果、keep_prob_1
は1.0(dropoutを適用しない)keep_prob_2
を0.8に設定した結果がdropout適用しないときより良かった
mnist-dropout-48-96-10-08-ckpt [Train] step: 0, loss: 17.872959, acc: 0.066667 [Test] loss: 16.914766, acc: 0.103000 [Train] step: 2000, loss: 1.499189, acc: 0.666667 [Test] loss: 1.236634, acc: 0.663800 [Train] step: 4000, loss: 0.986408, acc: 0.666667 [Test] loss: 0.778916, acc: 0.762500 [Train] step: 6000, loss: 0.436568, acc: 0.866667 [Test] loss: 0.605089, acc: 0.813300 [Train] step: 8000, loss: 0.290827, acc: 0.933333 [Test] loss: 0.504056, acc: 0.846000
次に上記の結果はノード数が少なすぎるからkeep_prob_1
、keep_prob_2
が共に0.5の結果は悪かったのでは?と疑問に思い、各ノード数を256,512にした時でも同様に結果を観察することにした
まずはdropout適用しないときの結果
[Train] step: 0, loss: 9.332975, acc: 0.166667 [Test] loss: 10.855021, acc: 0.127700 [Train] step: 2000, loss: 0.380751, acc: 0.966667 [Test] loss: 0.349467, acc: 0.905500 [Train] step: 4000, loss: 0.222563, acc: 0.966667 [Test] loss: 0.241931, acc: 0.931800 [Train] step: 6000, loss: 0.108663, acc: 0.966667 [Test] loss: 0.199128, acc: 0.943000 [Train] step: 8000, loss: 0.077383, acc: 0.966667 [Test] loss: 0.172284, acc: 0.948200
元々の表現力が上がったせいか48,96のときより精度が良くなっている
次にkeep_prob_1
、keep_prob_2
が共に0.5のときの結果
[Train] step: 0, loss: 7.612897, acc: 0.200000 [Test] loss: 9.190090, acc: 0.129300 [Train] step: 2000, loss: 0.934076, acc: 0.733333 [Test] loss: 0.541602, acc: 0.850500 [Train] step: 4000, loss: 0.303545, acc: 0.966667 [Test] loss: 0.453306, acc: 0.869100 [Train] step: 6000, loss: 0.434249, acc: 0.900000 [Test] loss: 0.529966, acc: 0.884700 [Train] step: 8000, loss: 0.707538, acc: 0.866667 [Test] loss: 0.521837, acc: 0.899100
精度自体はdropoutなしの方より悪い気するけど48,96でkeep_prob_1
、keep_prob_2
が共に0.5のときの結果と比べて、学習が停滞しているようには感じない
やはりノード数が多いからだろうか?
最後にkeep_prob_1
は1.0(dropoutを適用しない)keep_prob_2
を0.8に設定した結果
[Train] step: 0, loss: 15.822262, acc: 0.000000 [Test] loss: 15.912350, acc: 0.069200 [Train] step: 2000, loss: 0.096453, acc: 0.966667 [Test] loss: 0.338137, acc: 0.914300 [Train] step: 4000, loss: 0.138627, acc: 0.966667 [Test] loss: 0.221914, acc: 0.936700 [Train] step: 6000, loss: 0.005314, acc: 1.000000 [Test] loss: 0.170819, acc: 0.948000 [Train] step: 8000, loss: 0.288768, acc: 0.866667 [Test] loss: 0.142067, acc: 0.958000
dropoutなしよりとても良くなった
Dropoutのパラメータの設定でここまで結果が変わるとは思っていなかったので調べてて面白かった