External Memory

プログラミング周辺知識の備忘録メイン

ドロップアウトにおける過学習抑止と学習精度のためのkeep率の設定

ニューラルネットワークを用いた機械学習において、
ドロップアウトは汎用性、つまり学習用データの正答率とテストデータの正答率のギャップを埋めるためのものであり、ニューラルネットワークそのものの学習精度を向上させるものとは言い切れない。

テストデータに対する正答率は、
学習用データに対する正答率を超えることは普通はないと思われる。
学習用データの正答率そのものが小さければ、
いくら汎用性を獲得してもテストデータの正答率は良くはならない。

そういう意味で学習用データの正答率や誤差関数(loss)値は学習精度の一つの目安となりえると思われる。また過学習防止のほかに、入力データの典型的な特徴をうまく抽出できなければならない。よってドロップアウト過学習防止と学習精度とのトレードオフのような関係を作り出す。

また、入力データに十分な普遍性が備わることがあれば、それはテストデータの正答率向上に反映されるはずなので過学習であっても問題はないように思う。



検討においてMNISTデータを使用し、
ニューラルネットワークはCNNではなくすべて全結合層で行った。

前回の検討においてドロップアウトの処理を最終層のみに限定したが、
全結合層のみで構成されたニューラルネットワークでは層間ごとにドロップアウトが必要であるように見えたので、まずそれら比較を行った。

学習のstep数を30000とし、過学習の影響がより明確に見えるように学習用のデータによる正答率とloss値も出力するようにした。

ここでのkeep率とはドロップアウト処理における出力の保持率のことである。

以下はkeep率を0.5に固定した時の比較結果である。
unitsは層ごとのunit数である。右端が入力層、左端が出力層、中央の二つがhidden layerである。データが多いので途中経過は省略した。
train_lossとtrain_accはそれぞれ学習データに対するloss値と正答率、
test_lossとtest_accはテストデータに対するloss値と正答率である。

units                  step   train_loss    train_acc    test_loss     test_acc
----------------------------------------------------------------------------------
最終層間のみドロップアウト処理keep率0.5
784,392,392,10        30000       0.0029       0.9994       0.1230       0.9824
784,784,784,10        30000       0.0081       0.9980       0.1749       0.9824
784,1024,1024,10      30000       0.0054       0.9984       0.2209       0.9791 
784,392,100,10        30000       0.0004       1.0000       0.1305       0.9834
784,784,392,10        30000       0.0029       0.9986       0.1586       0.9835
784,1024,784,10       30000       0.0092       0.9980       0.1744       0.9812

すべての層間でドロップアウト処理keep率0.5 
784,392,392,10        30000       0.0474       0.9870       0.0783       0.9766
784,784,784,10        30000       0.0332       0.9928       0.0717       0.9804
784,1024,1024,10      30000       0.0460       0.9888       0.0780       0.9791
784,392,100,10        30000       0.0438       0.9864       0.0731       0.9787
784,784,392,10        30000       0.0363       0.9902       0.0689       0.9791
784,1024,784,10       30000       0.0429       0.9906       0.0805       0.9766

最終層間のみのドロップアウト処理の場合は学習データのloss値とテストデータでのloss値を見ると、明らかに過学習の兆候が見られる。ここでは途中の結果は省略しているが、テストデータのloss値はあるstep数からstep数の増加に従って悪化する傾向にあった。学習データに対する正答率が0.999以上は受け入れがたい。

すべての層間でドロップアウト処理処理をした場合は、過学習の傾向が明確に抑えられている。テストデータのloss値が小さくなっているのが分かる。

しかしテストデータの正答率は悪化している。
loss値と正答率にはそれなりの相関があると直観的に思われるが、
ここではそうではないので、後々よく考える必要がある。

一方、学習データでの正答率は0.99前後で飽和した。この辺りが学習精度の限界であると思われる。よって、step数を増やしてもテストデータの正答率が0.99を上回ることはほぼない。

そこでkeep率を少し大きくして、検討を行った。
具体的には全ての層間に対してkeep率を0.7、0.8とした。

units                   step   train_loss    train_acc    test_loss     test_acc
----------------------------------------------------------------------------------
keep率0.7
784,392,392,10         30000       0.0060       0.9984       0.0595       0.9846
784,784,784,10         30000       0.0084       0.9980       0.0680       0.9836
784,1024,1024,10       30000       0.0068       0.9986       0.0716       0.9841
784,392,100,10         30000       0.0073       0.9984       0.0662       0.9818
784,784,392,10         30000       0.0067       0.9980       0.0590       0.9841
784,1024,784,10        30000       0.0110       0.9960       0.0776       0.9821

keep率0.8 
784,392,392,10         30000       0.0030       0.9994       0.0711       0.9826
784,784,784,10         30000       0.0032       0.9992       0.0739       0.9854
784,1024,1024,10       30000       0.0072       0.9978       0.0845       0.9837
784,392,100,10         30000       0.0026       0.9996       0.0620       0.9843
784,784,392,10         30000       0.0029       0.9990       0.0786       0.9837
784,1024,784,10        30000       0.0034       0.9988       0.0867       0.9848

過学習の傾向を抑えながらも、うまく学習精度の上限を上げることができている。
テストデータに対する正答率も向上した。
keep率0.8では少し過学習の兆候が見られたので、この系においてはkeep率0.7-0.8付近に最適値があると思われる。


ドロップアウトを用いる場合、最終の出力層だけでなく全結合層間すべてに行うのが無難であると思われる。


loss値と正答率の関係がよく分からない。
loss値が小さいからと言って正答率が良くなるとは限らないようである。
loss値が小さいということは、惜しい間違いを数多く出してしまっていると考えられるから、必要な特徴量を抽出しきれていないということが思いつく。
逆にloss値が大きいのに正答率が良い場合、出力において確率分布の分散が大きくなりやすくなる傾向があることが考えられる。

比喩としては、視力検査でぼんやりとしか見えないけどなんとなく判別がつくということと、はっきり判別がつくことの差異みたいなものを想像する。
keep率が小さいときにそれらの特徴が目についたことを考えると、
特徴がぼやけてしまうのはそれなりに妥当であるかもしれない。

さらに精度を上げるためには、それなりにもっとうまく特徴量を抽出できるように
一工夫加えるだけでいいかもしれないし、
根本的なところから考慮に入れるべきであるかもしれない。

TensorFlow exampleプログラムのmnist_deep.pyにあるドロップアウトについて

ドロップアウトの効果を確認したく、過学習状態を無理やり作っていろいろ試してみたが
なかなかうまくいかなかった。

そこでTensorFlow exampleプログラムの
tensorflow/mnist_deep.py at r1.2 · tensorflow/tensorflow · GitHub

ドロップアウト処理を行っているので、
これを利用して(こちらで使いやすいように若干プログラムを変更、内容はほぼ同じ)
ドロップアウト有無で比較することにより、ドロップアウトの効果を確認した。


プログラムを眺めている時に肝心な点に気が付いた。
学習時はドロップアウトを使用するkeep率(keep_prob=0.5)が、
テスト時はkeep率は1.0である。
見落としていた点であるが、ドロップアウトは学習時のための処理だから、
テスト時はドロップアウトを行わなない方がしっくりくる。
一応確認のためテスト時のドロップアウト処理についてお互いの比較を行った。


mnist_deep.pyは畳み込みニューラルネットワーク(CNN)を使用している。
CNNの性質から画像や動画認識特化かと思ったら、recommend systemや自然言語処理にも使われているらしい。


以下が作成したプログラムである。

import tensorflow as tf
import math
from tensorflow.examples.tutorials.mnist import input_data 

mnist = input_data.read_data_sets("mnist/", one_hot=True)

PIXEL = 784 
R = 10 

class cnn():
    
    def __init__(self,l_rate,step,keep_p=1.0):
        self.l_rate = l_rate
        self.step = step
        self.keep_p = keep_p
        self.x  = tf.placeholder(tf.float32, [None, PIXEL])
        self.y_ = tf.placeholder(tf.float32, [None, R])
        self.y = tf.reshape(self.x, [-1, 28, 28, 1])
        self.keep_prob = tf.placeholder(tf.float32)
        
    def conv2d(self,p_height,p_width,ch,next_ch):
        
        W = tf.Variable(tf.truncated_normal([p_height, p_width, ch, next_ch],
                            stddev=1.0 / math.sqrt(float(p_height * p_width))))
        b = tf.Variable(tf.zeros([next_ch]))

        self.y = tf.nn.relu(tf.nn.conv2d(self.y, W, strides=[1,1,1,1], padding='SAME')+ b)
    
    def max_pool(self):
        self.y = tf.nn.max_pool(self.y, ksize=[1,2,2,1],
            strides=[1,2,2,1], padding='SAME')
        
    def ful_connect(self,unit,next_unit):
        W = tf.Variable(tf.truncated_normal([unit, next_unit],
                        stddev=1.0 / math.sqrt(float(unit))))
        b = tf.Variable(tf.zeros([next_unit]))
        self.y = tf.reshape(self.y, [-1, unit])
        self.y = tf.nn.relu(tf.matmul(self.y, W)+ b)
        
    def drop_out(self):
        self.y = tf.nn.dropout(self.y, self.keep_prob)
        
    def train(self,unit):
        W = tf.Variable(tf.truncated_normal([unit, R],
                    stddev=1.0 / math.sqrt(float(unit))))
        b = tf.Variable(tf.zeros([R]))

        self.y = tf.matmul(self.y, W)+ b
        
        cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels = self.y_,logits = self.y))
        train_step = tf.train.AdamOptimizer(self.l_rate).minimize(cross_entropy)
        
        correct_prediction = tf.equal(tf.argmax(self.y, 1), tf.argmax(self.y_, 1))
        accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    
        sess = tf.InteractiveSession()
        tf.global_variables_initializer().run()
        
        for i in range(self.step):
            batch_xs, batch_ys = mnist.train.next_batch(50)
            sess.run(train_step, feed_dict={self.x: batch_xs, self.y_: batch_ys,self.keep_prob:self.keep_p})
            
            if i % 2000 == 0:
                print("{0:>5}{1[0]:>15.4f}{1[1]:>15.4f}".format(i,sess.run([cross_entropy,accuracy], feed_dict={self.x: mnist.test.images,
                                         self.y_: mnist.test.labels,self.keep_prob:1.0})))
        return sess,cross_entropy
    
    def print_accurency(self,unit):
        sess,cross_entropy = self.train(unit)
        correct_prediction = tf.equal(tf.argmax(self.y, 1), tf.argmax(self.y_, 1))
        accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
        print("{0:>5}{1[0]:>15.4f}{1[1]:>15.4f}".format(self.step,sess.run([cross_entropy,accuracy], feed_dict={self.x: mnist.test.images,
                                         self.y_: mnist.test.labels,self.keep_prob:1.0})))
    
if __name__ == '__main__':
    print("{0:>5}{1:>15}{2:>15}".format("step","loss","accurancy"))
    print("--------------------------------------")
    print("*** keep_prob = 0.5 ***")
    cnn1 = cnn(0.0001,20000,0.5)
    cnn1.conv2d(5,5,1,32)
    cnn1.max_pool()
    cnn1.conv2d(5,5,32,64)
    cnn1.max_pool()
    cnn1.ful_connect(7*7*64,1024)
    cnn1.drop_out()
    cnn1.print_accurency(1024)
    print("*** keep_prob = 0.8 ***")
    cnn2 = cnn(0.0001,20000,0.8)
    cnn2.conv2d(5,5,1,32)
    cnn2.max_pool()
    cnn2.conv2d(5,5,32,64)
    cnn2.max_pool()
    cnn2.ful_connect(7*7*64,1024)
    cnn2.drop_out()
    cnn2.print_accurency(1024)
    print("*** keep_prob = 1.0 ***")
    cnn3 = cnn(0.0001,20000)
    cnn3.conv2d(5,5,1,32)
    cnn3.max_pool()
    cnn3.conv2d(5,5,32,64)
    cnn3.max_pool()
    cnn3.ful_connect(7*7*64,1024)
    cnn3.drop_out()
    cnn3.print_accurency(1024)


出力は以下となる。
こちらはテスト時はドロップアウト処理を行っていない。
学習時のみで行っている。

>python mnist-cnn.py
 step           loss      accurancy
--------------------------------------
*** keep_prob = 0.5 ***
    0         2.2298         0.2129
 2000         0.0457         0.9850
 4000         0.0293         0.9895
 6000         0.0308         0.9890
 8000         0.0259         0.9913
10000         0.0223         0.9924
12000         0.0233         0.9926
14000         0.0226         0.9932
16000         0.0277         0.9913
18000         0.0229         0.9929
20000         0.0252         0.9921
*** keep_prob = 0.8 ***
    0         2.2767         0.1582
 2000         0.0441         0.9846
 4000         0.0378         0.9877
 6000         0.0302         0.9904
 8000         0.0291         0.9895
10000         0.0276         0.9919
12000         0.0282         0.9915
14000         0.0264         0.9910
16000         0.0347         0.9899
18000         0.0317         0.9916
20000         0.0347         0.9911
*** keep_prob = 1.0 ***
    0         2.3039         0.1228
 2000         0.0452         0.9844
 4000         0.0374         0.9868
 6000         0.0284         0.9896
 8000         0.0335         0.9894
10000         0.0249         0.9910
12000         0.0246         0.9924
14000         0.0379         0.9896
16000         0.0269         0.9924
18000         0.0370         0.9915
20000         0.0332         0.9912

"keep_prob=1.0"はkeep率1.0のため実質ドロップアウト処理は行われていない。

正答率からみると、ドロップアウトの効果はわずかにあるように見える。
またstep数に対するlossの値の増加傾向も、
keep率が0.5の場合は他と比べて抑えられている。
後者はおそらく過学習の特徴が出ているのだろう。

もっと明確に確認するなら学習用データでの正答率とlossも確認すべきではあるが。
他の数字画像データではもっと顕著に有意差が出るかもしれない。


もうひとつ、以下の出力データはテスト時もドロップアウト処理を行ったものである。

出力データを比較して、
テスト時はドロップアウトの処理を行わないほうがいいと思われる。
keep率が小さい場合は顕著に正答率、lossともに悪化するので特にそうである。

>python mnist-cnn.py
 step           loss      accurancy
--------------------------------------
*** keep_prob = 0.5 ***
    0         2.5310         0.1409
 2000         0.0678         0.9779
 4000         0.0465         0.9846
 6000         0.0383         0.9883
 8000         0.0348         0.9893
10000         0.0346         0.9878
12000         0.0380         0.9886
14000         0.0323         0.9893
16000         0.0315         0.9911
18000         0.0314         0.9910
20000         0.0377         0.9894
*** keep_prob = 0.8 ***
    0         2.3156         0.1184
 2000         0.0631         0.9797
 4000         0.0362         0.9877
 6000         0.0346         0.9886
 8000         0.0310         0.9892
10000         0.0346         0.9886
12000         0.0288         0.9912
14000         0.0347         0.9902
16000         0.0318         0.9909
18000         0.0268         0.9917
20000         0.0319         0.9912
*** keep_prob = 1.0 ***
    0         2.2096         0.2297
 2000         0.0486         0.9837
 4000         0.0428         0.9857
 6000         0.0314         0.9888
 8000         0.0349         0.9892
10000         0.0260         0.9918
12000         0.0241         0.9924
14000         0.0275         0.9923
16000         0.0313         0.9914
18000         0.0254         0.9926
20000         0.0248         0.9927


また前回のニューラルネットワークunit数検証のプログラムを用いて
ドロップアウトを使って実行した。
keep率は0.8と0.5で行ったが、以下の出力結果は0.5の時のものである。
前回のドロップアウトなしの結果は以下のURLにある。
taka74k4.hatenablog.com


こちらはドロップアウト処理により、
正答率に変化が見られないもしくは、逆に低下した。

ドロップアウトは使いどころを選ぶようある。

今回はドロップアウトは最後の出力の場面で使用している。
CNNの場合は畳み込み層やpooling層でうまく特徴を抽出できるような構造だから、
最後の場面だけのドロップアウト処理でもうまくいくかもしれないが、
すべての層が全結合層の場合は、途中の層間でもドロップアウト処理をしたほうがいいかもしれないし、別の工夫が必要かもしれない。

>python nnet.py

units                     step           loss      accurancy
-------------------------------------------------------------
***same size***
784,100,100,10               0         2.2678         0.1839
784,100,100,10            1000         0.1601         0.9505
784,100,100,10            2000         0.1172         0.9637
784,100,100,10            3000         0.1010         0.9686
784,100,100,10            4000         0.0900         0.9730
784,100,100,10            5000         0.0874         0.9752
784,100,100,10            6000         0.0944         0.9703
784,100,100,10            7000         0.0969         0.9726
784,100,100,10            8000         0.0941         0.9731
784,100,100,10            9000         0.0830         0.9784
784,100,100,10           10000         0.0882         0.9769

784,392,392,10               0         2.2548         0.3129
784,392,392,10            1000         0.1228         0.9607
784,392,392,10            2000         0.0840         0.9752
784,392,392,10            3000         0.0836         0.9736
784,392,392,10            4000         0.0717         0.9787
784,392,392,10            5000         0.0723         0.9795
784,392,392,10            6000         0.0752         0.9801
784,392,392,10            7000         0.0732         0.9825
784,392,392,10            8000         0.0925         0.9770
784,392,392,10            9000         0.0832         0.9793
784,392,392,10           10000         0.0769         0.9805

784,784,784,10               0         2.2183         0.1888
784,784,784,10            1000         0.1021         0.9692
784,784,784,10            2000         0.0844         0.9732
784,784,784,10            3000         0.0931         0.9722
784,784,784,10            4000         0.0836         0.9767
784,784,784,10            5000         0.0846         0.9767
784,784,784,10            6000         0.0885         0.9786
784,784,784,10            7000         0.0868         0.9796
784,784,784,10            8000         0.0972         0.9786
784,784,784,10            9000         0.1160         0.9752
784,784,784,10           10000         0.1139         0.9795

784,1024,1024,10             0         2.1906         0.2948
784,1024,1024,10          1000         0.1075         0.9681
784,1024,1024,10          2000         0.0863         0.9756
784,1024,1024,10          3000         0.0790         0.9784
784,1024,1024,10          4000         0.0940         0.9741
784,1024,1024,10          5000         0.0856         0.9778
784,1024,1024,10          6000         0.0976         0.9756
784,1024,1024,10          7000         0.1002         0.9772
784,1024,1024,10          8000         0.1054         0.9763
784,1024,1024,10          9000         0.1033         0.9805
784,1024,1024,10         10000         0.0885         0.9803

***decreasing size***
784,392,100,10               0         2.2625         0.2460
784,392,100,10            1000         0.1406         0.9545
784,392,100,10            2000         0.0907         0.9706
784,392,100,10            3000         0.0835         0.9747
784,392,100,10            4000         0.0783         0.9776
784,392,100,10            5000         0.0788         0.9776
784,392,100,10            6000         0.0851         0.9765
784,392,100,10            7000         0.0777         0.9790
784,392,100,10            8000         0.0856         0.9803
784,392,100,10            9000         0.0888         0.9793
784,392,100,10           10000         0.0932         0.9804

784,784,392,10               0         2.2167         0.1935
784,784,392,10            1000         0.1138         0.9639
784,784,392,10            2000         0.0966         0.9704
784,784,392,10            3000         0.0691         0.9793
784,784,392,10            4000         0.0810         0.9769
784,784,392,10            5000         0.0879         0.9751
784,784,392,10            6000         0.0793         0.9790
784,784,392,10            7000         0.0760         0.9804
784,784,392,10            8000         0.0856         0.9796
784,784,392,10            9000         0.0780         0.9820
784,784,392,10           10000         0.0886         0.9813

784,784,100,10               0         2.2621         0.1553
784,784,100,10            1000         0.1164         0.9654
784,784,100,10            2000         0.0857         0.9740
784,784,100,10            3000         0.0789         0.9774
784,784,100,10            4000         0.0748         0.9788
784,784,100,10            5000         0.0746         0.9799
784,784,100,10            6000         0.0774         0.9791
784,784,100,10            7000         0.0803         0.9779
784,784,100,10            8000         0.0869         0.9793
784,784,100,10            9000         0.0885         0.9803
784,784,100,10           10000         0.0999         0.9778

***increasing size***
784,784,1024,10              0         2.1892         0.2807
784,784,1024,10           1000         0.1363         0.9565
784,784,1024,10           2000         0.0859         0.9731
784,784,1024,10           3000         0.0786         0.9783
784,784,1024,10           4000         0.0799         0.9792
784,784,1024,10           5000         0.0853         0.9788
784,784,1024,10           6000         0.0766         0.9798
784,784,1024,10           7000         0.0787         0.9800
784,784,1024,10           8000         0.0971         0.9789
784,784,1024,10           9000         0.1066         0.9762
784,784,1024,10          10000         0.0909         0.9808

784,1024,1568,10             0         2.1720         0.2661
784,1024,1568,10          1000         0.1125         0.9681
784,1024,1568,10          2000         0.0875         0.9748
784,1024,1568,10          3000         0.0862         0.9747
784,1024,1568,10          4000         0.0763         0.9800
784,1024,1568,10          5000         0.0811         0.9796
784,1024,1568,10          6000         0.0864         0.9797
784,1024,1568,10          7000         0.1049         0.9789
784,1024,1568,10          8000         0.1037         0.9771
784,1024,1568,10          9000         0.0884         0.9814
784,1024,1568,10         10000         0.1216         0.9775

***3 hidden layer***
784,784,784,784,10           0         2.2269         0.3175
784,784,784,784,10        1000         0.1222         0.9643
784,784,784,784,10        2000         0.0875         0.9747
784,784,784,784,10        3000         0.0785         0.9783
784,784,784,784,10        4000         0.0838         0.9793
784,784,784,784,10        5000         0.0882         0.9770
784,784,784,784,10        6000         0.1022         0.9766
784,784,784,784,10        7000         0.0863         0.9811
784,784,784,784,10        8000         0.0903         0.9811
784,784,784,784,10        9000         0.0985         0.9809
784,784,784,784,10       10000         0.1202         0.9774

784,392,256,100,10           0         2.2843         0.1934
784,392,256,100,10        1000         0.1164         0.9646
784,392,256,100,10        2000         0.0979         0.9727
784,392,256,100,10        3000         0.0794         0.9780
784,392,256,100,10        4000         0.0914         0.9751
784,392,256,100,10        5000         0.0776         0.9792
784,392,256,100,10        6000         0.0770         0.9799
784,392,256,100,10        7000         0.0783         0.9801
784,392,256,100,10        8000         0.1013         0.9798
784,392,256,100,10        9000         0.0833         0.9817
784,392,256,100,10       10000         0.1020         0.9786

hidden layer(隠れ層)のunit数の違いによる学習制御について

ニューラルネットワークによる機械学習において、
それぞれのhidden layerごとのunit数によって
学習の制御がある程度可能であると直観的には思われる。

例えば、以下のようなことを考えた。

  • unit数が少ないとunitごとに相互作用が大きく(周囲の変化に敏感・従属的)なるので、特徴量の抽出および最適化(収束)がうまく働かない。
  • 逆にunit数が多いとアクティブなunitが疎となり、計算効率が悪いとか、相互作用が小さく学習がうまく進まない。また、パラメータ数の多さから余計な特徴まで拾って汎用性を失う。
  • これらのことから、うまくバランスのとれたunit数がある。
  • 入力データの複雑さや分類数によって、最適unit数が変わる。


(一方でテストデータに対して最適なバランスがあるとすれば、
それはテストデータに適応しただけという懸念もあり、
汎用性のためには多様なデータでテストをすべきではあると思う)


今回もMNISTデータを使用した。
主に隠れ層2層(一部3層)とし、それぞれのhidden層のunit数を変えて検証を行った。
また、誤差関数の出力も追加した。
学習の進み具合を確認したり正答率と比較して過学習などを確認することができるはず。


以下は使用したプログラムである。

import tensorflow as tf
import math
from tensorflow.examples.tutorials.mnist import input_data


mnist = input_data.read_data_sets("mnist/", one_hot=True)


class nnet():
    
    def __init__(self,units,l_rate,step):
        self.units = units
        self.l_rate = l_rate
        self.step = step
        self.x = tf.placeholder(tf.float32, [None, units[0]])
        self.y_ = tf.placeholder(tf.float32,[None, units[-1]])
        
    def build(self):
        y = self.x
        for i in range(len(self.units)-1):
            y = self.layer(y,self.units[i],self.units[i+1],i)
            
        cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels = self.y_,logits = y))        
        return cross_entropy,y
            
    def layer(self,inpt,unit,next_unit,i):
        W = tf.Variable(tf.truncated_normal([unit, next_unit],
                            stddev=1.0 / math.sqrt(float(unit))))
        b = tf.Variable(tf.zeros([next_unit]))
        
        if len(self.units)-2 != i:
            return tf.nn.relu(tf.matmul(inpt, W) + b)
        else:
            return tf.matmul(inpt, W) + b
    
    def train(self):
        cross_entropy,y = self.build()
        train_step = tf.train.AdamOptimizer(self.l_rate).minimize(cross_entropy)
        
        correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(self.y_, 1))
        accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
                
        sess = tf.InteractiveSession()
        tf.global_variables_initializer().run()
    
        for i in range(self.step):
            batch_xs, batch_ys = mnist.train.next_batch(50)
            sess.run(train_step, feed_dict={self.x: batch_xs, self.y_: batch_ys})
            
            if i % 1000 == 0:
                print("{0:<25}{1:>5}{2[0]:>15.4f}{2[1]:>15.4f}".format(",".join(map(str,self.units)),i,sess.run([cross_entropy,accuracy], feed_dict={self.x: mnist.test.images,
                                         self.y_: mnist.test.labels})))
            
        return sess,cross_entropy,y
            
    def print_accurency(self):
        sess,cross_entropy,y = self.train()
        correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(self.y_, 1))
        accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
        print("{0:<25}{1:>5}{2[0]:>15.4f}{2[1]:>15.4f}".format(",".join(map(str,self.units)),self.step,sess.run([cross_entropy,accuracy], feed_dict={self.x: mnist.test.images,
                                         self.y_: mnist.test.labels})))
    
    
if __name__ == '__main__':
    print("{0:<25}{1:>5}{2:>15}{3:>15}".format("units","step","loss","accurancy"))
    print("-------------------------------------------------------------")
    print("***same size***")
    units_s1 = [784,392,392,10]
    units_s2 = [784,784,784,10]
    units_s3 = [784,1024,1024,10]
    nets1 = nnet(units_s1,0.001,10000)
    nets1.print_accurency()
    nets2 = nnet(units_s2,0.001,10000)
    nets2.print_accurency()
    nets3 = nnet(units_s3,0.001,10000)
    nets3.print_accurency()
    print("***decreasing size***")
    units_d1 = [784,392,100,10]
    units_d2 = [784,784,392,10]
    units_d3 = [784,784,100,10]
    netd1 = nnet(units_d1,0.001,10000)
    netd1.print_accurency()
    netd2 = nnet(units_d2,0.001,10000)
    netd2.print_accurency()
    netd3 = nnet(units_d3,0.001,10000)
    netd3.print_accurency()
    print("***increasing size***")
    units_i1 = [784,784,1024,10]
    units_i2 = [784,1024,1568,10]
    neti1 = nnet(units_i1,0.001,10000)
    neti1.print_accurency()
    neti2 = nnet(units_i2,0.001,10000)
    neti2.print_accurency()
    print("***3 hidden layer***")
    units_31 = [784,784,784,784,10]
    units_32 = [784,392,256,100,10]
    net31 = nnet(units_31,0.001,10000)
    net31.print_accurency()
    net32 = nnet(units_32,0.001,10000)
    net32.print_accurency()

末尾に実際の出力を載せた。
見やすさのため、データ挿入と改行を加えた(それでもわかりにくいが)。


結果を羅列すると

  • hidden layerのunit数を入力データのunit数と同じである場合で最良の結果が得られた(正答率0.985)。しかし、unit数が大きい場合や小さい場合と比較してそれほど有意差があるわけではない。
  • decreasing size形状(ピラミッド型)も比較的結果は良好だった。
  • unit数が入力データunit数よりも大きい場合は、誤差関数の値から学習が途中でうまく進まなくなる傾向が見られたので、ノイズを拾っているのかもしれない。
  • hidden layer数を2→3層にしても正答率が上がらなかった。


単純なニューラルネットワークでunit数の最適化では
顕著に正答率が向上するわけでもないようである。

正答率向上を狙うなら、アルゴリズムの変更や新しいハイパーパラメータ導入や変更を考えたほうが早いかもしれない。

入力データの複雑さなどにもよると思うが、
hidden layerのunit数を入力unit数と同じ数とするのが
第一選択として無難であるように思う。
また、unit数をdecreasing形状(ピラミッド型)も、
もう一つの選択としてはいいかもしれない。
unit数を極端に大きくしたり小さくすべきではない。

ピラミッド型で結果が良好であったことに関して、
徐々に特徴を抽象化させていると考えれば聞こえはいいかもしれないが
他の結果と結びつかず、よくわからない部分がある。
いくつかの要素が重なって影響しているのだろうか。

>python nnet.py
units                     step           loss      accurancy
-------------------------------------------------------------
***same size***
(784,100,100,10のデータは後で追加挿入)
784,100,100,10               0         2.2757         0.1661
784,100,100,10            1000         0.1627         0.9501
784,100,100,10            2000         0.1029         0.9681
784,100,100,10            3000         0.0968         0.9691
784,100,100,10            4000         0.0914         0.9716
784,100,100,10            5000         0.0791         0.9767
784,100,100,10            6000         0.0757         0.9765
784,100,100,10            7000         0.0799         0.9760
784,100,100,10            8000         0.0810         0.9767
784,100,100,10            9000         0.0744         0.9790
784,100,100,10           10000         0.0912         0.9775

784,392,392,10               0         2.2246         0.3222
784,392,392,10            1000         0.1323         0.9591
784,392,392,10            2000         0.0740         0.9758
784,392,392,10            3000         0.0866         0.9752
784,392,392,10            4000         0.0755         0.9788
784,392,392,10            5000         0.0822         0.9761
784,392,392,10            6000         0.0838         0.9784
784,392,392,10            7000         0.0832         0.9792
784,392,392,10            8000         0.1185         0.9735
784,392,392,10            9000         0.0985         0.9786
784,392,392,10           10000         0.0909         0.9788

784,784,784,10               0         2.1951         0.2831
784,784,784,10            1000         0.1036         0.9698
784,784,784,10            2000         0.0949         0.9729
784,784,784,10            3000         0.0674         0.9803
784,784,784,10            4000         0.0899         0.9747
784,784,784,10            5000         0.0844         0.9778
784,784,784,10            6000         0.0763         0.9800
784,784,784,10            7000         0.0852         0.9791
784,784,784,10            8000         0.0861         0.9820
784,784,784,10            9000         0.0871         0.9817
784,784,784,10           10000         0.0796         0.9852

784,1024,1024,10             0         2.1686         0.4042
784,1024,1024,10          1000         0.1070         0.9646
784,1024,1024,10          2000         0.0902         0.9716
784,1024,1024,10          3000         0.0844         0.9759
784,1024,1024,10          4000         0.0748         0.9766
784,1024,1024,10          5000         0.1080         0.9717
784,1024,1024,10          6000         0.0906         0.9794
784,1024,1024,10          7000         0.0776         0.9802
784,1024,1024,10          8000         0.0969         0.9789
784,1024,1024,10          9000         0.0816         0.9821
784,1024,1024,10         10000         0.1145         0.9793

***decreasing size***
784,392,100,10               0         2.2494         0.1666
784,392,100,10            1000         0.1273         0.9592
784,392,100,10            2000         0.0829         0.9735
784,392,100,10            3000         0.0808         0.9760
784,392,100,10            4000         0.0703         0.9798
784,392,100,10            5000         0.0672         0.9814
784,392,100,10            6000         0.0759         0.9776
784,392,100,10            7000         0.0780         0.9798
784,392,100,10            8000         0.0906         0.9784
784,392,100,10            9000         0.0765         0.9801
784,392,100,10           10000         0.0814         0.9818

784,784,392,10               0         2.1779         0.3722
784,784,392,10            1000         0.1029         0.9658
784,784,392,10            2000         0.0858         0.9741
784,784,392,10            3000         0.0655         0.9806
784,784,392,10            4000         0.0787         0.9761
784,784,392,10            5000         0.0727         0.9794
784,784,392,10            6000         0.0767         0.9780
784,784,392,10            7000         0.0918         0.9748
784,784,392,10            8000         0.0806         0.9795
784,784,392,10            9000         0.0749         0.9810
784,784,392,10           10000         0.0818         0.9816

784,784,100,10               0         2.2184         0.3194
784,784,100,10            1000         0.1115         0.9654
784,784,100,10            2000         0.0844         0.9742
784,784,100,10            3000         0.0760         0.9765
784,784,100,10            4000         0.0803         0.9765
784,784,100,10            5000         0.0808         0.9756
784,784,100,10            6000         0.0740         0.9808
784,784,100,10            7000         0.0712         0.9793
784,784,100,10            8000         0.0864         0.9782
784,784,100,10            9000         0.0769         0.9805
784,784,100,10           10000         0.0794         0.9813

***increasing size***
784,784,1024,10              0         2.2237         0.1310
784,784,1024,10           1000         0.1015         0.9672
784,784,1024,10           2000         0.1167         0.9648
784,784,1024,10           3000         0.0875         0.9752
784,784,1024,10           4000         0.0641         0.9796
784,784,1024,10           5000         0.1064         0.9719
784,784,1024,10           6000         0.0741         0.9790
784,784,1024,10           7000         0.0785         0.9794
784,784,1024,10           8000         0.0917         0.9779
784,784,1024,10           9000         0.1052         0.9779
784,784,1024,10          10000         0.0983         0.9798

784,1024,1568,10             0         2.1147         0.2857
784,1024,1568,10          1000         0.0919         0.9706
784,1024,1568,10          2000         0.0913         0.9718
784,1024,1568,10          3000         0.1061         0.9748
784,1024,1568,10          4000         0.0804         0.9783
784,1024,1568,10          5000         0.0822         0.9787
784,1024,1568,10          6000         0.1009         0.9761
784,1024,1568,10          7000         0.1074         0.9744
784,1024,1568,10          8000         0.0897         0.9801
784,1024,1568,10          9000         0.1069         0.9786
784,1024,1568,10         10000         0.1149         0.9753

***3 hidden layer***
784,784,784,784,10           0         2.2147         0.2800
784,784,784,784,10        1000         0.1029         0.9696
784,784,784,784,10        2000         0.0868         0.9729
784,784,784,784,10        3000         0.1062         0.9689
784,784,784,784,10        4000         0.0862         0.9771
784,784,784,784,10        5000         0.0898         0.9751
784,784,784,784,10        6000         0.1172         0.9761
784,784,784,784,10        7000         0.0894         0.9796
784,784,784,784,10        8000         0.1042         0.9796
784,784,784,784,10        9000         0.0885         0.9830
784,784,784,784,10       10000         0.0884         0.9815

784,392,256,100,10           0         2.2668         0.1804
784,392,256,100,10        1000         0.1087         0.9655
784,392,256,100,10        2000         0.0930         0.9713
784,392,256,100,10        3000         0.0841         0.9755
784,392,256,100,10        4000         0.0838         0.9762
784,392,256,100,10        5000         0.0781         0.9797
784,392,256,100,10        6000         0.0912         0.9759
784,392,256,100,10        7000         0.0856         0.9770
784,392,256,100,10        8000         0.0963         0.9776
784,392,256,100,10        9000         0.0982         0.9771
784,392,256,100,10       10000         0.0830         0.9812

CIFAR-10データのニューラルネットワーク学習

MNISTに続いてCIFAR-10についても機械学習を行うプログラムを作成した。
CIFAR-10は自動車や飛行機、カエルなど10種類の
各6000*10個の32*32pixel RGBカラー画像データです。

画像データは種類に対してほぼランダムのようですが、
一部ある種類の画像が偏っている場合があるようです。
データは以下からダウンロード。
CIFAR-10 and CIFAR-100 datasets

前回と同じく、モデルは
入力と出力のみの2層、及びhidden layerを1層挿入した3層ネットワークです。

import tensorflow as tf
import numpy as np
import math
import time
import os
import pickle


def unpickle(file):
    with open(file, 'rb') as fo:
        dict = pickle.load(fo, encoding='bytes')
    return dict

def cifar10_data(folder):
    train_data = np.zeros((0,3072))
    train_labels = np.zeros((1))
    
    for i in range(1,6):
        fname = os.path.join(folder,"data_batch_{}".format(i))
        data_d = unpickle(fname)
        if i == 1:
            train_data = data_d[b'data']/255
            train_labels = data_d[b'labels']
        else:
            train_data = np.vstack((train_data, data_d[b'data']/255))
            train_labels = np.hstack((train_labels, data_d[b'labels']))
            
    d_dict = unpickle(os.path.join(folder, 'test_batch'))
    test_data = d_dict[b'data']/255
    test_labels = np.array(d_dict[b'labels'])
    
    return train_data,np.eye(10)[train_labels],\
           test_data,np.eye(10)[test_labels]
        
def n_net2(units,l_rate):
    
    T = time.time()
    x = tf.placeholder(tf.float32, [None, units[0]])
    
    W_1 = tf.Variable(
        tf.truncated_normal([units[0], units[1]],
                            stddev=1.0 / math.sqrt(float(units[0]))))
    
    b_1 = tf.Variable(tf.zeros([units[1]]))
    y = tf.matmul(x, W_1) + b_1
    
    y_ = tf.placeholder(tf.float32, [None, units[-1]])

    cross_entropy = tf.reduce_mean(
        tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y))

    train_step = tf.train.AdamOptimizer(l_rate).minimize(cross_entropy)

    sess = tf.InteractiveSession()
    tf.global_variables_initializer().run()
    
    train_data,train_labels,test_data,test_labels = cifar10_data("cifar-10-batches-py")
    
    for step in range(1000):
        i = (step * 100) % 50000
        batch_xs = train_data[i:i+100]
        batch_ys = train_labels[i:i+100]
        sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})
        
    correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    print(sess.run(accuracy, feed_dict={x: test_data,
                                         y_: test_labels}))  
    print("time: {:.4f} s".format(time.time()-T))

    
def n_net3(units,l_rate,keep_p=1.0,lmd=0.0):
    
    T = time.time()
    x = tf.placeholder(tf.float32, [None, units[0]])
    
    W_1 = tf.Variable(
        tf.truncated_normal([units[0], units[1]],
                            stddev=1.0 / math.sqrt(float(units[0]))))
    b_1 = tf.Variable(tf.zeros([units[1]]))
    
    hidden1 = tf.nn.relu(tf.matmul(x, W_1) + b_1)
    
    
    
    keep_prob = tf.placeholder(tf.float32)
    drop_fc = tf.nn.dropout(hidden1,keep_prob)
    
    
    W_h1 = tf.Variable(
        tf.truncated_normal([units[1], units[-1]],
                            stddev=1.0 / math.sqrt(float(units[1]))))
    b_h1 = tf.Variable(tf.zeros([units[-1]]))
    
    y = tf.matmul(drop_fc, W_h1) + b_h1
    
    y_ = tf.placeholder(tf.float32,[None,units[-1]])

    
    L2_n = tf.constant(0.0, dtype=tf.float32)    
    if lmd > 0.0:
        L2_sqr = tf.nn.l2_loss(W_1) + tf.nn.l2_loss(W_h1)
        L2_n = lmd * L2_sqr
        
    cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels = y_,logits = y))
    train_step = tf.train.AdamOptimizer(l_rate).minimize(cross_entropy + L2_n)
    
    
    sess = tf.InteractiveSession()
    tf.global_variables_initializer().run()
    
    train_data,train_labels,test_data,test_labels = cifar10_data("cifar-10-batches-py")
    
    for step in range(1000):
        i = (step * 100) % 50000
        batch_xs = train_data[i:i+100]
        batch_ys = train_labels[i:i+100]
        sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys, keep_prob: keep_p})
        
    correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    print(sess.run(accuracy, feed_dict={x: test_data,
                                         y_: test_labels, keep_prob: keep_p}))
    print("time: {:.4f} s".format(time.time()-T))
    
if __name__ == '__main__':
    units1 = [3072,10]
    n_net2(units1,0.001)
    
    units2 = [3072,2048,10]
    n_net3(units2,0.001)

unpickle関数とcifar10_data関数でデータを読み込んで、
変数に代入できる形に整えた。

データは1ファイルにつき、
列数3072個(32*32*3)、行数10000個の値0-255の二次元uint8 numpy配列です。
最初の32*32エントリに赤、次いで緑、最後に青のチャネル値がある。
255で除して、正規化した。

labelsデータは0-9の値の一次元配列でそれぞれの値は画像の種類を表す。
これらは離散的なので、onehot表現に変更した。

出力
>python cifar10.py
0.3644
time: 3.9231 s
0.4291
time: 64.1100 s

正答率50%くらいは行くかと思っていたが、それほど甘くはなかった。
とはいえ正答率がすぐに飽和しそうなMNISTに比べると
正答率向上のためにいろいろやりようがありそうで面白そうではある。

MNISTデータのニューラルネットワーク学習

機械学習の練習によく使用されるMNISTの手書き数字データを利用して、
実際にプログラムを作成して学習を行った。

モデルは簡単な2つのニューラルネットワーク
入力と出力のみの2層、及びhidden layerを1層挿入した3層ネットワークです。

import tensorflow as tf
import math
import time
from tensorflow.examples.tutorials.mnist import input_data

mnist = input_data.read_data_sets("mnist/", one_hot=True)

def n_net2(units,l_rate):
    
    T = time.time()
    x = tf.placeholder(tf.float32, [None, units[0]])
    
    W_1 = tf.Variable(
        tf.truncated_normal([units[0], units[1]],
                            stddev=1.0 / math.sqrt(float(units[0]))))
    
    b_1 = tf.Variable(tf.zeros([units[1]]))
    y = tf.matmul(x, W_1) + b_1
    
    y_ = tf.placeholder(tf.float32, [None, units[-1]])

    cross_entropy = tf.reduce_mean(
        tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y))

    train_step = tf.train.AdamOptimizer(l_rate).minimize(cross_entropy)

    sess = tf.InteractiveSession()
    tf.global_variables_initializer().run()
    
    for _ in range(1000):
        batch_xs, batch_ys = mnist.train.next_batch(100)
        sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})
        
    correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    print(sess.run(accuracy, feed_dict={x: mnist.test.images,
                                         y_: mnist.test.labels}))  
    print("time: {:.4f} s".format(time.time()-T))

    
def n_net3(units,l_rate,keep_p=1.0,lmd=0.0):
    
    T = time.time()
    x = tf.placeholder(tf.float32, [None, units[0]])
    
    W_1 = tf.Variable(
        tf.truncated_normal([units[0], units[1]],
                            stddev=1.0 / math.sqrt(float(units[0]))))
    b_1 = tf.Variable(tf.zeros([units[1]]))
    
    hidden1 = tf.nn.relu(tf.matmul(x, W_1) + b_1)
    
    
    
    keep_prob = tf.placeholder(tf.float32)
    drop_fc = tf.nn.dropout(hidden1,keep_prob)
    
    
    W_h1 = tf.Variable(
        tf.truncated_normal([units[1], units[-1]],
                            stddev=1.0 / math.sqrt(float(units[1]))))
    b_h1 = tf.Variable(tf.zeros([units[-1]]))
    
    y = tf.matmul(drop_fc, W_h1) + b_h1
    
    y_ = tf.placeholder(tf.float32,[None,units[-1]])

    
    L2_n = tf.constant(0.0, dtype=tf.float32)    
    if lmd > 0.0:
        L2_sqr = tf.nn.l2_loss(W_1) + tf.nn.l2_loss(W_h1)
        L2_n = lmd * L2_sqr
        
    cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels = y_,logits = y))
    train_step = tf.train.AdamOptimizer(l_rate).minimize(cross_entropy + L2_n)
    
    
    sess = tf.InteractiveSession()
    tf.global_variables_initializer().run()
    
    
    for _ in range(1000):
        batch_xs, batch_ys = mnist.train.next_batch(100)
        sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys, keep_prob: keep_p})
        
    correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
    print(sess.run(accuracy, feed_dict={x: mnist.test.images,
                                         y_: mnist.test.labels, keep_prob: keep_p}))
    print("time: {:.4f} s".format(time.time()-T))
    
if __name__ == '__main__':
    units1 = [784,10]
    n_net2(units1,0.001)
    
    units2 = [784,392,10]
    n_net3(units2,0.001)


input_data.read_data_sets関数を使って、
画像ファイルがない場合のみweb上からダウンロードを行い、データを読み込む。
read_data_sets関数は引数"validation_data=5000"を使って、
validation_dataの5000個データをmnist.validation.imagesで抽出することもできるので、
ハイパーパラメータの調整に使用する場合などに便利です。

今回はOptimizerにAdamを使用しています。
tensorflowのoptimizerを見比べて一番無難そうだったからです。
Adamについては以下を参照。
https://arxiv.org/pdf/1412.6980.pdf

AdagradやRMSPropのいいとこ取り改善ver.です。
傾きの影響を受けにくい性質など、ステップサイズをうまく制御しています。

mini_batch_next関数はランダムシャッフルされてから出てくる。

ドロップアウト、L2正則化の使用は逆に分類精度が低下した。
データ数や汎用化にとっての質が十分だっで過適合になりにくかったからか。
これらを適用すると、学習速度が落ちることがある。
これらの効果を確認するなら、
データ数を少なくして検証してみるのが良いかもしれない。

出力
>python mnist.py
Extracting mnist/train-images-idx3-ubyte.gz
Extracting mnist/train-labels-idx1-ubyte.gz
Extracting mnist/t10k-images-idx3-ubyte.gz
Extracting mnist/t10k-labels-idx1-ubyte.gz
0.9133
time: 0.8999 s
0.969
time: 5.1246 s

99%以上まで正答率を上げるのがどれくらい難しいかわからないが、
すぐに達成できても、検証サンプルとしては手軽に使えそうである。

機械学習におけるハイパーパラメータ最適化自動化について

機械学習においてハイパーパラメータをうまく調節するのは
面倒で時間がかかる作業である。
この最適化を自動化して、良い成果を得ている論文を見つけた。(おそらく有名な論文)
https://papers.nips.cc/paper/4522-practical-bayesian-optimization-of-machine-learning-algorithms.pdf


パラメータ最適化に関して、ベイズ統計を利用している。
またベイジアン最適化の勉強に関してはgoogle検索で見つけた以下の論文を利用した。
https://arxiv.org/pdf/1012.2599.pdf


ベイジアン最適化

ベイジアン最適化はサンプリングした値の出力値を知り得る未知の関数の
範囲\mathcal{A}における最大、最小を見つけるための手法です。

事前にサンプリングされた情報、
ここでは目的となる関数の座標の集合 \displaystyle \mathcal{D}_{t} =\{x_t,f(x_t)\}とすると
事前確率分布 P(f)は尤度 \displaystyle P(\mathcal{D}_t|f)と組み合わされ、
事後分布:

 \displaystyle P(f|\mathcal{D}_t) \propto P(\mathcal{D}_t|f)P(f)

ベイズの定理の形として表される。
この式は感覚的には、事前に知りえた関数予測と、
得られたサンプリングデータがその関数予測からどの程度妥当であるかを考慮した結果に
事後分布が依存しているような感じである。

ベイジアン最適化は簡単に言えば
上式を利用したサンプリングの仕方から最大値を探索する。
よってサンプリングアルゴリズムが重要となっており、
いろいろ考えだされているようである。

大まかな流れは以下のようになる。
・サンプリングデータを取る(初回は2点以上任意に取る)。
・そのデータから目的関数の予想確率分布を作成する。
・acquisition functionを使って評価し最大値の点をサンプリングする。
・以上を繰り返し最大値を得る。

また符号を変えれば、最小値も探索可能である。

ハイパーパラメータ最適化自動化の論文では
事前の確率分布の作成にはGaussian Process(GP)を利用している。
GPは関数の事前分布を正規分布として評価する方法である。

正規分布の分散と平均は

平均:
 \displaystyle \mu_{n+1}={\bf k}^T{\bf k}^{-1}{\bf f}_n
分散:
 \displaystyle \sigma_{n+1}=k(x_{n+1},x_{n+1})-{\bf k}^T{\bf k}^{-1}{\bf k}

として表される。

ただし、共分散関数(カーネル)は
 \displaystyle {\bf K}=\left[
    \begin{array}{rrr}
      k(x_1,x_1) & \ldots & k(x_1,x_n) \\
      \vdots & \ddots & \vdots \\
      k(x_n,x_1) & \ldots & k(x_n,x_n) \end{array}\right]

 \displaystyle {\bf k}=\left[\begin{array}{r} k(x_n,x_1) & \ldots & k(x_n,x_{n-1})\end{array}\right]

 \displaystyle k(x,x')=\theta_0(1+\sqrt{5r^2(x,x')}+\frac{5}{3}r^2(x,x'))exp\{-\sqrt{5r^2(x,x')}\}

 \displaystyle r^2(x,x')=\sum_{d=1}^{D}(x_d-x'_d)^2/\theta^2

である。
ここでのカーネルの選択k(x,x')はARD Matern 5/2 kernelと呼ばれるもので
一般的にはsquared exponential kernelがポピュラーらしい。
カーネルの選択がGPの性質をほぼ決定している。

共分散行列については
https://en.wikipedia.org/wiki/Covariance_matrix
に説明がある。


ここで利用するacquisition functionはExpected Improvement(EI)と呼ばれるものである。
他のacquisition functionとして
"Probability of Improvement"と"GP Upper Confidence Bound"が紹介されていた。
挙動やパラメータ数の関係でEIが選ばれている。

acquisition function(EI)は以下のように定義される。

 \displaystyle a_{EI}(x;\{x_n,y_n\},\theta)=\sigma(x;\{x_n,y_n\},\theta)(\gamma(x)\Phi(\gamma(x))+\mathcal{N}(\gamma(x);0,1))

 \displaystyle \gamma(x)=\frac{f(x_{best})-\mu(x)}{\sigma(x)}

 \displaystyle \mathcal{N}(x)は標準正規分布関数、
 \displaystyle \Phi(・)は累積正規分布関数である。

また、ハイパーパラメータ数を減らすためacquisition functionの代わりに
integrated acquisition functionを用い(ハイパーパラメータはθのみになる)、
並列計算のため、評価するデータの保留にモンテカルロ法を用いている。
 \displaystyle \widehat{a}(x)=\int_{\mathbb{R}^J}a(x)P(\{y_j\}_{j=1}^J|\{x_j\}_{j=1}^J,\{x_n,y_n\}_{n=1}^N)dy_1\ldots dy_J


この自動化によってCIFAR-10の3層畳み込みネットワークでの学習で
熟練の人間がハイパーパラメータをチューンしたモデルよりも3%以上よい結果が得られている。
CIFAR-10の3層畳み込みネットワークのコードのURLは以下である。
https://code.google.com/archive/p/cuda-convnet/


またこの自動化プログラムは以下のURLにある。
Home · jaberg/hyperopt Wiki · GitHub



ベイジアン最適化の理解が甘い感じがする。
どこかの段階でまじめに統計を勉強すべきだろうか。

重み初期設定と活性化関数による学習速度の改善

前回の3層ニューラルネットワーク構造の学習の検討において
学習速度の速度の問題が目についた。
http://taka74k4.hatenablog.com/entry/2017/08/03/120000


今回は学習速度の改善を試みた。

過適合による学習速度の低下ではなく、始めから学習速度は遅かった。
現状では速度の向上には以下が思いつく。

1 重みの初期値の変更
2 より適当な活性化関数の選択
3 最適化法と学習率の最適化

1~3について順番に検証を行った。

1について

重みの初期化には前回、
標準偏差を入力パラメータ数の平方の逆数をとった正規分布を用いた。
この手法は、シグモイド関数系統に用いられる手法であって
softmaxなどに用いる必要性はない。
正規分布の鋭さを抑える理由はシグモイド関数σ(z)においてz>1 or z<0の時に
傾きがなだらかになる影響で重みを変化させてもσ(z)はあまり変化せず
学習速度が遅くなるからである。

重みの初期値はhidden layerへの出力に影響すると思われる。
hidden layerへの出力の関数として前回使用したのはsoftmax関数である。
softmax関数は確率分布を出力し、分布を鋭くするような性質を持つ。
ここでは重みやバイアスの差を際立たせるような役目である。

いろいろ試行錯誤をしたが、
結局すべて0に設定したとき以外は分布の変化に対し学習速度に影響はなかった。

2について

hiddenlayerへの出力の関数としてsoftmax関数を用いたが、
より軽量で効果が期待できるといわれるReLUを使った。
ReLUは以下のような単純な関数である。

relu(x) = max(0,x)

結果、重みの分布の標準偏差を大きめにすることで、
2000回ほどで正答率が約0.98で飽和した。
学習速度と精度ともに大幅に向上したことになる。
ただ標準偏差が10.0以上になると徐々に極小付近で振動するようになった。
3.0程度がちょうどよかった。

なぜそうなったのか。
標準偏差が小さい場合、学習速度は極端に遅かった。
また、分布の平均値は0以上ならそれほど違いはないようである。

softmax関数の出力は1以下であるのに比べて、ReLUは1以上の値を出力する。
標準偏差が小さい場合に学習速度が遅かったことと合わせて、
最終出力の確率分布が鋭いことが学習速度に影響を与えているように見える。
入力のパラメータ数が少ない上に、
分布が平坦だとlayerごとに特徴が表れにくいからだろうか。
またsoftmax関数のクロスエントロピーの性質から分布がある程度鋭いほうが、
この場合においては学習速度は速くなりそうである。

以下は参考で標準偏差の違いによる重みの初期値である。

print(sess.run(tf.truncated_normal([2, 5],stddev=1.0  / math.sqrt(float(2)))))
[[ 0.02702628  0.91012442 -0.18702209 -0.03638476 -0.67407352]
 [-0.39508435 -0.0171926   0.02808492 -0.95571172 -0.11896304]]

print(sess.run(tf.truncated_normal([2, 5],stddev=1.0)))
[[-1.10803401  0.15547203 -1.11678505  0.81495434 -0.77232492]
 [ 0.86105257 -0.29577333 -0.5726105  -0.10565408 -1.52466965]]

print(sess.run(tf.truncated_normal([2, 5],stddev=3.0 )))
[[ 2.23127007 -1.23616624  5.98013353  0.65966594 -3.75138664]
 [ 3.99544954 -3.38144922  0.53547996  1.00201726  2.26863289]]

print(sess.run(tf.truncated_normal([2, 5],stddev=10.0 )))
[[  5.31843233   3.73229957  -8.37697124   6.94522333  -1.52599192]
 [ 14.96617889   8.58791733   1.42042267  -1.91340804 -12.14058113]]

3について

活性化関数にReLUを用いた場合にさらに成果を上げるために、
簡単な最適化法と学習率の検討を行った。

使用した勾配降下法はAdagrad、GradientDescent、Adadeltaである。
この3つを選んだ理由は私がまだこの3つしか知らないからである。
Adadeltaはもともと使っていたAdagradと比べて、
学習速度の低下は起こりにくい性質を持つので少し期待はしていた。

Adagradを使用した場合、学習率は0.5程度で良好な結果が得られた。
学習率がそれより小さいと学習速度が下がり、
大きいと正答率が落ちた、勾配の和の影響を受けているようである。

GradientDescentを使用した場合は極小値付近で振動する傾向がみられた。

Adadeltaの場合は3.0-5.0程度の係数を掛けることでAdagradと似たような結果が得られた。

結局、さらに学習効果を向上させることはできなかった。

Pythonコード

import tensorflow as tf
import math
import random

N = 10000
R = 9 #label Range
times = 10000
HIDDEN_UNITS = 100

abmlist = [random.uniform(0,1) for i in range(N)] 
 
d_list = [random.uniform(0.1,1) for i in range(N)]

apmlist = [ [0 for i in range(R)] for j in range(N)]

for i in range(N):
    #星の等級式 apm = abm + 5 * log(distance)- 5
    apmlist[i][round(abmlist[i]*3+5*math.log10(d_list[i]*10))]=1

x = tf.placeholder(tf.float32,[None,2])
y_ = tf.placeholder(tf.float32,[None,R])

with tf.name_scope('hidden1'):

    
    weights1 = tf.Variable(
        tf.truncated_normal([2, HIDDEN_UNITS],
                            stddev=3.0),
        name='weights')

    biases1 = tf.Variable(tf.zeros([HIDDEN_UNITS]),
                         name='biases')
    hidden1 = tf.nn.relu(tf.matmul(x, weights1) + biases1)
 
  
with tf.name_scope('output'):
    
    weights_o = tf.Variable(
        tf.truncated_normal([HIDDEN_UNITS, R],
                            stddev=1.0 / math.sqrt(float(HIDDEN_UNITS))),
        name='weights')
    
    biases_o = tf.Variable(tf.zeros([R]),
                         name='biases')
    logits = tf.matmul(hidden1, weights_o) + biases_o
 

cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels = y_,logits = logits))
train_step = tf.train.AdagradOptimizer(0.5).minimize(cross_entropy)
 
sess = tf.InteractiveSession()
tf.global_variables_initializer().run()

testlist = [[i,j] for (i,j) in zip(abmlist[-500:],d_list[-500:])]
labellist = apmlist[-500:]
        
for i in range(times):
    s = (i * 100)%(N-500)
    sess.run(train_step,feed_dict={x:[[j,k] for (j,k) in zip(abmlist[s:s+100],d_list[s:s+100])],y_:apmlist[s:s+100]})
    if i%1000 ==0:
        correct_prediction = tf.equal(tf.arg_max(logits,1),tf.arg_max(y_,1))
        accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))
        print(sess.run(accuracy,feed_dict={x:testlist,y_:labellist}))

最適化法やハイパーパラメータ類を少し変えただけでかなり挙動が変化する上、
法則性を見つけるのも困難である。
うまくいいパラメータを探し出すのは骨の折れる作業である。

初期の検討から比較すると訓練回数が大幅に低減し、正答率も向上した。

題材として星の等級をモデルにした検証は深追いせずここまでとする。
深層学習のモデルとしてはあまり適当だとは思えないからである。

MNISTやCIFARなどでいろいろ試すことになる予定。