🌟

ディープラーニングの手順を解説してPythonで実装する

2021/12/25に公開

今回はMNISTの手書き文字認識を題材にします。
学習の種類としては「教師あり学習の分類問題」となります。

ディープラーニングでは下記の手順でディープニューラルネットーワーク(以下"モデル"と表記)を作成し、実データから予測を行います。

  1. データを用意する
  2. モデルを構築する
  3. モデルに学習させる
  4. モデルを評価する

データを用意する

ディープラーニングを行うためには大量のデータを用意する必要があります。
データは自分で用意する他にも、まとめてあるサイトからダウンロードしたり、機械学習のライブラリに付属しているものを使ったりすることができます。

モデルを構築する

データを用意したら、モデル(DNN)の構築を行います。
構築には、下記の情報が必要になります。

入力層、隠れ層、出力層のニューロン数 / 隠れ層の数


入力層、隠れ層、出力層のニューロン数を決めます。分類問題を行う場合は出力層のニューロン数は分類の種類数と同じにすることが多いです。
また隠れ層の数も決めます。層が薄すぎると精度が出ませんが、厚過ぎても勾配消失などの問題が発生してしまうことがあるため、適切な層の数を選択する必要があります。

活性化関数


ニューロンの出力値を決定する活性化関数を決めていきます。
様々な種類があり、また学習や層の種類ごとに変えていく必要があります。
ここでは代表的なものを5つ紹介します。

  • シグモイド関数 ... 滑らかで微分しやすい
  • tanh ... 0を中心とした対象でバランスがいい
  • ReLU ... 層が深くても安定する。出力層以外でよく用いる。
  • 恒等関数 ... 入力値がそのまま出力される。回帰問題の出力層でよく用いる
  • ソフトマックス関数 ... 同じ層の合計が1になるような値が出力される。総和に占める割合を取得できるため、分類問題の出力層でよく用いる。

損失関数


最適化アルゴリズムに渡す出力値と正解値の差(損失)の値を計算する関数を決めていきます。
ここでは代表的なものを2種類紹介します。

  • 二重和誤差 ... 出力値と正解値の二重の総和。どの程度一致しているかを定量化でき、連続した数値の処理に強みを持つため回帰問題でよく用います。
  • 交差エントロピー誤差 ... 出力値の対数と正解値の積の総和をマイナスする。正解値を1とした時、出力値がどのぐらい離れているかを表すことができるため分類問題でよく用います。

最適化アルゴリズム


損失を元にバイアスと重みを調節し、モデルを最適化(バックプロパゲーション)するためのアルゴリズムを決めていきます。
ここでは代表的なものを5種類紹介していきます。

  • 確率的勾配降下法(SGD) ... 学習結果からランダムに抽出したサンプルを元に勾配降下法を行う。局所最適解に囚われにくい
  • Momentum ... 勾配を移動平均したグラフを元に勾配降下法を行う。急激な変化を抑えられるので、振動に強い
  • AdaGrad ... 各次元ごとに学習率を調整していくことができるため効率がいい。
  • RMSProp ... AdaGradの欠点である最初の勾配が急な場合に局所最適解に陥ってしまうことがあることを解消したもの。
  • Adam ... 最もよく使われている最適化アルゴリズムのひとつ。AdaGradとRMSPropを合わせたもの。2つのいいとこ取り

バッチサイズ


学習データを何個ずつ学習するか決めていきます。
バッチサイズによって以下の3つの学習に分けられます。
バッチ学習

オンライン学習

ミニバッチ学習

エポック数

学習データを何回回すか決めていきます。

評価関数

モデルを評価するための関数を決めていきます。
代表的なものに正解率があります。

モデルに学習させる


学習は下記のフローで行います。

  1. バッチサイズ分のデータを入力層に入力する
    1. 各ニューロンでの処理
      • 入力値 * 重みの総和を取り、バイアスを加算してニューロンの入力値とする
      • 入力値を活性化関数に掛け、ニューロンの出力値を計算する
      • 出力値を次のニューロンに渡す
      • これを繰り返し、出力層から結果を出力する
    2. これをバッチサイズ分繰り返す
    3. 損失関数で出力値と正解値との誤差を求める
    4. 最適化アルゴリズムでバイアスと重みを調整し最適化する
    5. これをデータを1周するまで行う
  2. これをエポック数分繰り返す

モデルを評価する

評価関数で、モデルを評価します。
納得いくまでモデルの構築 ~ モデルの評価までを繰り返します。
モデルの評価に納得できたら完了です。

pythonで実装してみる

MNISTの手描き文字認識を題材にしたディープラーニングをpythonのKerasというライブラリを使用して実装してみましょう。

コード全文

# MNISTによるMLP構築の写経
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Activation

# データを用意する
# x ... 入力データ
# y ... 出力データ
# train ... 学習用データ
# test ... 検証用データ
(x_train, y_train), (x_test, y_test) = mnist.load_data()

# 入力データをベクトルに変換する
x_train = x_train.reshape(-1, 784)
x_test = x_test.reshape(-1, 784)

# 出力データをone_hot表現に変形する
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)

# モデルを構築する
model = Sequential()

# 入力層
# ニューロン数 500, 活性化関数 ReLU
model.add(Dense(units=500, input_shape=(784,))) # Dense ... 全結合層
model.add(Activation('relu')) # Activation ... 活性化関数
# 隠れ層1
# ニューロン数 250, 活性化関数 ReLU
model.add(Dense(units=250))
model.add(Activation('relu'))
# 隠れ層2
# ニューロン数 100, 活性化関数 ReLU
model.add(Dense(units=100))
model.add(Activation('relu'))
# 出力層
# ニューロン数 10, 活性化関数 softmax関数
model.add(Dense(units=10))
model.add(Activation('softmax')) 

# loss ... 損失関数
# optimizer ... 最適化アルゴリズム
# metrics ... 評価関数
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

# モデルに学習させる
model.fit(x_train, y_train, batch_size=1000, epochs=10, verbose=1, validation_data=(x_test, y_test))

# モデルを評価する
test_loss, test_accuracy = model.evaluate(x_test, y_test, verbose=0)
print('損失:', test_loss)
print('評価:', test_accuracy)

解説

はじめにデータを読み込みます。
keras.datasets.mnistのload_data()メソッドを呼び出すことで、データを読み込むことができます

(x_train, y_train), (x_test, y_test) = mnist.load_data()

データは4種類用意されており、それぞれ
x_train ... 学習用データの入力値
y_train ... 学習用データの正解値
x_test ... 検証用データの入力値
y_test ... 検証用データの正解値
となります。
ディープラーニングでは学習用データと検証用データは別に用意する必要があるため、分けられています。

今回の入力データは画像 = 二次元配列であるため、ベクトルに変換しモデルに入力できるようにする必要があります。
reshapeメソッドを呼び出すことで、配列の次元数を操作しベクトルに変換することができます。

x_train = x_train.reshape(-1, 784)
x_test = x_test.reshape(-1, 784)

分類問題では出力層の活性化関数にsoftmax関数を使用し、正解値の位置に対応するニューロンの出力値を1に、それ以外のニューロンの出力値を0に近づけるよう学習を行うため、正解値をone-hot表現にする必要があります。
こちらの処理にはkeras.utilsのto_categorical関数を使用します。

y_train = to_categorical(y_train)
y_test = to_categorical(y_test)

ここまででデータの準備はできたので、モデルの構築を行います。
kerasではkeras.modelsのSequentialでモデルを入れる箱を用意し、その中にkeras.layersのDenseと Activationを重ねていくようにしてモデルを構築していきます。
Denseはディープラーニングで使用頻度が最も高い全結合層を表します。
Activationは活性化関数を表します。
これら2つを1セットとして層を作成し、重ねていくことでDNNを構築していきます。
Sequentialのaddメソッドを使用して、モデルに層を重ねていきます。

入力層では用意したデータを入力していくため、input_shapeに入力データのベクトル長を指定する必要があります。
今回は入力層と隠れ層の活性化関数には、ReLUを使用します。

model.add(Dense(units=500, input_shape=(784,)))
model.add(Activation('relu'))

隠れ層1

model.add(Dense(units=250))
model.add(Activation('relu'))

隠れ層2

model.add(Dense(units=100))
model.add(Activation('relu'))

今回はsoftmax関数を使用した分類問題を行うため、出力層のニューロン数は正解値の種類と同じ数を指定します。

model.add(Dense(units=10))
model.add(Activation('softmax')) 

Sequentialのcompileメソッドで

  • 損失関数
  • 最適化アルゴリズム
  • 評価関数

を指定し、モデルの構築を完了します。

今回は

  • 損失関数 ... 交差エントロピー誤差
  • 最適化アルゴリズム ... adam
  • 評価関数 ... 正解率

を指定します。

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

問題ごとの基本的な出力層の活性化関数と使用する損失関数の組み合わせは下記になります。

分類問題

回帰問題

Sequentialのfitメソッドを呼び出して、モデルに学習させていきます。
ここでバッチサイズとエポック数を指定します。
今回は

  • バッチサイズ ... 1,000
  • エポック数 ... 10

を指定します。

model.fit(x_train, y_train, batch_size=1000, epochs=10, verbose=1, validation_data=(x_test, y_test))

Sequentialのevaluateメソッドを呼び出し、検証用データを引数に渡してモデルの評価を行います。
結果配列のindex = 0には損失関数の結果、index = 1には評価関数の結果が格納されています。

test_loss, test_accuracy = model.evaluate(x_test, y_test, verbose=0)
print('損失:', test_loss)
print('評価:', test_accuracy)

まとめ

今回はMNISTの手書き文字認識を題材にし、基本的なディープラーニングの手順とpythonを使用した実装方法について解説していきました。
MNISTには他にもいろいろなデータがあり、他の分類問題や回帰問題に使用できるデータがありますので、興味を持った方は他の問題についてもチャレンジしてみてください!
また今回は画像サイズが小さいため行いませんでしたが、画像認識等に強みを持つCNN(畳み込みニューラルネットワーク)というものもありますでの、またそちらの解説記事についても書いてみたいと思います。

後でやる
数式の記述、解説

GitHubで編集を提案

Discussion