対象読者
AIの概要は学んだけど何から作ればいいんだろう。という悩みをお持ちではありませんか?
この記事はそんな悩みにピッタリなものになっています。AI初心者でも簡単に形あるものを作ることを目標にしています。
前回はMNISTを使って簡単に正解率を導出しました。今回やることは、そこから少しだけレベルアップしてCNN(畳み込みニューラルネットワーク)を構築していきましょう。前回に引き続き、AI実装初心者にとても適した内容となっています。
今回も気楽にやっていきましょう!
今日のゴール
CNNをモデルに追加して、よりディープラーニングらしくしていきましょう。そもそもCNNとは全結合層の欠点を補える技術です。
全結合層
・空間的な構造や局所的なパターンを考慮できない→画像分析には不向き
・その代わり、言語処理や時系列データなどの系列データのパターン分析に向いている
CNN
・位置的な情報を保持したまま学習を進められる→画像分析に向く
また、今回もこちらのサイト(https://qiita.com/Ka-k/items/deb59b20f3e622028455)を参考に進めさせてもらいます。こちらはAI初心者にはとても参考になるサイトですが、少し古い部分があったりします。なので、本ページでは今風にアレンジしつつ誰でも実装できるように解説していきます。
今日使うコード
これが今日の全コードです。理解のために、コードと説明の順番が上下するので、もし迷ったらこちらを参考にしてください。
from keras.models import Sequential
from keras.layers import Dense, Activation, Flatten
from keras.layers import Conv2D,Reshape, MaxPooling2D,Dropout
from keras.datasets import mnist
from keras.utils import to_categorical
import numpy as np
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = np.array(X_train)/255
X_test = np.array(X_test)/255
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
model = Sequential()
model.add(Reshape((28,28,1), input_shape=(28,28)))
model.add(Conv2D(32,(3,3)))
model.add(Activation("relu"))
model.add(Conv2D(32,(3,3)))
model.add(Activation("relu"))
model.add(MaxPooling2D((2,2)))
model.add(Dropout(0.5))
model.add(Conv2D(16,(3,3)))
model.add(Activation("relu"))
model.add(MaxPooling2D((2,2)))
model.add(Dropout(0.5))
model.add(Flatten())
model.add(Dense(784))
model.add(Activation("relu"))
model.add(Dropout(0.5))
model.add(Dense(10))
model.add(Activation("softmax"))
model.compile(loss="categorical_crossentropy", optimizer="sgd", metrics=["accuracy"])
hist = model.fit(X_train, y_train, batch_size=200, verbose=1,
epochs=15, validation_split=0.1)
score = model.evaluate(X_test, y_test, verbose=1)
model.save("./ai-first.h5")
基本モデルの構築
まずは、全結合層のみでニューラルネットワークモデルを構築しましょう。
from keras.models import Sequential
from keras.layers import Dense, Activation
model = Sequential()
model.add(Dense(784))
model.add(Activation("relu"))
model.add(Dropout(0.5))
model.add(Dense(10))
model.add(Activation("softmax"))
from keras.models import Sequential
#KerasのSequential
モデルクラスをインポートします。Sequential
モデルは、レイヤーを順番に重ねてモデルを定義するためのものです。
from keras.layers import Dense, Activation
#Dense層とActivation層を含む必要なモジュールをインポートします。Dense層は、全結合層を表し、Activation層は活性化関数を適用するために使用されます。
from keras.layers import Dropout
#過学習を防ぐために使用される正則化手法の1つであるDropout
をインポートしています。
model = Sequential()
#Sequentialモデルのインスタンスを作成します。これは、ニューラルネットワークモデルを構築するための土台となるものです。
model.add(Dense(784))
#全結合層(Dense層)を追加します。この層は784個(28×28)のユニット(ニューロン)を持ち、画像データの特徴を抽出し、平坦化します。
model.add(Activation(“relu”))
#ReLU(Rectified Linear Unit)活性化関数を適用します。これにより、非線形性が導入されます。
model.add(Dropout(0.5))
#Dropout率が0.5であるDropoutレイヤーを追加しています。ここでのDropout率0.5は、ランダムに選択された50%のニューロンを無効にし、それ以外のニューロンを活性化させることを意味します。
model.add(Dense(10))
#出力層として全結合層を追加します。この層は、モデルが10クラスの分類を行うことを示します。
model.add(Activation(“softmax”))
#最後に、ソフトマックス活性化関数を適用します。これにより、出力は各クラスに属する確率として解釈されます。softmax関数は、クラス分類問題において一般的に使用されます。
畳み込み層を追加
では、実際に畳み込み層を追加していきましょう。コード量が多く感じるかもしれませんが、実質は同じメソッドを使いまわしているだけなので、恐れることはありません。
p.s.コメント部分は既に書いたコードを表しています。
#from keras.models import Sequential
#from keras.layers import Dense, Activation
#from keras.layers import Dropout
from keras.layers import Conv2D,MaxPooling2D
#model = Sequential()
model.add(Conv2D(32,(3,3)))
model.add(Activation("relu"))
model.add(Conv2D(32,(3,3)))
model.add(Activation("relu"))
model.add(MaxPooling2D((2,2)))
model.add(Dropout(0.5))
model.add(Conv2D(16,(3,3)))
model.add(Activation("relu"))
model.add(MaxPooling2D((2,2)))
model.add(Dropout(0.5))
#model.add(Dense(784))
#model.add(Activation("relu"))
#model.add(Dropout(0.5))
#model.add(Dense(10))
#model.add(Activation("softmax"))
from keras.layers import Conv2D
#2次元畳み込み層(Convolutional Layer)を使用するためのモジュールをインポートします。
from keras.layers import MaxPooling2D
#畳み込みニューラルネットワーク(CNN)内で使用されるプーリング層の1つであるMaxPooling2Dをインポートしています。
model.add(Conv2D(32,(3,3)))
#32個の3×3のフィルタを持つ畳み込み層を追加しています。この層は、画像データの特徴を抽出します。model.add(Activation("relu"))
#ReLU(Rectified Linear Unit)活性化関数を適用します。これにより、非線形性が導入されます。model.add(Conv2D(32,(3,3)))
#32個の3×3のフィルタを持つ畳み込み層を追加しています。この層も、画像データの特徴を抽出します。model.add(Activation("relu"))
#再びReLU活性化関数を適用します。model.add(MaxPooling2D((2,2)))
#MaxPooling2D層を追加します。2×2のウィンドウ内の最大値を取得し、画像を圧縮します。これにより、特徴マップのサイズが削減されます。model.add(Dropout(0.5))
#Dropoutレイヤーを追加します。50%のドロップアウト率で、ランダムに一部のニューロンを無効にします。過学習を防止するために使用されます。
以上のプロセスを繰り返し、さらに畳み込み層とプーリング層、Dropoutレイヤーを追加しています。3つ目の畳み込み層は16個の3×3のフィルタを持ち、その後にReLU活性化関数が適用され、MaxPooling2Dで画像が圧縮され、最後にDropoutが適用されます。
Conv2Dのルール
Conv2Dを使用するためにはいくつかのルールがあります。そのルールに従わないと学習を進めることができません。そのため、ここでは上のコードをConv2Dに合うように必要なコードを追加していきます。
Conv2D入力編
Conv2D()
の入力は3次元(width,height,channel)でなけらばなりません。しかし、今回入力とするMNISTはチャンネルを持たない2次元です。なので、この入力を3次元に変換していきましょう。
# from keras.models import Sequential
# from keras.layers import Dense, Activation
# from keras.layers import Conv2D,MaxPooling2D,Dropout
from keras.layers import Reshape
#model = Sequential()
model.add(Reshape((28,28,1), input_shape=(28,28)))
# model.add(Conv2D(32,(3,3)))
# model.add(Activation("relu"))
# model.add(Conv2D(32,(3,3)))
# model.add(Activation("relu"))
# model.add(MaxPooling2D((2,2)))
# model.add(Dropout(0.5))
# model.add(Conv2D(16,(3,3)))
# model.add(Activation("relu"))
# model.add(MaxPooling2D((2,2)))
# model.add(Dropout(0.5))
# model.add(Dense(784))
# model.add(Activation("relu"))
# model.add(Dropout(0.5))
# model.add(Dense(10))
# model.add(Activation("softmax"))
from keras.layers import Reshape
#Reshapeレイヤーを使用するために必要なモジュールをインポートします。Reshapeレイヤーは、入力データの形状を変更するために使用されます。
model.add(Reshape((28,28,1), input_shape=(28,28)))
#Reshapeレイヤーを追加します。このレイヤーは、入力データの形状を変更します。具体的には、入力データの形状を (28, 28)
から (28, 28, 1)
に変更します。これは、入力データがグレースケールの画像であり、1つのチャンネル(色)のみを持つことを示しています。このような形状のデータは、通常、畳み込みニューラルネットワーク(CNN)の入力として多々使用されています。
Conv2D出力編
Conv2Dで出力されたデータは2次元配列です。しかし、それと結合する全結合層は1次元配列しか受け付けません。そのため、全結合層(Dense)に渡す前には2次元配列を1次元に変形する必要があります。
# from keras.models import Sequential
# from keras.layers import Dense, Activation
# from keras.layers import Conv2D,MaxPooling2D,Dropout
#from keras.layers import Reshape
from keras.layers import Flatten
#model = Sequential()
# model.add(Reshape((28,28,1), input_shape=(28,28)))
# model.add(Conv2D(32,(3,3)))
# model.add(Activation("relu"))
# model.add(Conv2D(32,(3,3)))
# model.add(Activation("relu"))
# model.add(MaxPooling2D((2,2)))
# model.add(Dropout(0.5))
# model.add(Conv2D(16,(3,3)))
# model.add(Activation("relu"))
# model.add(MaxPooling2D((2,2)))
# model.add(Dropout(0.5))
model.add(Flatten())
# model.add(Dense(784))
# model.add(Activation("relu"))
# model.add(Dropout(0.5))
# model.add(Dense(10))
# model.add(Activation("softmax"))
from keras.layers import Flatten
#Flatten
をインポートしています。Flattenレイヤーは入力を平滑化するために使用されます。
model.add(Flatten())
#畳み込み層やプーリング層などで処理された2次元の特徴マップを、その後の全結合層に入力するために、1次元に変換するためにFlatten()レイヤーが使います。
MNISTデータを読み込む
前回同様、今回もMNISTを使って分析していきます。MNISTデータセットを読み込み、画像データとラベルデータを前処理してニューラルネットワークのトレーニングに適した形式に変換していきましょう。
#~~~省略~~~~~~~~~~~~~~~~~~~~~~~
from keras.datasets import mnist
from keras.utils import to_categorical
import numpy as np
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = np.array(X_train)/255
X_test = np.array(X_test)/255
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
#~~~省略~~~~~~~~~~~~~~~~~~~~~~~
from keras.datasets import mnist
#Kerasのmnist
モジュールからMNISTデータセットを読み込むためのモジュールをインポートします。MNISTデータセットは、手書き数字(0から9までの数字)の画像データセットです。
from keras.utils import to_categorical
#Kerasのto_categorical
関数を使用するためのモジュールをインポートします。この関数は、クラスラベルをone-hotエンコーディング形式に変換するのに使用されます。import numpy as np
#NumPyライブラリをnp
としてインポートします。NumPyはPythonで数値計算を行うための基本的なライブラリです。(X_train, y_train), (X_test, y_test) = mnist.load_data()
# mnist.load_data()
関数を使用してMNISTデータセットを読み込みます。この関数は、トレーニング用の画像データとラベル、テスト用の画像データとラベルを返します。それぞれを(X_train, y_train)
と(X_test, y_test)
に代入します。X_train = np.array(X_train)/255
X_test = np.array(X_test)/255
#トレーニング用とテスト用の画像データをNumPy配列に変換し、値を0から1の範囲に正規化します。これは、画像のピクセル値を0から255の範囲から0から1の範囲にスケーリングすることで、ニューラルネットワークの学習を効率的に行うためです。y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
#トレーニング用とテスト用のラベルデータをone-hotエンコーディング形式に変換します。これにより、各サンプルのラベルがクラスの数だけの次元を持つベクトルに変換されます。例えば、数字3の場合、[0, 0, 0, 1, 0, 0, 0, 0, 0, 0]となります。これは、多クラス分類のタスクにおいて、出力層でソフトマックス活性化関数を使用するために必要です。
モデルをコンパイル
モデルが学習するための損失関数や最適化アルゴリズム、および評価指標を設定していきましょう。
#~~~省略~~~~~~~~~~~~~~~~~~~~~~~
model.compile(loss="categorical_crossentropy", optimizer="sgd", metrics=["accuracy"])
model.compile
#モデルをコンパイルします。このメソッドを使用して、モデルの学習プロセスを設定します。loss="categorical_crossentropy"
#損失関数(loss function)として、カテゴリカルクロスエントロピーを指定しています。カテゴリカルクロスエントロピーは、多クラス分類問題において使用される損失関数であり、モデルの予測と真のラベルの間の差異を評価します。optimizer="sgd"
#オプティマイザ(optimizer)として、確率的勾配降下法(Stochastic Gradient Descent、SGD)を指定しています。SGDは、ミニバッチごとに勾配を計算し、モデルのパラメータを更新する際に使用される最適化アルゴリズムです。metrics=["accuracy"]
#モデルの性能評価指標(metrics)として、正解率(accuracy)を指定しています。正解率は、モデルが正しく分類したサンプルの割合を示します。訓練や評価の際に、この指標を監視することでモデルの性能を評価することができます。
モデルの訓練
訓練が開始されると、各エポックごとに訓練データと検証データの損失と評価指標が計算され、hist
オブジェクトに保存されるようにします。
#~~~省略~~~~~~~~~~~~~~~~~~~~~~~
hist = model.fit(X_train, y_train, batch_size=200, verbose=1,
epochs=1, validation_split=0.1)
hist = model.fit(X_train, y_train, batch_size=200, verbose=1,
epochs=10, validation_split=0.1)
#hist = model.fit
: モデルの訓練を開始するためのメソッドです。model
は訓練するニューラルネットワークモデルを指します
#X_train, y_train
: 訓練用の入力データ X_train
とそのラベルデータ y_train
を指定しています。このデータはモデルの学習に使用されます。
#batch_size=200
: ミニバッチのサイズを指定しています。ミニバッチ学習では、全データセットを小さなバッチに分割し、各バッチごとにモデルのパラメータを更新します。batch_size
は1回のパラメータ更新に使用するデータのサンプル数を指定します。
#verbose=1
: 訓練中にログメッセージを表示するかどうかを制御します。verbose=1
の場合、進捗バーが表示されます。表示した方がカッコいいですよ。(笑)
#epochs=
3
: エポック数を指定しています。1つのエポックとは、全ての訓練データを1回学習することを指します。epochs
パラメータは、モデルが訓練データを何回反復して学習するかを指定します。
#validation_split=0.1
:検証データの割合を指定しています。訓練データの一部を検証に使用します。この場合、訓練データの10%が検証に使用されます。訓練中にモデルの性能を監視し、過学習を防ぐために使用されます
モデルの評価
先ほど訓練したモデルをテストデータを使って評価していきます。
#~~~省略~~~~~~~~~~~~~~~~~~~~~~~
score = model.evaluate(X_test, y_test, verbose=1)
print("正解率(acc):", score[1])
score = model.evaluate(X_test, y_test, verbose=1)
#evaluate()
:モデルの性能を評価するためのメソッドです。evaluatte()メソッドは、指定されたデータセットでモデルを評価し、損失と評価指標(ここでは正解率)を返します。
#引数 X_test
はテストデータの入力、y_test
はテストデータの出力(正解ラベル)を表します。
#verbose=1
は評価の進捗状況を表示する設定です。
print(“正解率(acc):”, score[1])
#テストデータでの評価結果を出力しています。score[1]
は、evaluate
メソッドが返す評価指標のうち、正解率(accuracy)に対応する値を取得しています。この値は、モデルがテストデータで正確に予測できた割合を示します。
学習の実行
では、実行していきましょう。実行方法は人それぞれですが、私の場合はCMDでファイル名を入力することで実行しています。
ファイル名.py
モデルの保存
訓練されたニューラルネットワークモデルを”MNIST.h5″ という名前のファイルに保存しましょう。このファイルを後で読み込むことで、モデルを再利用したり、別のプログラムで使用したりすることができます。
#~~~省略~~~~~~~~~~~~~~~~~~~~~~~
model.save("MNIST.h5")
model.save(“MNIST.h5”)
#model
は保存するKerasモデルを指します。
#.save()
メソッドは、指定されたファイル名でモデルを保存します。ここでは、”MNIST.h5″ という名前のファイルにモデルが保存されます。拡張子 .h5
は、HDF5形式で保存されることを示しています。
保存したモデルを使うには
from keras.models import load_model
model = load_model("MNIST.h5")
hist = model.fit(X_train, y_train, batch_size=200, verbose=1,
epochs=3, validation_split=0.1)
from keras.models import load_model
#Kerasの models
モジュールから load_model
関数をインポートしています。load_model
関数は、保存されたモデルを読み込むために使用されます。
model = load_model(“MNIST.h5”)
#load_model
関数を使って、”MNIST.h5″ というファイルからモデルを読み込んでいます。読み込まれたモデルは、model
変数に格納されます。
hist = model.fit(X_train, y_train, batch_size=200, verbose=1,
epochs=3, validation_split=0.1)
#続きから学習を再開することができます。
#先ほどのコードと同じなので細かい説明は割愛します。
まとめ
しゅうりょーー。お疲れさまでした。書籍で原理を学ぶよりも、やっぱり動かしながら学んだ方が楽しいし、頭に入りますよね。
ということで、これからもどんどん実践を積んでいきましょう!