第21回では、多項分類器と隠れ層が1層からなるニューラルネットワークを使用して、0〜9までの手書き文字を分類しました。今回は、この隠れ層の前段に、畳み込みフィルターとプーリング層を挿入したニューラルネットワークをご紹介します。隠れ層の前段に畳み込みフィルターを挿入するニューラルネットワークは、特に畳み込みニューラルネットワーク(Convolutional Neural Networks: CNN)と呼ばれています。画像認識の分野でよく使われます。畳み込みフィルターでは、画像のエッジや特徴を抽出し、プーリング層では画像の解像度を低くすることで認識率を向上させます。一見、画像の解像度を下げると認識率が悪くなるように思えるのですが、解像度を下げることで、高い空間周波数のノイズが除去されることにより、認識率が向上します。
TensorFlowによる畳み込みニューラルネットワークを使用した機械学習の流れは、概ね以下になります。なお、Pythonのバージョンは3.5以上を想定しています。
- データを入力する。
- 変量(image)の正規化とラベル(0〜9)の変換(one-hot vector representation)
- 最適化すべき畳み込みフィルターを定義する。
- プーリング層を定義する
- TensorFlowにより隠れ層(hidden layer)の最適化すべきVariableとNet Input関数および活性化関数を定義する。
- TensorFlowにより、出力層(output layer)の最適化すべき係数(Variable)と活性化関数を定義する。
- TensorFlowにより誤差関数とその最適化法を定義する。
- TensorFlowにより精度を定義する。
- TensorFlowによりセッションを定義する。
- TensorFlowによりセッションを実行する。
では、各ステップを詳しく見ていきましょう。
①データを入力する。
ここで使用するデータは前回と同様に、MNIST(Mixed National Institute of Standards an Technology)と呼ばれるデータで、60,000個のトレーニングデータと10,000個のテストデータからなります。トレーニングデータは、250名(半分は高校生、半分は国税調査局の職員)による0〜9までの手書き数字のラベルと画像データからなります。また、テストデータについても、同じ割合で(トレーニングデータとは)別の人の手書きデータからなります。それぞれの手書き数字は、0〜9までのラベルと28x28ピクセルのグレースケールの画像データで、0〜255の整数値で各ピクセルの濃度が与えられています。
まず、対話形式ではなく、Pythonのスクリプトをファイルで用意します。Pythonスクリプトの先頭に以下の2行を添付しておくことをお勧めします。
#!/usr/bin/env python # -*- coding:utf-8 -*- |
TensorFlowには、Webで公開されているMNISTのデータセットをダウンロードして、NumPyのarrayオブジェクトとして格納するモジュールがあらかじめ用意されているのですが、Windows環境で実行するとエラーが発生したため(macOS SierraおよびUbuntu 16.4LTSでは問題がありませんでした)、ここでは、TensorFlowのモジュールを使用しない方法で、データ入力を行うことにします。まず、以下のURLをオープンし、トレーニンセットのimageとラベル、テストセットのimageとラベルをそれぞれ、ローカルのファイルとしてダウンロードします。
http://yann.lecun.com/exdb/mnist/
- train-images-idx3-ubyte.gz: training set images (9912422 bytes)
- train-labels-idx1-ubyte.gz: training set labels (28881 bytes)
- t10k-images-idx3-ubyte.gz: test set images (1648877 bytes)
- t10k-labels-idx1-ubyte.gz: test set labels (4542 bytes)
gzip *ubyte.gz -d |
展開したファイルを読み込む関数を以下のように定義します。
def load_mnist(path, kind='train'): """Load MNIST data from `path`""" labels_path = os.path.join(path, '%s-labels.idx1-ubyte' % kind) images_path = os.path.join(path, '%s-images.idx3-ubyte' % kind) with open(labels_path, 'rb') as lbpath: magic, n = struct.unpack('>II', lbpath.read(8)) labels = np.fromfile(lbpath, dtype=np.uint8) with open(images_path, 'rb') as imgpath: magic, num, rows, cols = struct.unpack(">IIII", imgpath.read(16)) images = np.fromfile(imgpath, dtype=np.uint8).reshape(len(labels), 784) return images, labels |
トレーニングデータとテストデータを上記の関数を使用して読み込みます。スクリプトの実行は、mnistディレクトリー(またはフォルダー)の1つ上の親のディレクトリー(またはフォルダー)から実行してください。
X_train, y_train = load_mnist('mnist', kind='train') X_test, y_test = load_mnist('mnist', kind='t10k') |
load_mnist関数は、2つの配列を返します。最初の配列はN x m次元のNumPyの配列(image)です、ここでNはサンプルの数、mは変量の数です。 X_train は60,000 x 784の配列、X_testは、10,000x784の配列です。imageはもともと28x28のピクセルでしたが、1次元(28x28=784)のベクターに変換しています。2番目の配列は、ラベル(0〜9の整数)の配列で、y_trainは60,000x10の配列、y_testは10,000x10の配列になります。
画像データの分類のアルゴリズムですが、この画像データは、もともと28x28ピクセルの画像ですが、各ピクセルの濃度の数値を一列に並べてしまえば、28x28=784次元のベクトルになります。言い換えると784次元空間の1つの点に対応します。すなわち、MNISTの画像データは、784次元空間上に配置された多数の点の集合とみなすことができます。この時、同じ数字に対応する画像は、784次元空間上で互いに近い場所に集まっていると期待することができます。そして、784次元空間を10個の領域に分割することで、その領域に対応する数字が決まることになります。
②変量(image)の正規化とラベル(0〜9)の変換(one-hot vector representation)
一般に、変量をx、変量の最大値と最小値をそれぞれ、x_max, x_min とすると、変量xの正規化の値は、(x - x_min)/(x_max - x_min)で与えられます。ここで、変量(image)データの各要素の最大値は255, 最小値は0 であるため、結果として、各要素を255で割ったものが正規化された値となります。ただし、ここでは正規化とは別に各変量(image)にペナルティ(Lambda)を課しています。これは、後ほど定義する誤差関数による値を適切な範囲で収束させるためのものです。
Lambda = 10000 C = 1/Lambda X_train_pre = X_train/255*C X_test_pre = X_test/255*C |
次にラベル y_trainとy_testを多項分類器により分類できるように以下のように変換します。このようなベクトルを one-hot vectorと呼びます。
"0" -> [1,0,0,0,0,0,0,0,0,0]
"1" -> [0,1,0,0,0,0,0,0,0,0]
"2" -> [0,0,1,0,0,0,0,0,0,0]
"3" -> [0,0,0,1,0,0,0,0,0,0]
"4" -> [0,0,0,0,1,0,0,0,0,0]
"5" -> [0,0,0,0,0,1,0,0,0,0]
"6" -> [0,0,0,0,0,0,1,0,0,0]
"7" -> [0,0,0,0,0,0,0,1,0,0]
"8" -> [0,0,0,0,0,0,0,0,1,0]
"9" -> [0,0,0,0,0,0,0,0,0,1]
y_train_s = np.zeros((len(y_train),10), dtype=np.int) for i in range(0,len(y_train),1): y_train_s[i,y_train[i]] = 1 y_test_s = np.zeros((len(y_test),10), dtype=np.int) for i in range(0,len(y_test),1): y_test_s[i,y_test[i]] = 1 |
③最適化すべき畳み込みフィルターを定義する。
ここからが、TensorFlowの実際の記述になります。num_filtersは、畳み込みフィルターで使用するフィルターの個数です。フィルターの個数は任意ですが、ここでは、32を指定します。28x28=784個のピクセルからなる画像データを「placeholder」に格納します。更に画像データに対して畳み込みフィルターを適用する関数tf.nn.conv2dが用意されています。
この第1引数のx_imageの、tf.reshape(x, [-1,28,28,1])は、画像サイズを28x28ピクセルでレイヤー数を1, 最初の-1はplaceholderに格納されているxのデータ数に応じて、適切なサイズに調整するとう意味です。
また、第2引数のW_convは、画像の特徴を抽出する最適化すべきフィルターです。TensorFlowではフィルターそのものを最適化の対象にすることが可能です。[5,5,1,num_filters]は、フィルターのサイズが5x5ピクセル、入力レイヤー数が1、出力レイヤー数(フィルターの個数)がnum_filterにより指定された値を意味しています。
stddevオプションで乱数の範囲を±0.1の範囲に指定しています。
第3引数のstridesは、一定の間隔でピクセルを抽出して計算することにより、画像サイズを小さくするためのオプションです。[1,1,1,1]を指定すると、全てのピクセルに対して計算します。
第4引数のpaddingは、画像の端の部分にフィルターを適用する方法を指定します。SAMEを指定した場合、存在しない部分のピクセルについて、その値を0として計算します。
畳み込みフィルターの結果は、h_convに出力され、次のプーリング層に入力されます。
import tensorflow as tf num_filters = 32 x = tf.placeholder(tf.float32, [None, 784]) x_image = tf.reshape(x, [-1,28,28,1]) W_conv = tf.Variable(tf.truncated_normal([5,5,1,num_filters], stddev=0.1)) h_conv = tf.nn.conv2d(x_image, W_conv, strides=[1,1,1,1], padding='SAME') |
④プーリング層を定義する
畳み込みフィルターの出力であるh_convを入力として、tf.nn.max_pool関数により、画像の解像度を縮小するためのプーリング層の処理を定義します。ksizeオプションで指定されたサイズのブロック(ここでは2x2)をstridesオプションで指定した間隔(ここでは2x2)で移動しながら、ブロック内におけるピクセルの最大値で置き換えて行きます。これにより、28x28ピクセルだったオリジナル画像がプーリング層で14x14ピクセルの解像度に置き替わります。また、プーリング層から出力されるデータの総数は[14x14xフィルター数」となりますので、tf.reshape関数で変換した値をh_pool_flatに格納して、次の隠れ層へ出力します。
h_pool =tf.nn.max_pool(h_conv, ksize=[1,2,2,1], strides=[1,2,2,1], padding='SAME') h_pool_flat = tf.reshape(h_pool, [-1, 14*14*num_filters]) |
⑤TensorFlowにより隠れ層(hidden layer)の最適化すべきVariableとNet Input関数および活性化関数を定義する。隠れ層について以下のように記述します。
num_units1 = 14*14*num_filters num_units2 = 1024 w2 = tf.Variable(tf.truncated_normal([num_units1, num_units2])) b2 = tf.Variable(tf.zeros([num_units2])) hidden2 = tf.nn.relu(tf.matmul(h_pool_flat, w2) + b2) |
num_units1はプーリング層から出力されたデータの総数で、num_units2は隠れ層におけるノードの数です。ここでは1024を指定していますが、num_units2に2以上の自然数を指定することにより、ノードの数を増減ことができます。ノードの数を増やすことにより、より複雑なモデルに対応することができますが、それに伴いより多くのコンピュータ資源が必要となります。機械学習により最適化すべき隠れ層の係数b2およびw2を「Variable」に格納します。最適化すべき係数のb2の値は、0(ゼロ)に初期化しますが、w2は乱数を用いて初期値を決定しています。w2は(14x14xnum_filters)x1024の行列です。Net Input関数と活性化関数による隠れ層の出力としてhidden2を定義します。活性化関数としてReLU関数を使用しています。活性化関数の詳細については、第15回をご参照ください。ここで注意すべきことは、Net Input関数も活性化関数も、必ずtensorflowライブラリーのものを使用しなければならないということです。例えば、Pythonプログラミングとしては、tf.matmul(h_pool_flat, w2)もnp.dot(h_pool_flat, w2)も全く同じ機能で計算結果も同じですが、np.dot(h_pool_flat, w2)を使用するとTensorFlowのセッション実行時にエラーとなるので、注意が必要です。
⑥TensorFlowにより出力層(output layer)の最適化すべきVariableとNet Input関数および活性化関数を定義する。
最適化すべき係数 w0 と b0を0(ゼロ)に初期化します。隠れ層からの出力であるhidden2の値を入力にNet Input関数を計算し、softmax関数で確率に変換します。はじめに、softmaxを使用した多項分類器の仕組みについて少し説明しておきたいと思います。入力データの各変量 x1, x2, ... ,xmについて、各係数w1, w2, ... ,wmを掛け合わせた和を「Net Input 関数」と言います。 f = w0 + x1*w1 + x2*w3, ... ,xm*wm のように表せます。 このNet Input関数の結果を入力にしてラベルを分類する関数を「活性化関数」と呼びます。多項分類器の活性化関数の1つにsoftmax関数があります。softmax関数は、分類すべきラベルがk個ある場合、k個の要素のうち、n( n < k)番目の要素だけが最も大きくなるように値を返します。例えば、分類クラスが3つで、1番目のクラスを返す場合は[0.8,0.1,0.1]、2番目のクラスを返す場合は[0.2,0.7,0.1]、3番目のクラスを返す場合は[0.2,0.2,0.6]のように連続した確率の値を返します。活性化関数による計算結果と元の分類データとを比較して、誤差(Error)を計算し、誤差が最小になるように、隠れ層と出力層の各係数w(w2,b2,w0,b0)の値と畳み込みフィルターのW_convの値を再調整するセッションを繰り返します。
w0 = tf.Variable(tf.zeros([num_units2, 10])) b0 = tf.Variable(tf.zeros([10])) p = tf.nn.softmax(tf.matmul(hidden2, w0) + b0) |
機械学習により最適化すべき係数w0およびb0は「Variable」に格納します。w0は1024x10の行列です。活性化関数として、tf.nn.softmax関数を使用し確率pを定義します。ここでも注意すべきことは、Net Input関数も活性化関数も、必ずtensorflowライブラリーのものを使用しなければならないということです。softmax関数に関しても同様で、以下のように指定してもPythonプログラミング上は同じなのですが、TensorFlowのセッション実行時にエラーになります。
def softmax(x): """Compute softmax values for each sets of scores in x.""" e_x = np.exp(x - np.max(x)) return e_x / e_x.sum() |
⑦TensorFlowにより誤差関数とその最適化法を定義する。
ラベルデータ(10種類の数字)tのデータの型と大きさ(ここでは10)をplaceholderを用いて定義します。サンプル数については指定は不要です(ここではNoneを指定)。また、実際のデータ内容はセッション実行時に指定するため、ここでは指定しません。
次に誤差関数を定義します。i番目のデータの正解がkだった場合の確率をpとすると
正解を表すラベルは、t = [0,...0,1,0,...0) (k番目の要素のみ1)で与えられます。i番目のデータを正しく予測する確率Piは、Pi = Πp^t となります。
N個のデータ全てを正しく予測する確率は、P = P1 x P2 x ... xPN = ΠPi = ΠΠ p^t となり、この確率Pを最大にするようにVariable w0, b0,w2,b2及びW_convの値を最適化すれば良いことになります。これを最尤推定法(maximum likelihood method)と呼びます。したがって誤差関数としては、-Pを最小になるように定義すれば良いわけです。対数関数を使って、次のように記述できます。
loss = - log P = -log ΠΠp^t = -ΣΣt*log(p)
ここで、ΣΣは、tesorflowライブラリーで、 reduce_sum関数が用意されています。また、train.AdamOptimizer関数で、誤差関数 lossを最小化するよう設定しています。
t = tf.placeholder(tf.float32, [None, 10]) loss = -tf.reduce_sum(t * tf.log(p)) train_step = tf.train.AdamOptimizer().minimize(loss) |
⑧TensorFlowにより精度を定義する。
1行目のargmax関数は、複数の要素が並んだリストから、最大値を持つ要素のインデックスを取り出す関数で、要素のインデックスを比較することで、計算値pが元データtと一致しているかどうかを判定しています。equalは、2つのインデックスが等しいかどうかを判定して、Bool値を返す関数です。correct_predictionには、データ数分の要素のBool値が格納されます。2行目では、cast関数で、correct_predictionの各要素のBool値を1,0の値に変換します。reduce_mean関数は、1,0の値に変換されたcorrect_predictionの各要素の平均値を返します。これが結局、機械学習アルゴリズムの精度となります。
correct_prediction = tf.equal(tf.argmax(p, 1), tf.argmax(t, 1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) |
⑨TensorFlowによりセッションを定義する。
Variable(w0, b0,w2,b2及びW_conv)の値を最適化するための、セッションを定義して、Variableの値を初期化します。
sess = tf.InteractiveSession() sess.run(tf.initialize_all_variables()) |
⑩TensorFlowによりセッションを実行する。
誤差関数が最小になるよう、Variableの最適化を9,000回繰り返します。ここでは、100回繰り返すごとに、その時点での誤差関数lossとトレーニングデータの精度Accuracy(train)とテストデータのAccuracy(test)の値をそれぞれ計算し、表示しています。sess.runの中で、feed_dictにより、placeholderのx と t にそれぞれ、実際のデータ(変量と分類ラベル)を割り当てています。
また、トレーニングデータとテストデータを全て使うのではなく、100回繰り返すごとに、トレーニングデータとテストデータが最初の要素から順次、取り出され変更する仕組みになっています。この手法は「ミニバッチ」もしくは、「確率的勾配降下法」と呼ばれています。利点は、トレーニングデータが大量にある場合、1回あたりのデータ量を減らして、その代わり最適化の処理を何度も繰り返すことで、全体の計算時間を短くすることができます。また、誤差関数の値が極小値になるのを避けて、真の最小値に達することができます。これは、勾配ベクトルが正確に計算されないため、誤差関数の値はジグサグに移動して行きます。このため、極小値の付近にやってきた場合でも、パラメーターの修正処理を何度も繰り返せば、偶然に極小値の谷から出て、本来の最小値の方へ向かうことができるのです。
k = 100 X_train_p = X_train_pre[0:k,:] y_train_p = y_train_s[0:k,:] X_test_p = X_test_pre[0:k,:] y_test_p = y_test_s[0:k,:] for i in range(0,9000,1): sess.run(train_step, feed_dict={x:X_train_p, t:y_train_p}) if i % k == 0 and i !=0: loss_val, acc_val = sess.run( [loss, accuracy], feed_dict={x:X_train_p, t:y_train_p}) acc_test_val =sess.run(accuracy, feed_dict={x:X_test_p, t:y_test_p}) print ('Step: %d, Loss: %f, Accuracy(train): %f, Accuracy(test): %f' % (i, loss_val, acc_val, acc_test_val)) X_train_p = X_train_pre[i:i+k,:] y_train_p = y_train_s[i:i+k,:] X_test_p = X_test_pre[i:i+k,:] y_test_p = y_test_s[i:i+k,:] |
(実行結果)
Step: 100, Loss: 183.777985, Accuracy(train): 0.660000, Accuracy(test): 0.570000
Step: 200, Loss: 73.483429, Accuracy(train): 0.880000, Accuracy(test): 0.600000
Step: 300, Loss: 34.218094, Accuracy(train): 0.890000, Accuracy(test): 0.650000
Step: 400, Loss: 16.268526, Accuracy(train): 0.970000, Accuracy(test): 0.730000
Step: 500, Loss: 14.372743, Accuracy(train): 0.980000, Accuracy(test): 0.830000
Step: 600, Loss: 11.040833, Accuracy(train): 0.990000, Accuracy(test): 0.760000
Step: 700, Loss: 6.549707, Accuracy(train): 1.000000, Accuracy(test): 0.790000
Step: 800, Loss: 5.300950, Accuracy(train): 1.000000, Accuracy(test): 0.750000
Step: 900, Loss: 10.609255, Accuracy(train): 0.980000, Accuracy(test): 0.800000
Step: 1000, Loss: 7.941400, Accuracy(train): 0.990000, Accuracy(test): 0.810000
Step: 1100, Loss: 13.099250, Accuracy(train): 0.980000, Accuracy(test): 0.830000
Step: 1200, Loss: 5.448847, Accuracy(train): 1.000000, Accuracy(test): 0.790000
Step: 1300, Loss: 6.604653, Accuracy(train): 0.990000, Accuracy(test): 0.680000
Step: 1400, Loss: 6.445173, Accuracy(train): 1.000000, Accuracy(test): 0.770000
Step: 1500, Loss: 2.798632, Accuracy(train): 1.000000, Accuracy(test): 0.860000
Step: 1600, Loss: 4.183159, Accuracy(train): 1.000000, Accuracy(test): 0.810000
Step: 1700, Loss: 2.909752, Accuracy(train): 1.000000, Accuracy(test): 0.850000
Step: 1800, Loss: 1.210684, Accuracy(train): 1.000000, Accuracy(test): 0.760000
Step: 1900, Loss: 2.458305, Accuracy(train): 1.000000, Accuracy(test): 0.850000
Step: 2000, Loss: 2.325439, Accuracy(train): 1.000000, Accuracy(test): 0.800000
Step: 2100, Loss: 2.931401, Accuracy(train): 1.000000, Accuracy(test): 0.840000
Step: 2200, Loss: 1.084124, Accuracy(train): 1.000000, Accuracy(test): 0.790000
Step: 2300, Loss: 1.239462, Accuracy(train): 1.000000, Accuracy(test): 0.890000
Step: 2400, Loss: 1.561638, Accuracy(train): 1.000000, Accuracy(test): 0.830000
Step: 2500, Loss: 2.063262, Accuracy(train): 1.000000, Accuracy(test): 0.820000
Step: 2600, Loss: 0.899242, Accuracy(train): 1.000000, Accuracy(test): 0.830000
Step: 2700, Loss: 1.466760, Accuracy(train): 1.000000, Accuracy(test): 0.840000
Step: 2800, Loss: 2.304255, Accuracy(train): 1.000000, Accuracy(test): 0.860000
Step: 2900, Loss: 0.871685, Accuracy(train): 1.000000, Accuracy(test): 0.890000
Step: 3000, Loss: 1.698694, Accuracy(train): 1.000000, Accuracy(test): 0.860000
Step: 3100, Loss: 1.164159, Accuracy(train): 1.000000, Accuracy(test): 0.890000
Step: 3200, Loss: 1.179613, Accuracy(train): 1.000000, Accuracy(test): 0.800000
Step: 3300, Loss: 0.805498, Accuracy(train): 1.000000, Accuracy(test): 0.910000
Step: 3400, Loss: 0.717708, Accuracy(train): 1.000000, Accuracy(test): 0.830000
Step: 3500, Loss: 0.438190, Accuracy(train): 1.000000, Accuracy(test): 0.890000
Step: 3600, Loss: 0.661679, Accuracy(train): 1.000000, Accuracy(test): 0.850000
Step: 3700, Loss: 0.753053, Accuracy(train): 1.000000, Accuracy(test): 0.950000
Step: 3800, Loss: 0.892217, Accuracy(train): 1.000000, Accuracy(test): 0.860000
Step: 3900, Loss: 0.590006, Accuracy(train): 1.000000, Accuracy(test): 0.830000
Step: 4000, Loss: 0.538472, Accuracy(train): 1.000000, Accuracy(test): 0.860000
Step: 4100, Loss: 0.758038, Accuracy(train): 1.000000, Accuracy(test): 0.850000
Step: 4200, Loss: 0.940807, Accuracy(train): 1.000000, Accuracy(test): 0.840000
Step: 4300, Loss: 0.799272, Accuracy(train): 1.000000, Accuracy(test): 0.830000
Step: 4400, Loss: 0.993536, Accuracy(train): 1.000000, Accuracy(test): 0.860000
Step: 4500, Loss: 0.565234, Accuracy(train): 1.000000, Accuracy(test): 0.860000
Step: 4600, Loss: 0.348917, Accuracy(train): 1.000000, Accuracy(test): 0.890000
Step: 4700, Loss: 1.034211, Accuracy(train): 1.000000, Accuracy(test): 0.900000
Step: 4800, Loss: 0.511234, Accuracy(train): 1.000000, Accuracy(test): 0.920000
Step: 4900, Loss: 0.483357, Accuracy(train): 1.000000, Accuracy(test): 0.850000
Step: 5000, Loss: 0.675492, Accuracy(train): 1.000000, Accuracy(test): 0.910000
Step: 5100, Loss: 0.504364, Accuracy(train): 1.000000, Accuracy(test): 0.930000
Step: 5200, Loss: 1.076527, Accuracy(train): 1.000000, Accuracy(test): 0.910000
Step: 5300, Loss: 0.503094, Accuracy(train): 1.000000, Accuracy(test): 0.940000
Step: 5400, Loss: 0.709761, Accuracy(train): 1.000000, Accuracy(test): 0.990000
Step: 5500, Loss: 0.271724, Accuracy(train): 1.000000, Accuracy(test): 0.990000
Step: 5600, Loss: 0.448247, Accuracy(train): 1.000000, Accuracy(test): 0.910000
Step: 5700, Loss: 0.493549, Accuracy(train): 1.000000, Accuracy(test): 0.870000
Step: 5800, Loss: 0.428497, Accuracy(train): 1.000000, Accuracy(test): 0.930000
Step: 5900, Loss: 0.429683, Accuracy(train): 1.000000, Accuracy(test): 0.900000
Step: 6000, Loss: 0.261063, Accuracy(train): 1.000000, Accuracy(test): 0.810000
Step: 6100, Loss: 0.189601, Accuracy(train): 1.000000, Accuracy(test): 0.860000
Step: 6200, Loss: 0.220398, Accuracy(train): 1.000000, Accuracy(test): 0.940000
Step: 6300, Loss: 0.279369, Accuracy(train): 1.000000, Accuracy(test): 0.990000
Step: 6400, Loss: 0.227113, Accuracy(train): 1.000000, Accuracy(test): 0.960000
Step: 6500, Loss: 0.410849, Accuracy(train): 1.000000, Accuracy(test): 0.900000
Step: 6600, Loss: 0.186502, Accuracy(train): 1.000000, Accuracy(test): 0.870000
Step: 6700, Loss: 0.523566, Accuracy(train): 1.000000, Accuracy(test): 0.850000
Step: 6800, Loss: 0.221379, Accuracy(train): 1.000000, Accuracy(test): 0.870000
Step: 6900, Loss: 1.199175, Accuracy(train): 1.000000, Accuracy(test): 0.920000
Step: 7000, Loss: 0.539828, Accuracy(train): 1.000000, Accuracy(test): 0.930000
Step: 7100, Loss: 0.708153, Accuracy(train): 1.000000, Accuracy(test): 0.970000
Step: 7200, Loss: 0.593428, Accuracy(train): 1.000000, Accuracy(test): 0.990000
Step: 7300, Loss: 0.948721, Accuracy(train): 1.000000, Accuracy(test): 0.970000
Step: 7400, Loss: 0.583049, Accuracy(train): 1.000000, Accuracy(test): 0.970000
Step: 7500, Loss: 0.376657, Accuracy(train): 1.000000, Accuracy(test): 0.890000
Step: 7600, Loss: 0.557975, Accuracy(train): 1.000000, Accuracy(test): 0.930000
Step: 7700, Loss: 0.396158, Accuracy(train): 1.000000, Accuracy(test): 0.990000
Step: 7800, Loss: 0.461487, Accuracy(train): 1.000000, Accuracy(test): 0.970000
Step: 7900, Loss: 0.524427, Accuracy(train): 1.000000, Accuracy(test): 0.890000
Step: 8000, Loss: 0.379618, Accuracy(train): 1.000000, Accuracy(test): 0.940000
Step: 8100, Loss: 0.194338, Accuracy(train): 1.000000, Accuracy(test): 0.890000
Step: 8200, Loss: 0.758357, Accuracy(train): 1.000000, Accuracy(test): 0.980000
Step: 8300, Loss: 1.079205, Accuracy(train): 1.000000, Accuracy(test): 0.950000
Step: 8400, Loss: 0.315644, Accuracy(train): 1.000000, Accuracy(test): 0.960000
Step: 8500, Loss: 0.352871, Accuracy(train): 1.000000, Accuracy(test): 0.880000
Step: 8600, Loss: 0.318024, Accuracy(train): 1.000000, Accuracy(test): 0.940000
Step: 8700, Loss: 0.731874, Accuracy(train): 1.000000, Accuracy(test): 0.980000
Step: 8800, Loss: 0.791516, Accuracy(train): 1.000000, Accuracy(test): 0.990000
Step: 8900, Loss: 0.770000, Accuracy(train): 1.000000, Accuracy(test): 0.990000
誤差関数(Loss)の値が減少すると共に、トレーニングデータとテストデータの精度(Accuracy)の値が共に上昇していることがわかります。また、確率的勾配降下法を使用しているため、誤差関数の値と精度が上がったり、下がったりを繰り返しながら、徐々に最適解へと向かっている様子がわかります。第21回の結果と比較して、MNISTの手書き文字画像においては、畳み込みニューラルネットワークを使用した分類結果と、隠れ層(1層)のみのニューラルネットワークの分類結果とは、さほど変わらないことがわかります。
なお、今回の内容は、以下の書籍の4章を参考に執筆いたしました。ご興味のある方は、ご参考いただければと思います。
「TensorFlowで学ぶディープラーニング入門 ~畳み込みニューラルネットワーク徹底解説~」
中井 悦司 (著)
出版社: マイナビ出版
全体を通してのコードは以下のようになります。なお、本コードの稼働環境は、Python3.5以上を想定しています。
#!/usr/bin/env python # -*- coding:utf-8 -*- import os import struct import numpy as np def load_mnist(path, kind='train'): """Load MNIST data from `path`""" labels_path = os.path.join(path, '%s-labels.idx1-ubyte' % kind) images_path = os.path.join(path, '%s-images.idx3-ubyte' % kind) with open(labels_path, 'rb') as lbpath: magic, n = struct.unpack('>II', lbpath.read(8)) labels = np.fromfile(lbpath, dtype=np.uint8) with open(images_path, 'rb') as imgpath: magic, num, rows, cols = struct.unpack(">IIII", imgpath.read(16)) images = np.fromfile(imgpath, dtype=np.uint8).reshape(len(labels), 784) return images, labels X_train, y_train = load_mnist('mnist', kind='train') print('X_train: Rows: %d, columns: %d' % (X_train.shape[0], X_train.shape[1])) X_test, y_test = load_mnist('mnist', kind='t10k') print('X_test: Rows: %d, columns: %d' % (X_test.shape[0], X_test.shape[1])) Lambda = 10000 C = 1/Lambda X_train_pre = X_train/255*C X_test_pre = X_test/255*C y_train_s = np.zeros((len(y_train),10), dtype=np.int) for i in range(0,len(y_train),1): y_train_s[i,y_train[i]] = 1 y_test_s = np.zeros((len(y_test),10), dtype=np.int) for i in range(0,len(y_test),1): y_test_s[i,y_test[i]] = 1 import tensorflow as tf tf.set_random_seed(20170816) num_filters = 32 x = tf.placeholder(tf.float32, [None, 784]) x_image = tf.reshape(x, [-1,28,28,1]) W_conv = tf.Variable(tf.truncated_normal([5,5,1,num_filters], stddev=0.1)) h_conv = tf.nn.conv2d(x_image, W_conv, strides=[1,1,1,1], padding='SAME') h_pool =tf.nn.max_pool(h_conv, ksize=[1,2,2,1], strides=[1,2,2,1], padding='SAME') h_pool_flat = tf.reshape(h_pool, [-1, 14*14*num_filters]) num_units1 = 14*14*num_filters num_units2 = 1024 w2 = tf.Variable(tf.truncated_normal([num_units1, num_units2])) b2 = tf.Variable(tf.zeros([num_units2])) hidden2 = tf.nn.relu(tf.matmul(h_pool_flat, w2) + b2) w0 = tf.Variable(tf.zeros([num_units2, 10])) b0 = tf.Variable(tf.zeros([10])) p = tf.nn.softmax(tf.matmul(hidden2, w0) + b0) t = tf.placeholder(tf.float32, [None, 10]) loss = -tf.reduce_sum(t * tf.log(p)) train_step = tf.train.AdamOptimizer().minimize(loss) correct_prediction = tf.equal(tf.argmax(p, 1), tf.argmax(t, 1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) sess = tf.InteractiveSession() sess.run(tf.initialize_all_variables()) k = 100 X_train_p = X_train_pre[0:k,:] y_train_p = y_train_s[0:k,:] X_test_p = X_test_pre[0:k,:] y_test_p = y_test_s[0:k,:] for i in range(0,9000,1): sess.run(train_step, feed_dict={x:X_train_p, t:y_train_p}) if i % k == 0 and i !=0: loss_val, acc_val = sess.run( [loss, accuracy], feed_dict={x:X_train_p, t:y_train_p}) acc_test_val =sess.run(accuracy, feed_dict={x:X_test_p, t:y_test_p}) print ('Step: %d, Loss: %f, Accuracy(train): %f, Accuracy(test): %f' % (i, loss_val, acc_val, acc_test_val)) X_train_p = X_train_pre[i:i+k,:] y_train_p = y_train_s[i:i+k,:] X_test_p = X_test_pre[i:i+k,:] y_test_p = y_test_s[i:i+k,:] |