LoginSignup
9
3

More than 3 years have passed since last update.

【AI初心者向け】mnist_mlp.pyを1行ずつ解説していく(KerasでMNISTを学習させる)

Last updated at Posted at 2019-11-30

はじめに

皆さんはじめまして。
この記事はmnist_mlp.pyを1行ずつ解説していくだけの記事です。
AIに興味があるけどまだ触ったことはない人などが対象です。これを読めばディープラーニングの基本的な学習の流れが理解できるはず、と思って書いていきます。(もともとは社内で研修用に使おうと思って作成していた内容です)

全3回予定です。
1. 【AI初心者向け】mnist_mlp.pyを1行ずつ解説していく(KerasでMNISTを学習させる)
2. 【AI初心者向け】mnist_cnn.pyを1行ずつ解説していく(KerasでMNISTを学習させる)
3. 【AI初心者向け】mnist_transfer_cnn.pyを1行ずつ解説していく(KerasでMNISTを学習させる)

動作確認方法について

MNISTは画像なので、このコードを動かすにはGPUがあったほうがいいです(CPUだとちょっと辛いです)。
おすすめの方法はGoogle Colaboratoryを使う方法です。
colab.gif
やることは2つだけ。
・Python3の新しいノートブックを開く
・ランタイムからGPUを有効にする
これでGPUが使えるようになりました。
セルにコードを貼り付けて実行(ショートカットはCTRL+ENTER)するだけで動きます。

mnistについて

手書き文字画像のデータセットで、機械学習のチュートリアルでよく使用されます。
内容:0~9の手書き文字
画像サイズ:28pix*28pix
カラー:白黒
データサイズ:7万枚(訓練データ6万、テストデータ1万の画像とラベルが用意されています)

mlpとは

Multilayer perceptron、多層パーセプトロンのことです。
mnistは画像データですが、画像データの形を(28, 28)から(784,)に変更することでmlpとして学習させることができます。(精度は第2回でやるCNNのほうが上です。)

mnist_mlp.pyについて

mnistの手書き文字の判定を行うモデルをKerasとTensorFlowを使って作成するコードです。
0~9の10種類の手書き文字を入力として受け取り、0~9のいずれであるか10種類に分類するモデルを作成します。

コードの解説

準備

'''Trains a simple deep NN on the MNIST dataset.
Gets to 98.40% test accuracy after 20 epochs
(there is *a lot* of margin for parameter tuning).
2 seconds per epoch on a K520 GPU.
'''

# 特に必要ないコードです(Pythonのバージョンが3だが、コードがPython2で書かれている場合に必要になる)
from __future__ import print_function

# 必要なライブラリをインポートしていく
import keras
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout
from keras.optimizers import RMSprop

# 定数を最初にまとめて指定する
batch_size = 128  # バッチサイズ。1度に学習するデータサイズ
num_classes = 10  # 分類するラベル数。今回は手書き画像を0~9の10種類に分類する
epochs = 20       # エポック数。全データを何回学習するか

データの前処理

# mnistのデータを読み込み、訓練データ(6万件)とテストデータ(1万件)に分割する
(x_train, y_train), (x_test, y_test) = mnist.load_data()

'''mlpでインプットデータとして使用できるようにするため、reshapeしてデータの形式を合わせる
x_train:(60000, 28, 28) ->(60000, 784) 28pix*28pixの画像を1列にする
x_test:(10000, 28, 28) ->(10000, 784) 28pix*28pixの画像を1列にする'''
x_train = x_train.reshape(60000, 784)
x_test = x_test.reshape(10000, 784)

# 画像データは0~255の値をとるので255で割ることでデータを標準化する
# .astype('float32')でデータ型を変換する。(しないと割ったときにエラーが出るはず)
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255

# データの数を出力して確認
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')

# ラベルデータをone-hot-vector化する
'''one-hot-vectorのイメージはこんな感じ
label  0 1 2 3 4 5 6 7 8 9
0:    [1,0,0,0,0,0,0,0,0,0]
8:    [0,0,0,0,0,0,0,0,1,0]'''
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)

標準化について:画像の各ピクセルの値は0~255になっています。これを0~1に変換するイメージです。画像で機械学習するときは大体この255で割る処理をして、値を標準化します。

one-hot-vectorについて:今回、ラベルは0~9の10種類があり、それぞれ0~9の数字で表しています。しかし、10種類に分類したいだけなので、ラベルの数字自体には意味がありません。そこで、one-hot-vectorすることにより0と1のみでどのラベルなのかを表せるように変換します。

モデルの定義

# Sequentialクラスをインスタンス化
model = Sequential()

# 中間層
# 全結合層(512ユニット、活性化関数:Relu、受け取る入力サイズ:784)を追加
model.add(Dense(512, activation='relu', input_shape=(784,)))  
# 0.2の確率でドロップアウト
model.add(Dropout(0.2))
# 全結合層(512ユニット、活性化関数:Relu、受け取る入力サイズは自動で判断)を追加
model.add(Dense(512, activation='relu'))
# 0.2の確率でドロップアウト
model.add(Dropout(0.2))

# 出力層
# 全結合層(10ユニット、活性化関数:SoftMax、受け取る入力サイズは自動で判断)を追加
model.add(Dense(num_classes, activation='softmax'))

# モデルの構造を可視化
model.summary()

SequentialモデルはDNNの層を積み重ねて作るモデルです。一番最初の層にだけ、input_shapeを指定してやる必要があります。
出力層の活性化関数は、今回は多値分類するモデルなのでsoftmaxを使います。

学習

# 学習プロセスを設定する
model.compile(
              # 損失関数を設定。今回は分類なのでcategorical_crossentropy
              loss='categorical_crossentropy',
              # 最適化アルゴリズムを指定。学習率などをいじれる
              optimizer=RMSprop(),
              # 評価関数を指定
              metrics=['accuracy'])

# 学習させる
history = model.fit(
                    # 学習データ、ラベル
                    x_train, y_train, 
                    # バッチサイズ(128)
                    batch_size=batch_size,
                    # エポック数(20)
                    epochs=epochs,
                    # 学習の進捗をリアルタムに棒グラフで表示(0で非表示)
                    verbose=1,
                    # テストデータ(エポックごとにテストを行い誤差を計算するため)
                    validation_data=(x_test, y_test))

モデルの定義が終わったら、損失関数や最適化アルゴリズムを指定してコンパイルします。その後、モデルにデータを渡して学習させます。より良いモデルを作るためには、最適化アルゴリズムやバッチサイズ、エポック数などをいろいろ変更して試してやる必要があります。

評価

# テストデータを渡す(verbose=0で進行状況メッセージを出さない)
score = model.evaluate(x_test, y_test, verbose=0)
# 汎化誤差を出力
print('Test loss:', score[0])
# 汎化性能を出力
print('Test accuracy:', score[1])

学習が終わったら、テストデータを使ってどの程度の性能になったのかを評価します。lossが低く、accuracyが高いほど良いモデルといえます。

9
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
3