TensorFlowをバックグラウンドで動作させるKerasを使って、scikit-learnで提供されているアヤメのデータセットを学習し、分類させる多層パーセプトロンのニューラルネットワークモデルを作ってみたので、サンプルコードを紹介する。

TensorFlow環境の構築

今回はJupyterNotebookでコードを書いていく。

Anaconda仮想環境にTensorFlow環境を構築する必要があるので、下記の記事を参考にまずは環境構築をおこなう。

なお、ここで新しく構築した環境には後で登場する「Keras」や「scikit-learn」などのライブラリがインストールされていないため「pip install」コマンドで随時インストールをおこなう必要がある。

環境が用意できたらJupyterNotebookを起動し、以下のコードを実行する。
エラーが発生しなければOK。
※Using TensorFlow backend. のメッセージはエラーではない。

import tensorflow as tf
import keras

補足で説明しておくと「Keras」はTensorFlowのようなディープラーニングに対応したライブラリを、より簡単に扱うためのライブラリだ。
ただし「Keras」だけでは動作しないので、必ずTensorFlowなどのライブラリも同時に必要となる。

データセットの読み込み

今回使用するデータセットは「scikit-learn」から読み込む。

from sklearn import datasets

iris = datasets.load_iris()
x = iris.data
y = iris.target

また、読み込んだデータセットを説明変数(x)と目的変数(y)に分けておく。

それぞれのデータの中身を確認しよう。

x[:5]
# がくの長さ、がくの幅、花弁の長さ、花弁の幅
# array([[5.1, 3.5, 1.4, 0.2],
#        [4.9, 3. , 1.4, 0.2],
#        [4.7, 3.2, 1.3, 0.2],
#        [4.6, 3.1, 1.5, 0.2],
#        [5. , 3.6, 1.4, 0.2]])

y
# 0: Setosa, 1: Versicolor, 2: Virginica

xにはアヤメの特徴量が格納されており、それぞれの説明変数によってアヤメは3つの種類(y)に分類される。

このデータセットをディープラーニングさせたモデルを作り、アヤメの分類器を作っていく。

説明変数xの正規化

xは大体0.1〜8.0の間にデータが散らばっている。

データの正規化は、例えば身長・体重など単位の違う複数の説明変数を使用する際によく行われ、正規化をすることでモデルの精度が向上する場合がある。

今回xに対し、次のとおりscale関数を使って正規化をおこなう。

from sklearn import preprocessing

x = preprocessing.scale(x)
x[:5]
# array([[-0.90068117,  1.01900435, -1.34022653, -1.3154443 ],
#        [-1.14301691, -0.13197948, -1.34022653, -1.3154443 ],
#        [-1.38535265,  0.32841405, -1.39706395, -1.3154443 ],
#        [-1.50652052,  0.09821729, -1.2833891 , -1.3154443 ],
#        [-1.02184904,  1.24920112, -1.34022653, -1.3154443 ]])

これにより説明変数が平均値0、標準偏差1の値に正規化された。

目的変数yをダミー変数(One-Hot)に変換する

今回の目的変数は0、1、2のいずれかの値だが、これはアヤメの品種を表す値のため、量的データでなく質的データと言える。

この目的変数をそのまま使用すると、量的データとして認識され、結果として1.5や0.8などの半端な値が予測されてしまうことになる。

この問題を避けるため、目的変数を「One-Hot」と呼ばれる形式に変換する。

変換後のデータは次のとおり、行列形式にエンコーディングされる。

品種変換前One-Hot
Setosa0[1, 0, 0]
Versicolor1[0, 1, 0]
Virginica2[0, 0, 1]

One-Hotへのエンコーディングは次のとおり、np_utilsを使えば良い。

from keras.utils import np_utils

y = np_utils.to_categorical(y)

# array([[1., 0., 0.],
#        [1., 0., 0.],
#        [1., 0., 0.],
#        [1., 0., 0.],
# ...

説明変数を訓練データとテストデータに分割する

次に正規化した説明変数を、訓練データとテストデータに分割する。

from sklearn.model_selection import train_test_split

x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=0)

モデルの作成と学習

Kerasを使ったモデルの学習の流れは、まずモデルを用意し、モデルにいくつかのパラメータを与えてcompileする。
そして最後にfit関数で訓練データを使って学習といった手順でおこなっていく。

まずはモデルの作成からパラメータを与えるところまで一気に見ていこう。

from keras.models import Sequential
from keras.layers.core import Dense, Activation

model = Sequential()

# Denseの第一引数は隠れ層のニューロン数を、
# 第二引数は入力層(がくの長さ、幅、花弁の長さ、幅)をタプル形式で指定
model.add(Dense(16, input_shape=(4,)))
model.add(Activation('relu'))

# 3種の分類をしたいので出力層は3を指定
model.add(Dense(3))
model.add(Activation('softmax'))

補足として、今回はニューロン数を16としたが特に決まりはなく、本来は学習の結果を見ながら調整するところだ。

また、Activationは活性化関数と呼ばれるものだが、ここでは深く掘り下げない。

さて、次はモデルをcompileし、fit関数で学習をおこなっていく。

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

model.fit(x_train, y_train, epochs=50, batch_size=1, verbose=1)

補足すると、まずcompile関数のoptimizerで最適化アルゴリズムを指定する。
多数のアルゴリズムが用意されているが今回は「adam」を使用した。

次にlossだが、ここでは任意の損失関数を指定できる。
今回は「categorical_crossentropy」という関数を選択。

最後にmetricsだが、これは評価関数を意味し「accuracy」を選択した。

さて、いよいよモデルの学習をfit関数で実行するが、引数について簡単に紹介しておこう。

epochsは整数でモデルを訓練するエポック数(x・y全データの反復)を指定する。

batch_sizeは設定したサンプル数ごとに勾配の更新を行う。

verboseはログの表示形式を表す。
0はログを出力せず、1はプログレスバーで標準出力、2はエポックごとに1行のログを出力する。

今回はverboseに1を指定したので、fit関数を実行すると以下のように学習の過程が出力されるはずだ。

Epoch 1/50
112/112 [==============================] - 0s 2ms/step - loss: 1.3313 - accuracy: 0.3661
Epoch 2/50
112/112 [==============================] - 0s 784us/step - loss: 0.8698 - accuracy: 0.4554
Epoch 3/50
112/112 [==============================] - 0s 701us/step - loss: 0.7584 - accuracy: 0.7054
...
...
Epoch 48/50
112/112 [==============================] - 0s 708us/step - loss: 0.1079 - accuracy: 0.9732
Epoch 49/50
112/112 [==============================] - 0s 713us/step - loss: 0.1057 - accuracy: 0.9732
Epoch 50/50
112/112 [==============================] - 0s 728us/step - loss: 0.1042 - accuracy: 0.9732

ログの「loss」に注目してほしい。

この値が小さければ小さいほど、モデルの精度は高まっているということだ。

今回はepochsに50を指定したが、学習が進むにつれてloss値が小さくなっていることが分かる。

モデルの精度を評価

では学習したモデルの精度を測ってみよう。

loss, accuracy = model.evaluate(x_test, y_test, verbose=0)
print('Accuracy', '{:.2f}'.format(accuracy))

# Accuracy 0.97

97%とかなり高い精度の学習モデルができあがった。

実際にテストデータを使って分類をおこなってみる。

下記のとおり、分類の正解は「2([0, 0, 1])」のVirginicaだ。

print(x_test[:1])
print(y_test[:1])

# [[5.8 2.8 5.1 2.4]]
# [[0. 0. 1.]]

この説明変数を学習器に与えてみる。

model.predict_classes(x_test[:1], batch_size=1)

# array([2])

見事に正解結果が得られた。