第15回では、多項分類器(Multinomial Classifier)利用したTensorFlowによる機械学習の流れをご紹介しました。多項分類器とは、分類すべきラベルが3つ以上ある場合の機械学習アルゴリズムのモデルを言います。今回は、この多項分類器と隠れ層が1層からなるニューラルネットワークを使用して、0〜9までの手書き文字を分類してみたいと思います。ここで使用するデータはMNIST(Mixed National Institute of Standards an Technology)と呼ばれるデータで、60,000個のトレーニングデータと10,000個のテストデータからなります。トレーニングデータは、250名(半分は高校生、半分は国税調査局の職員)の手書き数字のデータからなります。また、テストデータについても、同じ割合で(トレーニングデータとは)別の人の手書きデータからなります。それぞれの手書き数字は、28x28ピクセルのグレースケールの画像データで、0〜255の整数値で各ピクセルの濃度が与えられています。図1および図2に手書き数字のサンプルを添付しました。特に図2からわかるように、同じ数字(ここでは「7」)でも、書く人によって、バラツキがあることがわかります。

 

図1

図2

TensorFlowによる多項分類器とニューラルネットワークを使用した機械学習の流れは、概ね以下になります。なお、Pythonのバージョンは3.5以上を想定しています。

  1. データを入力する。
  2. 変量(image)の正規化とラベル(0〜9)の変換(one-hot vector representation)
  3. TensorFlowにより隠れ層(hidden layer)の最適化すべきVariableとNet Input関数および活性化関数を定義する。
  4. TensorFlowにより、出力層(output layer)の最適化すべき係数(Variable)と活性化関数を定義する。
  5. TensorFlowにより誤差関数とその最適化法を定義する。
  6. TensorFlowにより精度を定義する。
  7. TensorFlowによりセッションを定義する。
  8. TensorFlowによりセッションを実行する。

では、各ステップを詳しく見ていきましょう。

 

①データを入力する。

まず、対話形式ではなく、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/

 

  1. train-images.idx3-ubyte.gz:  training set images (9912422 bytes) 
  2. train-labels.idx1-ubyte.gz:  training set labels (28881 bytes) 
  3. t10k-images.idx3-ubyte.gz:   test set images (1648877 bytes) 
  4. t10k-labels.idx1-ubyte.gz:   test set labels (4542 bytes)
ローカルに「mnist」 と言うディレクトリー(またはフォルダー)を作成して、ダウンロードしたファイルを展開します。MacOSやUbuntuの場合は、以下のコマンドで、Windowsの場合は、7-Zipを利用して展開してください。
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を多項分類器により分類できるように以下のように変換します。

"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により隠れ層(hidden layer)の最適化すべきVariableとNet Input関数および活性化関数を定義する。ここからが、TensorFlowの実際の記述になります。隠れ層について以下のように記述します。

import tensorflow as tf
num_units = 1024
x = tf.placeholder(tf.float32, [None, 784])
w1 = tf.Variable(tf.truncated_normal([784, num_units]))
b1 = tf.Variable(tf.zeros([num_units]))
hidden1 = tf.nn.relu(tf.matmul(x, w1) + b1)
            

num_unitsは隠れ層におけるノードの数です。ここでは1024を指定していますが、2以上の自然数を指定することにより、ノードの数を増減ことができます。ノードの数を増やすことにより、より複雑なモデルに対応することができますが、それに伴いより多くのコンピュータ資源が必要となります。TensorFlowでは、入力データを「placeholder」に格納し、機械学習により最適化すべき隠れ層の係数b1およびw1を「Variable」に格納します。ここでは、placeholderのデータの型と変量の要素数(ここでは784)のみを指定し、サンプルの数は指定しなくても構いません(ここではNoneを指定)。実際のデータ内容はセッション実行時に設定するため、ここでは指定しません。最適化すべき係数のb1の値は、0(ゼロ)に初期化しますが、w1は乱数を用いて初期値を決定しています。w1は784x1024の行列です。Net Input関数と活性化関数による隠れ層の出力としてhidden1を定義します。活性化関数としてReLU関数を使用しています。活性化関数の詳細については、第15回をご参照ください。ここで注意すべきことは、Net Input関数も活性化関数も、必ずtensorflowライブラリーのものを使用しなければならないということです。例えば、Pythonプログラミングとしては、tf.matmul(x,w1)もnp.dot(x,w1)も全く同じ機能で計算結果も同じですが、np.dot(x,w1)を使用するとTensorFlowのセッション実行時にエラーとなるので、注意が必要です。tf.tanh関数に関しても同様で、np.tanhと指定するとセッション実行時にエラーになります。

 

④TensorFlowにより出力層(output layer)の最適化すべきVariableとNet Input関数および活性化関数を定義する。

最適化すべき係数 w0 と b0を0(ゼロ)に初期化します。隠れ層からの出力であるhidden1の値を入力に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(w0,b0,w1,b1)の値を再調整するセッションを繰り返します。

 

w0 = tf.Variable(tf.zeros([num_units, 10]))
b0 = tf.Variable(tf.zeros([10]))
p = tf.nn.softmax(tf.matmul(hidden1, 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,w1,b1の値を最適化すれば良いことになります。これを最尤推定法(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とw)の値を最適化するための、セッションを定義して、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: 222.278015, Accuracy(train): 0.160000, Accuracy(test): 0.160000

Step: 200, Loss: 215.535645, Accuracy(train): 0.280000, Accuracy(test): 0.110000

Step: 300, Loss: 200.481674, Accuracy(train): 0.560000, Accuracy(test): 0.350000

Step: 400, Loss: 181.225098, Accuracy(train): 0.590000, Accuracy(test): 0.340000

Step: 500, Loss: 170.544556, Accuracy(train): 0.590000, Accuracy(test): 0.490000

Step: 600, Loss: 165.152069, Accuracy(train): 0.600000, Accuracy(test): 0.480000

Step: 700, Loss: 148.990692, Accuracy(train): 0.660000, Accuracy(test): 0.540000

Step: 800, Loss: 121.880615, Accuracy(train): 0.680000, Accuracy(test): 0.530000

Step: 900, Loss: 114.389893, Accuracy(train): 0.670000, Accuracy(test): 0.650000

Step: 1000, Loss: 109.636612, Accuracy(train): 0.730000, Accuracy(test): 0.550000

Step: 1100, Loss: 116.471519, Accuracy(train): 0.610000, Accuracy(test): 0.480000

Step: 1200, Loss: 99.801132, Accuracy(train): 0.750000, Accuracy(test): 0.590000

Step: 1300, Loss: 96.941315, Accuracy(train): 0.770000, Accuracy(test): 0.470000

Step: 1400, Loss: 83.745850, Accuracy(train): 0.810000, Accuracy(test): 0.630000

Step: 1500, Loss: 82.356216, Accuracy(train): 0.800000, Accuracy(test): 0.600000

Step: 1600, Loss: 81.826660, Accuracy(train): 0.820000, Accuracy(test): 0.680000

Step: 1700, Loss: 69.526031, Accuracy(train): 0.870000, Accuracy(test): 0.630000

Step: 1800, Loss: 54.219505, Accuracy(train): 0.930000, Accuracy(test): 0.660000

Step: 1900, Loss: 60.556213, Accuracy(train): 0.840000, Accuracy(test): 0.740000

Step: 2000, Loss: 58.091846, Accuracy(train): 0.860000, Accuracy(test): 0.780000

Step: 2100, Loss: 62.141102, Accuracy(train): 0.840000, Accuracy(test): 0.770000

Step: 2200, Loss: 43.971390, Accuracy(train): 0.920000, Accuracy(test): 0.720000

Step: 2300, Loss: 42.586784, Accuracy(train): 0.920000, Accuracy(test): 0.770000

Step: 2400, Loss: 48.520874, Accuracy(train): 0.920000, Accuracy(test): 0.760000

Step: 2500, Loss: 47.964420, Accuracy(train): 0.870000, Accuracy(test): 0.730000

Step: 2600, Loss: 38.560570, Accuracy(train): 0.920000, Accuracy(test): 0.720000

Step: 2700, Loss: 46.418434, Accuracy(train): 0.890000, Accuracy(test): 0.720000

Step: 2800, Loss: 43.748123, Accuracy(train): 0.900000, Accuracy(test): 0.760000

Step: 2900, Loss: 33.370796, Accuracy(train): 0.950000, Accuracy(test): 0.800000

Step: 3000, Loss: 40.551636, Accuracy(train): 0.890000, Accuracy(test): 0.860000

Step: 3100, Loss: 42.937920, Accuracy(train): 0.900000, Accuracy(test): 0.870000

Step: 3200, Loss: 34.777302, Accuracy(train): 0.950000, Accuracy(test): 0.770000

Step: 3300, Loss: 32.340805, Accuracy(train): 0.960000, Accuracy(test): 0.840000

Step: 3400, Loss: 36.606216, Accuracy(train): 0.930000, Accuracy(test): 0.740000

Step: 3500, Loss: 24.376015, Accuracy(train): 0.960000, Accuracy(test): 0.790000

Step: 3600, Loss: 32.601982, Accuracy(train): 0.920000, Accuracy(test): 0.780000

Step: 3700, Loss: 34.463394, Accuracy(train): 0.920000, Accuracy(test): 0.900000

Step: 3800, Loss: 33.503185, Accuracy(train): 0.920000, Accuracy(test): 0.720000

Step: 3900, Loss: 29.841797, Accuracy(train): 0.970000, Accuracy(test): 0.770000

Step: 4000, Loss: 24.120964, Accuracy(train): 0.980000, Accuracy(test): 0.840000

Step: 4100, Loss: 28.409174, Accuracy(train): 0.930000, Accuracy(test): 0.830000

Step: 4200, Loss: 32.229568, Accuracy(train): 0.920000, Accuracy(test): 0.860000

Step: 4300, Loss: 28.273163, Accuracy(train): 0.920000, Accuracy(test): 0.760000

Step: 4400, Loss: 29.786045, Accuracy(train): 0.950000, Accuracy(test): 0.820000

Step: 4500, Loss: 25.856886, Accuracy(train): 0.940000, Accuracy(test): 0.850000

Step: 4600, Loss: 17.984814, Accuracy(train): 0.980000, Accuracy(test): 0.840000

Step: 4700, Loss: 32.794159, Accuracy(train): 0.860000, Accuracy(test): 0.830000

Step: 4800, Loss: 22.865391, Accuracy(train): 0.970000, Accuracy(test): 0.910000

Step: 4900, Loss: 21.844021, Accuracy(train): 0.950000, Accuracy(test): 0.810000

Step: 5000, Loss: 30.265957, Accuracy(train): 0.960000, Accuracy(test): 0.850000

Step: 5100, Loss: 24.177357, Accuracy(train): 0.970000, Accuracy(test): 0.880000

Step: 5200, Loss: 37.196522, Accuracy(train): 0.890000, Accuracy(test): 0.910000

Step: 5300, Loss: 20.472775, Accuracy(train): 0.960000, Accuracy(test): 0.920000

Step: 5400, Loss: 33.149208, Accuracy(train): 0.910000, Accuracy(test): 0.960000

Step: 5500, Loss: 17.188869, Accuracy(train): 0.970000, Accuracy(test): 0.980000

Step: 5600, Loss: 20.802626, Accuracy(train): 0.950000, Accuracy(test): 0.880000

Step: 5700, Loss: 25.379189, Accuracy(train): 0.910000, Accuracy(test): 0.870000

Step: 5800, Loss: 22.102867, Accuracy(train): 0.950000, Accuracy(test): 0.910000

Step: 5900, Loss: 25.865175, Accuracy(train): 0.940000, Accuracy(test): 0.840000

Step: 6000, Loss: 17.530960, Accuracy(train): 0.970000, Accuracy(test): 0.800000

Step: 6100, Loss: 14.576836, Accuracy(train): 0.960000, Accuracy(test): 0.820000

Step: 6200, Loss: 16.858952, Accuracy(train): 0.980000, Accuracy(test): 0.910000

Step: 6300, Loss: 18.272453, Accuracy(train): 0.970000, Accuracy(test): 0.990000

Step: 6400, Loss: 13.462811, Accuracy(train): 0.990000, Accuracy(test): 0.930000

Step: 6500, Loss: 25.941442, Accuracy(train): 0.930000, Accuracy(test): 0.910000

Step: 6600, Loss: 10.924963, Accuracy(train): 0.980000, Accuracy(test): 0.860000

Step: 6700, Loss: 21.953243, Accuracy(train): 0.950000, Accuracy(test): 0.810000

Step: 6800, Loss: 17.301933, Accuracy(train): 0.960000, Accuracy(test): 0.850000

Step: 6900, Loss: 36.062916, Accuracy(train): 0.920000, Accuracy(test): 0.920000

Step: 7000, Loss: 24.770287, Accuracy(train): 0.950000, Accuracy(test): 0.910000

Step: 7100, Loss: 27.079540, Accuracy(train): 0.930000, Accuracy(test): 0.930000

Step: 7200, Loss: 24.845245, Accuracy(train): 0.960000, Accuracy(test): 0.960000

Step: 7300, Loss: 35.769440, Accuracy(train): 0.910000, Accuracy(test): 0.940000

Step: 7400, Loss: 27.176071, Accuracy(train): 0.940000, Accuracy(test): 0.940000

Step: 7500, Loss: 16.142792, Accuracy(train): 0.970000, Accuracy(test): 0.900000

Step: 7600, Loss: 18.699791, Accuracy(train): 0.960000, Accuracy(test): 0.900000

Step: 7700, Loss: 23.854576, Accuracy(train): 0.940000, Accuracy(test): 0.960000

Step: 7800, Loss: 21.877106, Accuracy(train): 0.920000, Accuracy(test): 0.960000

Step: 7900, Loss: 30.497105, Accuracy(train): 0.850000, Accuracy(test): 0.880000

Step: 8000, Loss: 22.167772, Accuracy(train): 0.910000, Accuracy(test): 0.920000

Step: 8100, Loss: 9.526794, Accuracy(train): 1.000000, Accuracy(test): 0.910000

Step: 8200, Loss: 21.740162, Accuracy(train): 0.960000, Accuracy(test): 0.950000

Step: 8300, Loss: 36.942444, Accuracy(train): 0.880000, Accuracy(test): 0.920000

Step: 8400, Loss: 13.114602, Accuracy(train): 1.000000, Accuracy(test): 0.960000

Step: 8500, Loss: 31.684788, Accuracy(train): 0.950000, Accuracy(test): 0.900000

Step: 8600, Loss: 14.413628, Accuracy(train): 0.960000, Accuracy(test): 0.940000

Step: 8700, Loss: 31.573174, Accuracy(train): 0.920000, Accuracy(test): 0.960000

Step: 8800, Loss: 46.181305, Accuracy(train): 0.870000, Accuracy(test): 1.000000

Step: 8900, Loss: 27.463667, Accuracy(train): 0.910000, Accuracy(test): 0.980000

 

誤差関数(Loss)の値が減少すると共に、トレーニングデータとテストデータの精度(Accuracy)の値が共に上昇していることがわかります。また、確率的勾配降下法を使用しているため、誤差関数の値と精度が上がったり、下がったりを繰り返しながら、徐々に最適解へと向かっている様子がわかります。

 

 

なお、今回の内容は、以下の書籍の2章および3章を参考に執筆いたしました。ご興味のある方は、ご参考いただければと思います。

 

「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
num_units = 1024
x = tf.placeholder(tf.float32, [None, 784])
w1 = tf.Variable(tf.truncated_normal([784, num_units]))
b1 = tf.Variable(tf.zeros([num_units]))
hidden1 = tf.nn.relu(tf.matmul(x, w1) + b1)

w0 = tf.Variable(tf.zeros([num_units, 10]))
b0 = tf.Variable(tf.zeros([10]))
p = tf.nn.softmax(tf.matmul(hidden1, 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,:]