snovaのブログ

主にプログラミングやデジタルコンテンツについて書きます。最近はPython, Flutter, VRに興味があります。

KerasでIrisのデータセットを分類し、モデルを保存する

目次

イントロダクション

以前まで、Tensorflowを使っていましたが、 モデルを構築することが簡単 だったので、Kerasに乗り換えてみました。
今回は、非常に簡単な問題であるアヤメデータセットの分類を多層パーセプトロンで学習、予測してみます。

計算機環境

  • OS : Ubuntu 16.04
  • CPU : core i7 (第7世代)
  • RAM : 16GB
  • Python 3.5.5, Keras 2.2.0 (バックエンドはTensorflow 1.9.0)
    (どうでもいいですが、エディターもAtomからVSCodeに乗り換えました)

データのロード

scikit-learnがあれば、簡単にデータをロードできます。

from sklearn.datasets import load_iris
iris = load_iris()

print(iris.data) # xデータ
print(iris.feature_names) # 変数の説明
print(iris.target) # yデータ
print(iris.target_names) # 変数の説明

結果 :

[[5.1 3.5 1.4 0.2]
 [4.9 3.  1.4 0.2]
 [4.7 3.2 1.3 0.2]

...

 [6.5 3.  5.2 2. ]
 [6.2 3.4 5.4 2.3]
 [5.9 3.  5.1 1.8]]
['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
[0 0 0 0 0 0 0  ...  2 2 2 2 2 2 2]
['setosa' 'versicolor' 'virginica']

ちなみに、データの型はnumpy配列です。

print(type(iris.data))
print(type(iris.target))

結果 :

<class 'numpy.ndarray'>
<class 'numpy.ndarray'>

データ処理

分類問題なので、one-hotエンコーディングを実装します。
kerasにはnp_utilsという関数があるので、それを使います。
これは、int変数をone-hotエンコーディングで使われる配列形式に変換できます。
Irisデータセットでは、3種類の花に分類するので、[0, 0, 1], [0, 1, 0], [1, 0, 0]のいずれかに変換することになります。

from keras.utils import np_utils
data_y = np_utils.to_categorical(iris.target)

次に、データセットを訓練データとテストデータに分離させます。
これには、scikit-learnのtrain_test_splitを使います。
ここで、引数のtest_sizeはデータ全体から見たテストデータの割合、random_stateはランダムシードの設定を示しています。
つまり、test_size=0.3は (訓練データ数):(テストデータ)=7:3 を意味します。
通常、random_stateは設定しませんが、今回は設定してみます。

from sklearn.model_selection import train_test_split
data_X = iris.data
data_y = np_utils.to_categorical(iris.target)
X_train, X_test, y_train, y_test = train_test_split(data_X, data_y, test_size=0.3, random_state=0)
print(X_train, X_test, y_train, y_test)

結果 :

[[5.8 2.8 5.1 2.4]
 [6.  2.2 4.  1. ]
 [5.5 4.2 1.4 0.2]
...
[5.8 2.7 4.1 1. ]
 [7.7 3.8 6.7 2.2]
 [4.6 3.2 1.4 0.2]] [[5.8 2.8 5.1 2.4]
 [6.  2.2 4.  1. ]
 [5.5 4.2 1.4 0.2]
...
 [6.9 3.1 5.1 2.3]
 [5.  3.5 1.6 0.6]
 [5.4 3.7 1.5 0.2]] [[0. 1. 0.]
 [0. 0. 1.]
 [0. 0. 1.]
...
 [0. 1. 0.]
 [0. 0. 1.]
 [1. 0. 0.]] [[0. 0. 1.]
 [0. 1. 0.]
 [1. 0. 0.]
...
 [0. 0. 1.]
 [1. 0. 0.]
 [1. 0. 0.]]

Kerasで学習

流れは、モデルを定義 -> ネットワークを構築 -> コンパイル -> 実行 みたいな感じです。
層を重ねる時は、基本的にmodel.addを使用すればよいです。

ここで、 model.add(Dense(input_dim=4, output_dim=100, activation='relu')) は、この層の入力には4変数、出力には100変数、活性化関数にはrelu関数を使うことを意味しています。
また、model.compile(loss='categorical_crossentropy', optimizer=Adam(), metrics=['accuracy']) とは、損失関数をcategorical_crossentropy、最適化関数にはAdam、評価関数には精度を使用するという意味です。
csv_logger = CSVLogger('log.csv', separator=',', append=False) の部分はログをcsvで保存するためのものです。

学習の条件ですが、

変数名
層の数 3
入力層 4変数
中間層 100変数
出力層 3変数
バッチ数 32
epoch数 100

以下、コード

from keras.models import Sequential
from keras.layers.core import Dense
from keras.utils import to_categorical
from keras.optimizers import Adam
from keras.callbacks import CSVLogger

model = Sequential()
model.add(Dense(input_dim=4, output_dim=100, bias=True, activation='relu'))
model.add(Dense(input_dim=100, output_dim=3, bias=True, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer=Adam(), metrics=['accuracy'])
csv_logger = CSVLogger('log.csv', separator=',', append=False)
history = model.fit(X_train, y_train,
                    batch_size=32, epochs=100,
                    validation_data=(X_test, y_test),
                    callbacks=[csv_logger])

ターミナルの画面はこんな感じになります。

Epoch 1/100
105/105 [==============================] - 1s 5ms/step - loss: 1.1275 - acc: 0.3714 - val_loss: 1.1719 - val_acc: 0.2444
Epoch 2/100
105/105 [==============================] - 0s 75us/step - loss: 1.0522 - acc: 0.3714 - val_loss: 1.1002 - val_acc: 0.2444
Epoch 3/100
105/105 [==============================] - 0s 62us/step - loss: 1.0023 - acc: 0.3714 - val_loss: 1.0427 - val_acc: 0.2444
Epoch 4/100
105/105 [==============================] - 0s 54us/step - loss: 0.9615 - acc: 0.3714 - val_loss: 0.9832 - val_acc: 0.2444
...

もし、ターミナルの画面に上のような途中結果を出力したくなかったら、model.fitverbose=0と追記します。

history = model.fit(X_train, y_train,
                    batch_size=32, epochs=100,
                    validation_data=(X_test, y_test),
                    verbose=1,
                    callbacks=[csv_logger])

モデルの評価

model.evaluateを使うと、学習し終えたモデルの誤差と精度を呼び出すことができます。

train_score = model.evaluate(X_train, y_train)
test_score = model.evaluate(X_test, y_test)
print('Train loss:', train_score[0])
print('Train accuracy:', train_score[1])
print('Test loss:', test_score[0])
print('Test accuracy:', test_score[1])

結果 :

Train score: 0.15385689522538867
Train accuracy: 0.9809523815200443
Test score: 0.18351854549513924
Test accuracy: 0.9777777791023254

ちなみに、model.predictでは具体的な予測結果がわかります。 one-hotエンコーディングなので、一番大きな値のインデックスを出力します。 ついでに、答えも同時に出力しておきます。

pred_train = model.predict(X_train)
pred_test = model.predict(X_test)
pred_train = np.argmax(pred_train, axis=1)
pred_test = np.argmax(pred_test, axis=1)

print(pred_train)
print(np.argmax(y_train, axis=1))
print(pred_test)
print(np.argmax(y_test, axis=1))

結果 :

[1 2 2 2 2  ...  0 2 1 2 0]
[1 2 2 2 2  ...  0 2 1 2 0]
[2 1 0 2 0  ...  2 0 2 0 0]
[2 1 0 2 0  ...  2 0 2 0 0]

モデルの保存

機械学習のモデルを保存をすることは重要なので、保存についても説明します。 保存するときは、jsonファイル(またはyaml)でモデルを保存し、重みは別のファイルで保存します。

from keras.models import model_from_json

model_json = model.to_json()
with open('model.json', 'w') as file:
    file.write(model_json)

model.save_weights('weights.hdf5')

モデルの読み込み

読み込むときも似たような感じですが、model.evaluateを使うには、もう一回compileしないと、エラーが出ます。

with open('model.json', 'r') as file:
    model_json = file.read()
    model = model_from_json(model_json)

model.load_weights('weights.hdf5')

model.compile(loss='categorical_crossentropy', optimizer=Adam(), metrics=['accuracy'])
train_score = model.evaluate(X_train, y_train)
test_score = model.evaluate(X_test, y_test)
print('Train score:', train_score[0])
print('Train accuracy:', train_score[1])
print('Test score:', test_score[0])
print('Test accuracy:', test_score[1])

結果 :

Train score: 0.15385689522538867
Train accuracy: 0.9809523815200443
Test score: 0.18351854549513924
Test accuracy: 0.9777777791023254

model.compileの行を削除した時の結果 :

...
  File "/home/user/anaconda3/lib/python3.5/site-packages/keras/engine/training.py", line 1105, in evaluate
    batch_size=batch_size)
  File "/home/user/anaconda3/lib/python3.5/site-packages/keras/engine/training.py", line 680, in _standardize_user_data
    raise RuntimeError('You must compile a model before '
RuntimeError: You must compile a model before training/testing. Use `model.compile(optimizer, loss)`.

ソースコード全体

# -*- coding: utf-8 -*- #

import numpy as np
from keras.models import Sequential, model_from_json
from keras.layers.core import Dense
from keras.utils import to_categorical, np_utils
from keras.optimizers import Adam
from keras.callbacks import CSVLogger
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split

def keras_learning(X_train, X_test, y_train, y_test):
    # モデルの構築
    model = Sequential()
    model.add(Dense(input_dim=4, output_dim=100, activation='relu'))
    model.add(Dense(input_dim=100, output_dim=3, activation='softmax'))
    model.compile(loss='categorical_crossentropy', optimizer=Adam(), metrics=['accuracy'])

    # 訓練の開始
    csv_logger = CSVLogger('log.csv', separator=',', append=False)
    history = model.fit(X_train, y_train,
                        batch_size=32, epochs=100,
                        verbose=1,
                        validation_data=(X_test, y_test),
                        callbacks=[csv_logger])

    return model

def keras_predict(X_train, X_test, y_train, y_test, model):
    # モデルの評価(evaluate)
    train_score = model.evaluate(X_train, y_train)
    test_score = model.evaluate(X_test, y_test)
    print('Train score:', train_score[0])
    print('Train accuracy:', train_score[1])
    print('Test score:', test_score[0])
    print('Test accuracy:', test_score[1])

    # モデルの評価(predict)
    pred_train = model.predict(X_train)
    pred_test = model.predict(X_test)
    pred_train = np.argmax(pred_train, axis=1) # for one-hot
    pred_test = np.argmax(pred_test, axis=1) # for one-hot
    print(pred_train)
    print(np.argmax(y_train, axis=1))
    print(pred_test)
    print(np.argmax(y_test, axis=1))

def save_model(model):
    # モデルの保存
    model_json = model.to_json()
    with open('model.json', 'w') as file:
        file.write(model_json)
    model.save_weights('weights.hdf5')

def load_model():
    # モデルの読み込み
    with open('model.json', 'r') as file:
        model_json = file.read()
        model = model_from_json(model_json)
    model.load_weights('weights.hdf5')

    return model

def main():
    # データセットをロード
    iris = load_iris()
    print(iris.data)
    print(iris.feature_names)
    print(iris.target)
    print(iris.target_names)
    print(type(iris.data))
    print(type(iris.target))
    
    data_X = iris.data
    data_y = iris.target
    data_y = np_utils.to_categorical(data_y)
    print(data_X)
    print(data_y)

    X_train, X_test, y_train, y_test = train_test_split(data_X, data_y, test_size=0.3, random_state=0)
    print(X_train)
    print(y_train)
    print(X_test)
    print(y_test)

    model = keras_learning(X_train, X_test, y_train, y_test)
    keras_predict(X_train, X_test, y_train, y_test, model)
    save_model(model)
    model = load_model()
    model.compile(loss='categorical_crossentropy', optimizer=Adam(), metrics=['accuracy'])
    keras_predict(X_train, X_test, y_train, y_test, model)

if __name__ == '__main__':
    main()

まとめ

Kerasでのコーディングは簡単でいいですね。 PyTorchなども流行っているので、試して比較してみたいです。

いろいろしたいことがありますが、時間に余裕ができるまでは更新が遅くなりそうです。

参考文献

Google Play and the Google Play logo are trademarks of Google LLC.