TensorFlowをバックグラウンドで動作させるKerasを使って、scikit-learnで提供されているアヤメのデータセットを学習し、分類させる多層パーセプトロンのニューラルネットワークモデルを作ってみたので、サンプルコードを紹介する。
コンテンツ
TensorFlow環境の構築
今回はJupyterNotebookでコードを書いていく。
Anaconda仮想環境にTensorFlow環境を構築する必要があるので、下記の記事を参考にまずは環境構築をおこなう。
なお、ここで新しく構築した環境には後で登場する「Keras」や「scikit-learn」などのライブラリがインストールされていないため「pip install」コマンドで随時インストールをおこなう必要がある。
環境が用意できたらJupyterNotebookを起動し、以下のコードを実行する。
エラーが発生しなければOK。
※Using TensorFlow backend. のメッセージはエラーではない。
1 2 | import tensorflow as tf import keras |
補足で説明しておくと「Keras」はTensorFlowのようなディープラーニングに対応したライブラリを、より簡単に扱うためのライブラリだ。
ただし「Keras」だけでは動作しないので、必ずTensorFlowなどのライブラリも同時に必要となる。
データセットの読み込み
今回使用するデータセットは「scikit-learn」から読み込む。
1 2 3 4 5 | from sklearn import datasets iris = datasets.load_iris() x = iris.data y = iris.target |
また、読み込んだデータセットを説明変数(x)と目的変数(y)に分けておく。
それぞれのデータの中身を確認しよう。
01 02 03 04 05 06 07 08 09 10 | 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関数を使って正規化をおこなう。
1 2 3 4 5 6 7 8 9 | 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 |
Setosa | 0 | [1, 0, 0] |
Versicolor | 1 | [0, 1, 0] |
Virginica | 2 | [0, 0, 1] |
One-Hotへのエンコーディングは次のとおり、np_utilsを使えば良い。
1 2 3 4 5 6 7 8 9 | 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.], # ... |
説明変数を訓練データとテストデータに分割する
次に正規化した説明変数を、訓練データとテストデータに分割する。
1 2 3 | 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関数で訓練データを使って学習といった手順でおこなっていく。
まずはモデルの作成からパラメータを与えるところまで一気に見ていこう。
01 02 03 04 05 06 07 08 09 10 11 12 13 | 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関数で学習をおこなっていく。
1 2 3 4 | 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関数を実行すると以下のように学習の過程が出力されるはずだ。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 | 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値が小さくなっていることが分かる。
モデルの精度を評価
では学習したモデルの精度を測ってみよう。
1 2 3 4 | loss, accuracy = model.evaluate(x_test, y_test, verbose = 0 ) print ( 'Accuracy' , '{:.2f}' . format (accuracy)) # Accuracy 0.97 |
97%とかなり高い精度の学習モデルができあがった。
実際にテストデータを使って分類をおこなってみる。
下記のとおり、分類の正解は「2([0, 0, 1])」のVirginicaだ。
1 2 3 4 5 | print (x_test[: 1 ]) print (y_test[: 1 ]) # [[5.8 2.8 5.1 2.4]] # [[0. 0. 1.]] |
この説明変数を学習器に与えてみる。
1 2 3 | model.predict_classes(x_test[: 1 ], batch_size = 1 ) # array([2]) |
見事に正解結果が得られた。