EeBlog(テクニカルブログ)

AI(人工知能)実践 第9回 Keras(アヤメの分類)

AI(人工知能)実践 第9回です。
第6回~第8回までにかけて、tf.Estimatorを利用したアヤメの分類について解説してきました。
今回からはtf.kerasを利用してのアヤメの分類の実装とその解説となります。
 

まず、Kerasとは何かですが、公式サイト(https://keras.io/ja/)では

Kerasは,Pythonで書かれた,TensorFlowまたはCNTK,Theano上で実行可能な高水準のニューラル
ネットワークライブラリです. Kerasは,迅速な実験を可能にすることに重点を置いて開発されました.
アイデアから結果に到達するまでのリードタイムをできるだけ小さくすることが,良い研究をするため
の鍵になります.

と記載されています。
上記のサイトでは、TensorFlowとは異なり、日本語でAPI解説も書かれていますので、
使用する際にはぜひ一読してみて下さい。

ざっくり言えば、Kerasを使用することで、tf.Estimetorsを利用するよりも細かく、
かつTensorFlowの低レベルAPIを利用するより簡単に実装することが出来ます。

では、今回はTensorFlow上で実行する形でアヤメの分類の実装をしていきます。
 

ソース全文

import tensorflow as tf
import pandas as pd
import os
import urllib
import numpy as np

from tensorflow.python.keras.models import Sequential
from tensorflow.python.keras.layers import Activation, Dense

IRIS_TRAINING = "iris_training.csv"
IRIS_TRAINING_URL = "http://download.tensorflow.org/data/iris_training.csv"

IRIS_TEST = "iris_test.csv"
IRIS_TEST_URL = "http://download.tensorflow.org/data/iris_test.csv"

#データファイルが無い場合ダウンロード
if not os.path.exists(IRIS_TRAINING):
    raw = urllib.urlopen(IRIS_TRAINING_URL).read()
    with open(IRIS_TRAINING,'w') as f:
        f.write(raw)

if not os.path.exists(IRIS_TEST):
    raw = urllib.urlopen(IRIS_TEST_URL).read()
    with open(IRIS_TEST,'w') as f:
        f.write(raw)
    
#データ読み込み用のメソッド作成
def read_file(file_name):
    data=pd.read_csv(file_name, usecols = [0, 1, 2, 3])
    label = pd.read_csv(file_name, usecols = [4])
    return data.values, label.values

#データ読み込み
training_x, training_y = read_file(IRIS_TRAINING)
test_x, test_y = read_file(IRIS_TEST)


#ラベルを数値ではなく、行列(0,1の値)でなければならない為、ラベルをベクトルから行列に変更する
training_y = tf.keras.utils.to_categorical(training_y, 3)
test_y = tf.keras.utils.to_categorical(test_y, 3)


# モデルの定義
model = Sequential()

# ネットワーク作成
#中間層
model.add(Dense(10, activation='relu', input_shape=(4,)))
model.add(Dense(20, activation='relu'))
model.add(Dense(10, activation='relu'))
#出力層
model.add(Dense(3, activation='softmax'))


# モデルのコンパイル(損失関数はcategorycal_crossentropy(多クラス分類時の交差エントロピー)
# 最適化アルゴリズムはsgd(確率的勾配降下法))
model.compile(loss='categorical_crossentropy',
              optimizer='sgd',
              metrics=['accuracy'])


# 学習
model.fit(training_x, training_y,
                    epochs=2000,
                    verbose=0,
                    validation_data=(test_x, test_y))


# 学習モデルの評価
accuracy_score = model.evaluate(test_x, test_y, verbose=0)[1]


print("\nTest Accuracy: {0:f}\n".format(accuracy_score))

解説

ライブラリのインポート

import tensorflow as tf
import pandas as pd
import os
import urllib
import numpy as np

from tensorflow.python.keras.models import Sequential
from tensorflow.python.keras.layers import Activation, Dense

必要となるライブラリのインポートを行っています。
前回までのソースに追加して、今回はtf.kerasモジュールの
Sequential、Activation、Denseもインポートしています。
 

データのダウンロード~読み込み

#データファイルが無い場合ダウンロード
if not os.path.exists(IRIS_TRAINING):
    raw = urllib.urlopen(IRIS_TRAINING_URL).read()
    with open(IRIS_TRAINING,'w') as f:
        f.write(raw)

if not os.path.exists(IRIS_TEST):
    raw = urllib.urlopen(IRIS_TEST_URL).read()
    with open(IRIS_TEST,'w') as f:
        f.write(raw)
    
#データ読み込み用のメソッド作成
def read_file(file_name):
    data=pd.read_csv(file_name, usecols = [0, 1, 2, 3])
    label = pd.read_csv(file_name, usecols = [4])
    return data.values, label.values

#データ読み込み
training_x, training_y = read_file(IRIS_TRAINING)
test_x, test_y = read_file(IRIS_TEST)

この辺りは第6回、第7回で紹介したコードそのままです。
データが無ければダウンロードし、ファイルを読み込んでいきます。
 

ラベルの加工

#ラベルを数値ではなく、行列(0,1の値)でなければならない為、ラベルをベクトルから行列に変更する
training_y = tf.keras.utils.to_categorical(training_y, 3)
test_y = tf.keras.utils.to_categorical(test_y, 3)

今回は、ラベルが数値のベクトルではなく、0か1の値の行列である必要があるため、
to_categorical関数で変換しています。
第一引数は数値ベクトルのラベル(今回は0~2の値が入っています。)、
第二引数はラベル数(0~2までなので3つ)です。
なお、変換前後は下記のような形となります。
元の値が0,1,2の場合
0: [1, 0, 0]
1: [0, 1, 0]
2: [0, 0, 1]
 

モデルの定義

# モデルの定義
model = Sequential()

Sequential()で層を積み重ねたものでネットワークを構築する準備をしています。
kerasではSequentialモデル(https://keras.io/ja/models/sequential/)か、
functionalAPIと共に用いるモデル(https://keras.io/ja/models/model/)かの
2つから選択出来ます。
 

ネットワークの作成

# ネットワーク作成
#中間層
model.add(Dense(10, activation='relu', input_shape=(4,)))
model.add(Dense(20, activation='relu'))
model.add(Dense(10, activation='relu'))
#出力層
model.add(Dense(3, activation='softmax'))

Estimatorのソースの「DNN(Deep Neural Networks)分類器の作成」に該当する部分です。
先程のモデルの定義の際に、Sequentialで定義したため、
順に層を積み重ねていきます。
今回のソースでは、
10のノードを持ち、入力は4層で活性化関数がReLUの層を追加
20のノードを持ち、活性化関数がReLUの層を追加
10のノードを持ち、活性化関数がReLUの層を追加
3のノードを持ち、活性化関数がsoftmaxの層を追加※出力層用
となります。

ReLUやsoftmax以外には、tanhやsigmoid、linear等も使用出来ます。
詳しくは公式サイト(https://keras.io/ja/activations/)でご確認下さい。

なお、作成したネットワークについて確認したい場合、
model.summary()
を記述することで、確認が可能です。
 

モデルのコンパイル

# モデルのコンパイル(損失関数はcategorycal_crossentropy(多クラス分類時の交差エントロピー)
# 最適化アルゴリズムはsgd(確率的勾配降下法))
model.compile(loss='categorical_crossentropy',
              optimizer='sgd',
              metrics=['accuracy'])

先程までで層を追加してきたモデルをコンパイルします。
今回はアヤメの分類ですので、
損失関数をcategorycal_crossentropyを選択し、最適化アルゴリズムはsgdとしました。

損失関数とはそもそも何かというと、ニューラルネットワークが学習データに対して、
どれだけ誤差があるかを算出するものです。
損失関数から戻る値が0に近ければ近いほど良いということになります。
kerasで選択出来る損失関数については、公式サイト(https://keras.io/ja/losses/)を参照してください。

最適化に関しても、何をするものかというと、
ざっくりいえば損失関数の値を最小もしくは最大にするための関数です。
kerasで選択出来る最適化関数については、
公式サイト(https://keras.io/ja/optimizers/)を参照してください。

上記で触れていない引数metricsについては、評価関数の事を示しています。
評価関数とは、モデルの性能を測るために使われます。
定義されている評価関数の識別子を文字列として指定するか、
自分で定義した関数を関数として指定することが出来ます。
詳しくは公式サイト(https://keras.io/ja/metrics/)を参照してください。
 

学習の実行

# 学習
model.fit(training_x, training_y,
                    epochs=2000,
                    verbose=0,
                    validation_data=(test_x, test_y))

定義したモデルにて学習を実行しています。
学習の繰り返し回数は2000回で、冗長なメッセージ(学習中の出力)は無しとしています。
学習中のメッセージを確認したい場合は、verbose=1として下さい。

尚、このソースでは戻り値を受けていませんが、fit関数はHistoryオブジェクトを返します。
Historyオブジェクト(https://keras.io/ja/callbacks/#history)では、
実行に成功したエポックにおける訓練の損失値と評価関数値の記録と、
検証における損失値と評価関数値も記録しているので必要に応じて変数で受けておくと良いでしょう。
 

学習モデルの評価

# 学習モデルの評価
accuracy_score = model.evaluate(test_x, test_y, verbose=0)[1]


print("\nTest Accuracy: {0:f}\n".format(accuracy_score))

evaluateメソッドにてモデルの評価を行っています。
今回は精度のみ取得していますが、損失も表示したい場合は

model.evaluate(test_x, test_y, verbose=0)[1]

ではなく

model.evaluate(test_x, test_y, verbose=0)

として、変数に受け取ります。
受け取った値のインデックス0が損失、インデックス1が精度となります。

以上で、kerasでのアヤメの分類についての説明は終了です。
今回もEstimatorを利用した場合と同じく96%程度の精度は出ているかと思います。