新着情報
Kerasを用いたニューラルネットワーク
機械学習のためのTensorflowライブラリをより簡単なコードで動かすためにKerasライブラリがある。Kerasはpythonでディープラーニングを行うためのフレームワークであり、色々な種類のディープラーニングモデルを用いて訓練・学習を実施する便利な手段を提供する。F. Cholletが2015にKerasをディープラーニングの実検を素早く実施できる様に、研究者向けに開発したものである。しかし、KerasはユーザーフレンドリなAPI(Application Programming Interface)を備えているため、現在では研究者のみならず、エンジニアから学生まで人気の高いディープラーニング用フレームワークとなっている。ここでは、Kerasライブラリでどの様にニューラルネットワークを構築し、どの様に学習をするのかを述べる。
(1) MNISTデータセットの読み込みと加工をするプログラム例を示す。
import numpy as np # keras.utilsからnp_utilsをインポート from keras.utils import np_utils # MNISTデータセットをインポート from keras.datasets import mnist # MNISTデータセットの読み込み (x_train, y_train), (x_test, y_test) = mnist.load_data() # 訓練データ # 60000x28x28の3次元配列を60000×784の2次元配列に変換 x_train = x_train.reshape(60000, 784) # 訓練データをfloat32(浮動小数点数)型に変換 x_train = x_train.astype('float32') # データを255で割って0から1.0の範囲に変換 x_train = x_train / 255 # 正解ラベルの数 correct = 10 # 正解ラベルを1-of-K符号化法で変換 y_train = np_utils.to_categorical(y_train, correct) # テストデータ # 10000x28x28の3次元配列を10000×784の2次元配列に変換 x_test = x_test.reshape(10000, 784) # テストデータをfloat32(浮動小数点数)型に変換 x_test = x_test.astype('float32') # データを255で割って0から1.0の範囲に変換 x_test = x_test / 255 # 正解ラベルを1-of-K符号化法で変換 y_test = np_utils.to_categorical(y_test, correct)
これを実行すると、
Using TensorFlow backend.
というメッセージが返ってくる。
上記のプログラム内の19行目のコメント文で#正解ラベルを1-of-K符号化法で変換と記述したが、Kerasのnp_utils.to_categorical() 関数の第2引数として正解ラベル数としてcorrect = 10を指定したので、正解ラベルが3 の場合は、4番目の要素のみが1になる要素数10の[0., 0., 0., 1., 0., 0., 0., 0., 0., 0.] 配列に変換することを意味する。これを2階テンソル化した行列を y_train に戻す。即ち、print(y_test)を実行すると以下に示す様な10個の要素からなる配列を10,000個が出力される。
[[0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]
[0. 0. 1. 0. 0. 0. 0. 0. 0. 0.]
[0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]
………..
[0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 1. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]]
Kerasによるニューラルネットワークの実装は簡単である。
(1) まず、Sequential()コンストラクターでニューラルネットワークの基本となるSequentialクラスのオブジェクトを生成し、各層の設定を行い、オブジェクトに登録する。ここでは、ます、入力層のニューロン数を784、ニューロン数200、活性化関数はReLUで構成される隠れ層を定義し、add()メソッドでSequentialオブジェクトに登録する。
Dense()は層を表現するDenseオブジェクトを生成するコンストラクターであり、
Dense(ニューロン数、input_dim = 入力されるニューロン数、activation = ‘活性化関数’ )で定義される。
(2)次に、ニューロン数10, sigmoid()関数を活性化関数とする出力層を登録する。
(3)最後に、compile()メソッドでSequentialオブジェクトをコンパイルする。この時、パラメータとして学習に使用する方法や誤差の測定法などを指定する。
# ニューラルネットワークの構築 # keras.modelsからSequentialをインポート from keras.models import Sequential # keras.layersからDense、Activationをインポート from keras.layers import Dense, Activation # keras.optimizersからAdamをインポート from keras.optimizers import Adam model = Sequential() # Sequentialオブジェクトの生成 model.add(Dense(200, # 隠れ層のニューロン数は200 input_dim=784, # 入力層のニューロン数は784 activation='relu' # 活性化関数はReLU )) model.add(Dense(10, # 出力層のニューロン数は10 activation='sigmoid' # 活性化関数はsigmoid )) model.compile( # オブジェクトのコンパイル loss='categorical_crossentropy', # 損失の基準は交差エントロピー誤差 optimizer=Adam(), # 学習方法をAdamにする metrics=['accuracy'] # 学習評価として正解率を指定 )
model.summary() を出力すると、以下を得る。
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_1 (Dense) (None, 200) 157000
_________________________________________________________________
dense_2 (Dense) (None, 10) 2010
=================================================================
Total params: 159,010
Trainable params: 159,010
Non-trainable params: 0
_________________________________________________________________
ここでは誤差の基準で、確率を出力するモデルを扱う時は交差エントロピーの使用が最適とされているのでそれを指定した。また、学習方法として従来の勾配法は「局所解」に陥る欠点があり、これを改良する確率的勾配法が提案されたが、ここでは確率的勾配法を更に改良したAdam(Adaptic moment estimation)手法を指定する。
# 学習を行って結果を出力 import time startTime = time.time() history = model.fit(x_train, # 訓練データ y_train, # 正解ラベル epochs=5, # 学習を繰り返す回数 batch_size=100, # 勾配計算に用いるサンプル数 verbose=1, # 学習の進捗状況を出力する validation_data=( x_test, y_test # テストデータの指定 )) # テストデータで学習を評価するデータを取得 score = model.evaluate(x_test, y_test, verbose=0) # テストデータの誤り率を出力 print('Test loss:', score[0]) # テストデータの正解率を出力 print('Test accuracy:', score[1]) # 処理にかかった時間を出力 print("Time:{0:.3f} sec".format(time.time() - startTime))
Train on 60000 samples, validate on 10000 samples
Epoch 1/5
60000/60000 [==============================] – 4s 73us/step – loss: 0.3234 – acc: 0.9124 – val_loss: 0.1638 – val_acc: 0.9534
Epoch 2/5
60000/60000 [==============================] – 4s 61us/step – loss: 0.1384 – acc: 0.9604 – val_loss: 0.1186 – val_acc: 0.9641
Epoch 3/5
60000/60000 [==============================] – 4s 61us/step – loss: 0.0959 – acc: 0.9724 – val_loss: 0.0876 – val_acc: 0.9735
Epoch 4/5
60000/60000 [==============================] – 4s 62us/step – loss: 0.0730 – acc: 0.9784 – val_loss: 0.0853 – val_acc: 0.9736
Epoch 5/5
60000/60000 [==============================] – 4s 62us/step – loss: 0.0580 – acc: 0.9832 – val_loss: 0.0750 – val_acc: 0.9761
Test loss: 0.07500464550508186
Test accuracy: 0.9761
Time:19.757 sec
誤り率と正解率をグラフにするkeras_nn.py
# 損失(誤り率)、正解率をグラフにする import numpy as np import matplotlib.pyplot as plt %matplotlib inline # 訓練データの損失(誤り率)をプロット plt.plot(history.history['loss'], label='training', color='black') # テストデータの損失(誤り率)をプロット plt.plot(history.history['val_loss'], label='test', color='red') plt.ylim(0, 1) # y軸の範囲 plt.legend() # 凡例を表示 plt.grid() # グリッド表示 plt.xlabel('epoch') # x軸ラベル plt.ylabel('loss') # y軸ラベル plt.show() # 訓練データの正解率をプロット plt.plot(history.history['acc'], label='training', color='black') # テストデータの正解率をプロット plt.plot(history.history['val_acc'], label='test', color='red') plt.ylim(0.5, 1) # y軸の範囲 plt.legend() # 凡例を表示 plt.grid() # グリッド表示 plt.xlabel('epoch') # x軸ラベル plt.ylabel('acc') # y軸ラベル plt.show()
隠れ層の活性化関数として用いたReLU()関数はRectified Linear Unit(正規化線形関数)の略で、最善の活性化関数であるとされている。Sigmoid関数は入力値がある程度大きくなると常に1に近い値をとり、勾配法による学習が遅くなる欠点があるが、ReLU関数は入力値が0以下では0となり、入力値が0より大きい時は入力値をそのまま出力するので、学習の停滞を回避できるメリットがあるとされている。
ReLU関数のグラフは以下のプログラムで表示できる。
import numpy as np import matplotlib.pylab as plt %matplotlib inline def relu(x): # maximum()は2つの配列を比較して大きい要素を返す return np.maximum(0, x) x = np.arange(-5.0, 5.0, 0.1) y = relu(x) plt.plot(x, y) plt.show()
この投稿で提示したように、ラルネットワークを用いた学習による手書き数字の認識の精度は大体97%になることが確認できた。