プログラミング素人のはてなブログ

プログラミングも電気回路も専門外の技術屋の末端が勉強したことや作品をアウトプットするブログ。コードに間違いなど見つけられたら、気軽にコメントください。 C#、Python3、ラズパイなど。

TensorFlowとKerasでMnistをやってみた

いよいよMnistをやってみました。
Mnistは「エムニスト」と読むそうです。

TensorflowとKerasの環境構築は以前の記事を参照してください。

こちらの記事を参考(というよりほとんどコピペ)に学習と評価をfunctionに分離するようにしました。
脳死で覚えるkeras入門

ところでMnistってどんなテストなの?

有名なのはみんな知ってる。
でも、どんなデータなの?
画像認識なんだから画像データ集なんだよね???

と思って、HPを見に行くと…
MNIST handwritten digit database, Yann LeCun, Corinna Cortes and Chris Burges

画像ではなく数値のデータが入ったファイルになっています。
↓のようなpythonプログラムを作ってそのデータの中身を見てみました。
変数numberは適当な数字を入れます。
これによって、テストデータのnumber-1番目のデータ(リストなので0始まり)を確認してみました。

from keras.datasets import mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()  
   # 最初に実行するときダウンロードされる、これ以降はダウンロードしたデータを読み込む
data = x_test[number]  # テストデータをセット
print(data)
Anser =ytest[number]
print(Anser)

numpyのリストは適当なところで改行されてしまいますが手動で編集して、Excelにはりつけ、スペース区切りでセルに配置し条件付き書式をセットします。[]は手動削除。

テキストエディタで余計な改行を削除したところ。
f:id:s51517765:20180602172409p:plain
勘のいい人ならこの時点でわかりますね。

number=7 ,Answer=9
f:id:s51517765:20180602171356j:plain
number=100 ,Answer=6
f:id:s51517765:20180602171618j:plain
number=23 , Answer=5
f:id:s51517765:20180602171657j:plain

つまり、画素が0~255のモノクロの画像の画素データであることが分かります。
これで、すっきりしました。
ちなみに、Mnistのデータはテスト用データとトレーニング用(学習用)データに丁寧にも分けてあるようです。
そして、xが画素データでyが正解ラベルとなっています。

早速学習させてみる

先のQiitaの記事から学習部分を抜き出して関数化しました。
まず、二次元のリストである画像データをnumpyでreshapeします。
これでトレーニングデータは784の要素を持った1次元のリストになり、これが60000個できます。
正解ラベルは、正解がそのままの数字で入っているのですが、これをone-hot-encodingと呼ぶようですが、変換します。
One hot表現を実装してみた
例えば、「3」というデータは、[0, 0, 0, 1, 0, 0, 0, 0, 0, 0]という形になります。(0始まり)

先のQiitaの記事にはなかったのが、一度学習させた結果を

 model.save_weights("mnist.hdf5") #学習結果を保存

というように保存しておくことができます。

from keras.datasets import mnist
from keras.utils.np_utils import to_categorical
from keras.models import Sequential
from keras.layers import Dense

def model_build():
    (x_train, y_train), (x_test, y_test) = mnist.load_data()  
    # 最初に実行するときダウンロードされる
    # 画像を1次元化
    x_train = x_train.reshape(60000, 784)
    x_test = x_test.reshape(10000, 784)

    # 画素を0~1の範囲に変換(正規化)
    x_train = x_train.astype('float32') / 255
    x_test = x_test.astype('float32') / 255

    # 正解ラベルをone-hot-encoding
    y_train = to_categorical(y_train, 10)
    y_test = to_categorical(y_test, 10)

    model = Sequential()
    model.add(Dense(64, activation='relu', input_dim=784))
    model.add(Dense(10, activation='softmax'))

    model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])

    model.fit(x_train, y_train, batch_size=100, epochs=6, verbose=1)  # データを学習
    model.save_weights("mnist.hdf5") #学習結果を保存

    score = model.evaluate(x_test, y_test) #モデル評価結果
    print(score[0])
    print(score[1])

予測を動かす

テスト用のデータのなかから適当なサンプルを選び、これを学習させた時と同様に1次元のリストに変換して、モデルに予測をさせます。
予測結果もone-hot-encodingで帰ってくるので、これをラベルに変換します。
学習結果を保存しておいて、これを読み込むようにすることで予測だけを素早く動かすことができます。
また、今後もっと複雑なNNを使うときには当然、学習結果を使いまわせるようにしておくほうがよいし、例えばWindows PCで学習させてラズパイで予測を動かす、といった使い方もできるはずです。

def model_read():
  # モデルを読み込む
  model = Sequential()
  model.add(Dense(64, activation='relu', input_dim=784))
  model.add(Dense(10, activation='softmax'))
  model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])
  # 学習済みモデルの重みのデータを読み込み
  model.load_weights('mnist.hdf5') 
  return model

def check(number):
    (x_train, y_train), (x_test, y_test) = mnist.load_data()  #mnistデータを読み込む
    model = model_read()
    data = x_test[number]  # テストデータをセット
    print(data)
    data = x_test[number].reshape(1, 784)  # 1次元化
    # 判定
    res = model.predict([data])
    print("res = ", res)
    y = res.argmax()  # 値の中で最も値が大きいものが答え
    print(y)
    rabel = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    print("Prediction is ", rabel[y])
    print("Anser is ", y_test[number])

if __name__ == "__main__":
    #model_build() #学習させるとき
    check(134) # テストデータの中から適当に選ぶ

まとめ

学習量もQiita引用より少なくepochs=6 ですが手動でいくつか動かしてみたところ、外れることはありませんでした。
モデル評価結果も96%と出ているので、10数個試さないと外れないぐらいの精度になっているようです。

参考