【解説】MNISTデータセットで数字画像認識 | 基本的なCNNサンプルコード – Colaboratory・Keras・MNIST
Google ColaboratoryでKerasを使ってMNISTの数字画像認識用に、ディープラーニング(深層学習)でおなじみの「畳み込みニューラルネットワーク」(CNN:Convolutional Neural Network)を構築してみました。
* Keras:Pythonで書かれているTensorFlow上などで実行可能な高水準のニューラルネットワークライブラリ
このページでは、プログラムの解説をしておきます。
「 機械学習プログラミングについて勉強を始めたものの、コードが意味不明…」
と考えている初学者の方の参考になることがありましたら幸いです。
尚、内容に関しては正確に記載するように努めておりますが、個人で学習した範囲の情報ですので、正確な情報などの詳細に関してはご自身でもよくお調べください。
【変更情報:2021年10月13日】
「from keras.optimizers import Adam」「keras.utils.to_categorical」のところのエラーを修正しました。
【情報】
機械学習・人工知能プログラミングのオリジナルデータセットの壁を感じている方も多いのではないかと思います。
自作・自前画像で画像認識するためのサンプルコードを公開しました。日々の学習の参考になることがありましたら幸いです。
:【サンプルコード】Python・KerasでCNN機械学習。自作・自前画像のオリジナルデータセットで画像認識入門
:【コード解説】自作画像認識AI:Keras・CNN・Pythonオリジナルデータセット対応の機械学習サンプルコード
この記事のサンプルコードを理解するために必要な画像認識関連の深層学習プログラミングの知識を学ぶ上で補助教材となりそうな書籍のレビューや数学・数式関連の情報をまとめておきました。日々の学習の一助になることがありましたら幸いです。
:【1周目 – 学習レビュー】Pythonで動かして学ぶ!あたらしい深層学習の教科書 機械学習の基本から深層学習まで(Aidemy公式教科書)
:「Pythonで動かして学ぶ!あたらしい深層学習の教科書 機械学習の基本から深層学習まで」を読むためのギリシャ文字・数学記号
今回のプログラムをWebアプリ化する方法はこちら
:【サンプルコード】MNIST編 Flask(Python)Web機械学習アプリ開発入門:画像アップロード判定プログラム
日本語コード解説も公開しました。
:【コード解説】Keras・MNIST編:Flask(Python)Web機械学習アプリ開発入門 – 画像アップロード判定プログラム
オリジナルデータセット編のFlask(Python)Web機械学習アプリ開発サンプルコードの日本語解説も公開しました。
:【コード解説】自作画像認識編 Flask(Python)Web機械学習アプリ開発入門:画像アップロード判定プログラム
【コード全体の概略】人工知能(AI)プログラミング用サンプルコード・サンプルソース
実行環境 – Google Colaboratory
Google Colaboratory用のサンプルコードリンク
:program-Keras-cnn-basic.ipynb | Google Colaboratory共有リンク
【とりあえず動かすサンプルコード】CNN:畳み込みニューラルネットワーク(解説なし版:動画)
視聴時間:56秒
サンプルコードの動かし方がわからない方は、参考にしてみてください。
Google Colaboratoryでプログラムを実行する際には、Googleアカウントが必要です。GPUも無料で使えるのでありがたいですね。
***以下:サンプルコード***
#1 ライブラリのインポートなど
import keras
from keras.datasets import mnist
from keras.utils import np_utils #「keras.utils.to_categorical」のところでエラーとなるので追加
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Dense, Dropout, Flatten
#from keras.optimizers import Adam # ここでエラーとなるので以下のコードに変更
from tensorflow.keras.optimizers import Adam # 「tensorflow.」を追加
import time
#2 画像データの読み込みと、データ形式の設定・正規化
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train.reshape(x_train.shape[0], 28, 28, 1)
x_test = x_test.reshape(x_test.shape[0], 28, 28, 1)
x_train = x_train.astype('float32')/255
x_test = x_test.astype('float32')/255
#3 ラベルデータをOne-hotベクトルに変更
#y_train = keras.utils.to_categorical(y_train, 10) # エラーとなるのでコメントアウト
#y_test = keras.utils.to_categorical(y_test, 10) # エラーとなるのでコメントアウト
y_train = np_utils.to_categorical(y_train, 10) # 上記のコードでエラーとなるので変更
y_test = np_utils.to_categorical(y_test, 10) # 上記のコードでエラーとなるので変更
#4 機械学習(人工知能)モデルの作成
#「Conv2D」を使ってディープラーニング技術でおなじみの「畳み込みニューラルネットワーク」(CNN)を実装
model = Sequential()
model.add(Conv2D(16, (3, 3), padding='same',
input_shape=(28, 28, 1), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(128, (3, 3), activation='relu'))
model.add(Conv2D(256, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.5))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.25))
model.add(Dense(10, activation='softmax'))
#5 損失関数・最適化関数・評価関数などを指定してモデルをコンパイル
model.compile(loss='categorical_crossentropy',
optimizer=Adam(),
metrics=['accuracy'])
#6 処理を始めた時間
start_time = time.time()
#7 学習の実行
history = model.fit(x_train, y_train, batch_size=1024, epochs=20,
verbose=1, validation_data=(x_test, y_test))
#8 モデルの評価
score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])
print('Computation time:{0:.3f} sec'.format(time.time() - start_time))
***終わり:サンプルコード***
【コードの解説】人工知能(AI)プログラミング用サンプルコード・サンプルソース
#1 ライブラリのインポートなど
import keras
from keras.datasets import mnist
from keras.utils import np_utils #「keras.utils.to_categorical」のところでエラーとなるので追加
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Dense, Dropout, Flatten
#from keras.optimizers import Adam # ここでエラーとなるので以下のコードに変更
from tensorflow.keras.optimizers import Adam # 「tensorflow.」を追加
import time
まずは、今回の機械学習プログラムで必要なライブラリなどをインポートします。
Google ColaboratoryのJupyterノートブックの環境で #2〜#8 のコードを入力後にプログラムを実行してみてエラー表示がでたものを上記のように記載しました。現在書籍や、インターネット上に公開されているサンプルコードによっては、プラスアルファの内容の追記もあるのではないかと思います。
import keras
#コード解説
:kerasのインポート。
from keras.datasets import mnist
#コード解説
:mnistのデータセットのインポート。
from keras.utils import np_utils
#コード解説
:「keras.utils.to_categorical」のところでエラーとなるので追加。(2021年10月13日時点で追加)
from keras.models import Sequential
#コード解説
:Sequential – モデル層を積み重ねる。.addメソッドで簡単に層を追加。
from keras.layers import Conv2D, MaxPooling2D
#コード解説
:Conv2d – 2次元畳み込み層のモジュール。MaxPooling2D – 2次元最大プーリング層のモジュール。
from keras.layers import Dense, Dropout, Flatten
#コード解説
:Dense – 全結合層。Dropout – ドロップアウト。Flatten – 入力を平滑化(平坦化:次元削減)。
from keras.optimizers import Adam
#コード解説
:Adam(Adaptive moment estimation)- 学習の最適化手法の1つ。最適化関数。
2021年10月13日確認時点では、エラーとなるので以下のコードに変更
from tensorflow.keras.optimizers import Adam
「keras」の前に「tensorflow.」を追加しています。
import time
#コード解説
:timeモジュール。処理速度計測で利用。
#2 画像データの読み込みと、データ形式の設定・正規化
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train.reshape(x_train.shape[0], 28, 28, 1)
x_test = x_test.reshape(x_test.shape[0], 28, 28, 1)
x_train = x_train.astype('float32')/255
x_test = x_test.astype('float32')/255
(x_train, y_train), (x_test, y_test) = mnist.load_data()
#コード解説
:0~9の手書き文字MNISTのデータセットを読み込む。訓練用画像データ6万枚・テスト用画像データ1万枚。縦28×横28ピクセル・グレースケール(白黒画像)・各ピクセルは黒から白までの変化を0〜255で表現。
*補足:「28」の所はプログラムの記述順としては(width 横, height 縦)のようです。
学習データは多次元のため、行列を大文字で表記することが多い数学の慣習から、(X_train, y_train), (X_test, y_test)などと学習データを表記する際の「エックス」が大文字のサンプルコードを見かけることもあるのではないかと思います。今回は、学習データ・クラスラベル共に小文字を利用しています。
x_train = x_train.reshape(x_train.shape[0], 28, 28, 1)
#コード解説
:「縦28×横28ピクセル」の「グレースケール(白黒)」の「x_train.shape[0]:画像データが6万枚」。カラー画像の場合は(x_train.shape[0], 28, 28, 3)
その他に、書籍やチュートリアル等で
x_train = x_train.reshape(-1, 28, 28, 1)
となっているのを見かけることもあるのではないかと思います。調べられた範囲の情報では「-1」を指定すると、適当な値を推測して調整してくれるようです。
x_test = x_test.reshape(x_test.shape[0], 28, 28, 1)
#コード解説
:「縦28×横28ピクセル」の「グレースケール(白黒)」の「x_test.shape[0]:画像データが1万枚」。カラー画像の場合は(x_test.shape[0], 28, 28, 3)
その他に、書籍やチュートリアル等で
x_test = x_test.reshape(-1, 28, 28, 1)
となっているのを見かけることもあるのではないかと思います。調べられた範囲の情報では「-1」を指定すると、適当な値を推測して調整してくれるようです。
x_train = x_train.astype(‘float32’)/255
#コード解説
:訓練用の画像データを正規化。黒から白までの変化を0〜255で表現しているので、255で割ることでデータを0〜1の範囲に正規化。float型にすることで実数値で表現。
正規化前「0〜255」 正規化実施「0.0〜1.0」(実数値)
x_test = x_test.astype(‘float32’)/255
#コード解説
:テスト用の画像データを正規化。黒から白までの変化を0〜255で表現しているので、255で割ることでデータを0〜1の範囲に正規化。float型にすることで実数値で表現。
正規化前「0〜255」 正規化実施「0.0〜1.0」(実数値)
#3 ラベルデータをOne-hotベクトルに変更
#y_train = keras.utils.to_categorical(y_train, 10) # エラーとなるのでコメントアウト
#y_test = keras.utils.to_categorical(y_test, 10) # エラーとなるのでコメントアウト
y_train = np_utils.to_categorical(y_train, 10) # 上記のコードでエラーとなるので変更
y_test = np_utils.to_categorical(y_test, 10) # 上記のコードでエラーとなるので変更
y_train = keras.utils.to_categorical(y_train, 10)
#コード解説
:訓練用のラベルデータの整数値を2値クラスの行列に変換(例「整数 1」を「0,1,0,0,0,0,0,0,0,0」と表現。One-hotベクトル(1-of-k表記法))。
2021年10月13日確認時点では、エラーとなるので、
「y_train = np_utils.to_categorical(y_train, 10)」
にコードを変更。
(y_train, 10)
:0〜9までのため10クラス。
このような形式にすることで、出力結果が「0.1,0,0,0,0,0,0.8,0,0.1,0」(0の確率10%。6の確率80%。10の確率10%)を得られる。
画像データが機械学習モデルで、どういう判定結果となっているか?という情報を取得できる。
y_test = keras.utils.to_categorical(y_test, 10)
#コード解説
:テスト用のラベルデータの整数値を2値クラスの行列に変換(例「整数 1」を「0,1,0,0,0,0,0,0,0,0」と表現。One-hotベクトル(1-of-k表記法))。
2021年10月13日確認時点では、エラーとなるので、
「y_test = np_utils.to_categorical(y_test, 10) 」
にコードを変更。
(y_test, 10)
:0〜9までのため10クラス。
このような形式にすることで、出力結果が「0.2,0,0,0,0,0,0.8,0,0,0.1」(0の確率20%。6の確率80%。9の確率10%)を得られる。
画像データが機械学習モデルで、どういう判定結果となっているか?という情報を取得できる。
#4 機械学習(人工知能)モデルの作成 – 畳み込みニューラルネットワーク(CNN)
model = Sequential()
model.add(Conv2D(16, (3, 3), padding='same',
input_shape=(28, 28, 1), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(128, (3, 3), activation='relu'))
model.add(Conv2D(256, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.5))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.25))
model.add(Dense(10, activation='softmax'))
「Conv2D」を使ってディープラーニング技術でおなじみの「畳み込みニューラルネットワーク」(CNN)を実装しています。
model = Sequential()
#コード解説
:Sequential – モデル層を積み重ねる形式の記述方法。.addメソッドで簡単に層を追加できます。
model.add(Conv2D(16, (3, 3), padding=’same’,
input_shape=(28, 28, 1), activation=’relu’))
#コード解説
:空間フィルタ – 畳み込み演算層。
Conv2D(16, (3, 3)の解説
:「3×3」の大きさのフィルタを16枚使うという意味です。「5×5」「7×7」などと、中心を決められる奇数が使いやすいようです。
フィルタ数は、「16・32・64・128・256・512枚」などが使われる傾向にあるようですが、複雑そうな問題ならフィルタ数を多めに、簡単そうな問題ならフィルタ数を少なめで試してみるようです。
padding=’same’の解説
:今回は出力画像のサイズが変わらないように「padding=’same’」でパディングを実施。フィルタを適用前に0などの要素で周囲を増やすようです。(ゼロパディング)
その他にも「stride=(1,1)」(横に1ピクセル・縦に1ピクセルずつフィルタを適用)などとストライドの追加も可能。strideを指定しない場合(デフォルト)は縦・横1ピクセルずつフィルタが適用されるようです。
activation=’relu’の解説
:活性化関数「ReLU(Rectified Linear Unit)- ランプ関数」。フィルタ後の画像に実施。入力が0以下の時は出力0。入力が0より大きい場合はそのまま出力する。
model.add(MaxPooling2D(pool_size=(2, 2)))
#コード解説
:「2×2」の大きさの最大プーリング層。入力画像内の「2×2」の領域で最大の数値を出力する。
model.add(Conv2D(128, (3, 3), activation=’relu’))
#コード解説
:空間フィルタ – 畳み込み演算層。「3×3」の大きさのフィルタを128枚使う。
活性化関数「ReLU(Rectified Linear Unit)- ランプ関数」。フィルタ後の画像に実施。入力が0以下の時は出力0。入力が0より大きい場合はそのまま出力する。
model.add(Conv2D(256, (3, 3), activation=’relu’))
#コード解説
:空間フィルタ – 畳み込み演算層。「3×3」の大きさのフィルタを256枚使う。
活性化関数「ReLU(Rectified Linear Unit)- ランプ関数」。フィルタ後の画像に実施。入力が0以下の時は出力0。入力が0より大きい場合はそのまま出力する。
model.add(MaxPooling2D(pool_size=(2, 2)))
#コード解説:
:「2×2」の大きさの最大プーリング層。入力画像内の「2×2」の領域で最大の数値を出力する。
model.add(Dropout(0.5))
#コード解説
:ドロップアウト – 過学習予防。今回は、全結合の層とのつながりを「50%」無効化しています。
model.add(Flatten())
#コード解説
:平坦化(次元削減) – 1次元ベクトルに変換する。
model.add(Dense(128, activation=’relu’))
#コード解説
:全結合層。出力128。
活性化関数「ReLU(Rectified Linear Unit)- ランプ関数」。入力が0以下の時は出力0。入力が0より大きい場合はそのまま出力する。
model.add(Dropout(0.25))
#コード解説
:ドロップアウト – 過学習予防。全結合の層とのつながりを「25%」無効化しています。
model.add(Dense(10, activation=’softmax’))
#コード解説
:全結合層。出力10(0~9の10クラス判定のため)
Denseのところで、畳み込みニューラルネットワーク(CNN)の最終的な全結合層の出力次元数の決め方は、判定するクラス数を指定します。上記のコードの場合「0〜9」までの数字を判定するため、全部で「0・1・2・3・4・5・6・7・8・9」の「10」クラスになります。そのため、最終的な全結合層の出力次元数を「10」にします。
Softmax関数で総和が1となるように、各出力の予測確率を計算。例「0.1,0,0,0,0,0,0.8,0,0,0.1」(0の確率10%。6の確率80%。9の確率10%)
#5 損失関数・最適化関数・評価関数などを指定してモデルをコンパイル
model.compile(loss='categorical_crossentropy',
optimizer=Adam(),
metrics=['accuracy'])
#コード解説
:モデルをコンパイル(コンピュータが実行可能な形式に変換)します。
loss=’categorical_crossentropy’
:損失化関数 – 交差エントロピー誤差を指定しています。今回のような分類問題(識別問題)などで使われるようです。
optimizer=Adam()
:最適化関数 – Adam(Adaptive moment estimation)を指定しています。学習の最適化手法の1つのようです。
metrics=[‘accuracy’]
:評価関数 – 訓練時とテスト時にモデルにより評価される評価関数。学習の評価として正解率も計算します。
#6 処理を始めた時間
start_time = time.time()
#コード解説
:今回は、計算にかかった時間のスピード計測もするため処理を始めた時間を記録(取得)します。
#7 学習の実行
history = model.fit(x_train, y_train, batch_size=1024, epochs=20,
verbose=1, validation_data=(x_test, y_test))
history = model.fit(x_train, y_train, batch_size=1024, epochs=20,
verbose=1, validation_data=(x_test, y_test))
#コード解説
:訓練データで学習を実行します。
x_train, y_train
:訓練データ(画像とラベルデータ)を使用。
batch_size=1024
:バッチサイズは機械学習分野の慣習として、「2のn乗」(32, 64, 128, 256, 512, 1024, 2048)が使われることが多いようです。
公開されているサンプルコードによっては「1000」などのきりの良い数字を使っている場合もあるのではないかと思います。
バッチサイズのように、サブセットに分けて学習する理由として、学習する際の異常値の影響を小さくするためのようです。
epochs=20
:学習する回数を指定します。
学習回数を増やすと計算するのに時間がかかります。Google Colaboratoryではインターネット接続環境で無料で学習を実行できるので、回数を調整する際もありがたいです。
「Early Stopping」という手法もあるようですが、個人で情報収集してみた範囲では、学習回数に関して最終的には試行錯誤のようです。
verbose
:ログ出力の指定。「0」だとログが出ないの設定。
validation_data=(x_test, y_test)
:評価用データの指定。
(x_test, y_test)でテスト用データを指定しています。
機械学習におけるデータの種類に関しては、
・訓練データ(test data)
:機械学習のモデルを学習(調整)するために利用。
・検証データ(validation data)
:学習に使用していないデータで学習結果を評価。
・テストデータ(test data)
:機械学習モデルの学習が完了後に、訓練データ・検証データに使用していないテストデータを使用し、最終的な精度・予測結果を評価。
などがあるようですが、今回のプログラムでは検証データ(validation data)とテストデータ(test data)を同じものとして扱っています。厳密には、検証データ(validation data)への過学習予防のために、検証データ(validation data)・テストデータ(test data)を分けた方が良いようです。
#8 モデルの評価
score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])
print("Computation time:{0:.3f} sec".format(time.time() - start_time))
score = model.evaluate(x_test, y_test, verbose=0)
#コード解説
:テスト用データ画像・ラベルで評価。
verbose
:ログ出力の指定。「0」だとログが出ないの設定。
print(‘Test loss:’, score[0])
#コード解説
:score[0] – テストデータの交差エントロピー誤差を表示します。
print(‘Test accuracy:’, score[1])
#コード解説
:score[1] – テストデータの正解率を表示します。
print(‘Computation time:{0:.3f} sec’.format(time.time() – start_time))
#コード解説
:計算処理にかかった時間を表示します。
{0:.3f}
:{通し番号:float型の小数点以下3桁まで表示}
(time.time() – start_time)
:結果出力時の時間 – 学習スタート時の時間
お疲れ様でした。
以上、簡単ではありますがコードの解説です。
ある程度、畳み込みニューラルネットワークや機械学習の各種手順について学習されている方であれば、おおよそのイメージはつくのではないかと思います。
全く畳み込みニューラルネットワークのことについてわからない方の場合、チンプンカンプンなことだらけではないかと思います。そういった方は
・このページのコード解説に出てくる単語について少しずつ調べてみる
・畳み込みニューラルネットワークの理論的な内容を調べてみる(入門書やYouTubeの入門動画・ウェブサイトでディープラーニングについて解説しているものなど)
を繰り返すことで、今回のコードで実装している大まかな意味が理解できるのではないかと思います。
印刷用PDF:【コード解説】畳み込みニューラルネットワーク(CNN)サンプルコード – Colaboratory・Keras・MNIST
学習の利便性を考え、印刷しやすいようにPDFも作成しておきました。
プリンタの印刷設定で、ブックレットなどの冊子風に印刷すると読みやすいのではないかと思います。
初版:2019年10月15日(最終更新:2020年3月17日)
* 2021年10月13日のコード変更は、ダウンロード資料に反映されていません。以下の変更点は、ご自身で追記をよろしくお願いいたします。
#1 ライブラリのインポートなど
from keras.utils import np_utils #「keras.utils.to_categorical」のところでエラーとなるので追加
#from keras.optimizers import Adam # ここでエラーとなるので以下のコードに変更
from tensorflow.keras.optimizers import Adam # 「tensorflow.」を追加
#3 ラベルデータをOne-hotベクトルに変更
#y_train = keras.utils.to_categorical(y_train, 10) # エラーとなるのでコメントアウト
#y_test = keras.utils.to_categorical(y_test, 10) # エラーとなるのでコメントアウト
y_train = np_utils.to_categorical(y_train, 10) # 上記のコードでエラーとなるので変更
y_test = np_utils.to_categorical(y_test, 10) # 上記のコードでエラーとなるので変更
Webアプリ化に挑戦
:【コード解説】Keras・MNIST編:Flask(Python)Web機械学習アプリ開発入門 – 画像アップロード判定プログラム
:【コード解説】自作画像認識編 Flask(Python)Web機械学習アプリ開発入門:画像アップロード判定プログラム