Keras : Ex-Tutorials : Keras でアヤメ分類 (scikit-learn との比較) (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 06/17/2018 (2.2.0)
* 本ページは、Keras 開発チーム推奨の外部チュートリアル・リソースの一つ : `”Hello world” in keras` を
翻訳した上でまとめ直して補足説明したものです:
* サンプルコードの動作確認はしておりますが、適宜、追加改変している場合もあります。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
Keras は高位ニューラルネット・ライブラリで、TensorFlow バックエンド回りの (scikit-learn の API に類似した) API をラップします。scikit-learn との類似性を考えて、そして今ではニューラルネット設計とテストに総ての人がアクセス可能であることを強調するために、scikit-learn と明示的に比較することにより Keras をどのように使用するかの簡単なチュートリアルとします。
Scikit-learn は Python 開発者により使用される最もポピュラーな古典的機械学習ライブラリです。Scikit-learn についての多くの素晴らしいものの中でも特筆すべきはその単純で首尾一貫した API で、これは Estimator オブジェクト回りに構築されています。この API は機械学習ワークフローの良い記述で、それはパッケージを通して首尾一貫して適用されます。
必要なライブラリをインポートすることから始めます : scikit-learn、Keras そして幾つかのプロット用ライブラリです :
import seaborn as sns import numpy as np from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegressionCV from keras.models import Sequential from keras.layers.core import Dense, Activation from keras.utils import np_utils
Using TensorFlow backend.
アイリス・データ
有名な アイリス・データセット (published by Ronald Fisher in 1936) は機械学習フレームワークの API をデモする良い方法です。ある意味、それは機械学習の “Hello world” です。
データは単純で、極めて単純な分類器で高い精度を得ることが可能です。従ってこの問題を解くためにニューラルネットを使用することは大仰です。しかしこれで良いのです!目標は、モデル設計や選択の詳細ではなくデータから動作する分類器を得るために必要なコードを探検することです。
アイリス・データセットは多くの機械学習ライブラリに組み込まれています。seaborn のコピーは容易に可視化可能なラベル付けられた dataframe として扱われますので、seaborn を使用します、さてそれをそこからロードして最初の 5 サンプルを見てみましょう。
iris = sns.load_dataset("iris") iris.head()
各サンプル (i.e., 花) について、データの 5 つのピースがあります。4 つは花のサイズ (センチ) の標準的寸法で、5 番目はアイリスの種です。3 つの種: セトサ、バージカラー、バージニカがあります。私達のジョブは 2 つの花弁 (= petal) と 2 つのがく片 (= sepal) の寸法が与えられたとき、アイリスの種を予測できる分類器を構築することです。モデル構築を始める前に素早い可視化を行なってみましょう (常に良い考えです!) :
sns.pairplot(iris, hue='species');
訓練とテストのためにデータをマンジングして分割する
最初にアイリス dataframe から生データを引っ張り出す必要があります。花弁とがく片データを配列 X に、種のラベルを対応する配列 y に保持します。
X = iris.values[:, :4] y = iris.values[:, 4]
教師あり機械学習では標準的ですが、データの幾つかで訓練して残りでモデルのパフォーマンスを測定します。これは手動で行なうのも単純ですが、train_test_split() 関数のように scikit-learn にも組み込まれています。
train_X, test_X, train_y, test_y = train_test_split(X, y, train_size=0.5, test_size=0.5, random_state=0)
scikit-learn 分類器を訓練する
ロジスティック回帰分類器を訓練します。組み込みハイパーパラメータ交差検証でこれを行なうことは scikit-learn で 1 行です。総ての scikit-learn Estimator オブジェクトのように、LogisticRegressionCV 分類器は .fit() メソッドを持ちます、これは訓練データのベストに適合するモデルパラメータを学習する不愉快な (= gory) 数値的詳細をケアします。そこでそのメソッドが行なうことの総てです :
lr = LogisticRegressionCV() lr.fit(train_X, train_y)
LogisticRegressionCV(Cs=10, class_weight=None, cv=None, dual=False, fit_intercept=True, intercept_scaling=1.0, max_iter=100, multi_class='ovr', n_jobs=1, penalty='l2', random_state=None, refit=True, scoring=None, solver='lbfgs', tol=0.0001, verbose=0)
精度を用いて分類器にアクセス
今テストセットの断片を訓練された分類器が正しく分類するか (i.e., 精度) を測定できます。
print("Accuracy = {:.2f}".format(lr.score(test_X, test_y)))
Accuracy = 0.83
今 Keras で非常に類似したことを行ないます
丁度見たように、scikit-learn は分類器の構築を非常に単純にします :
- 分類器をインスタンス化するために 1 行。
- それを訓練するために 1 行。
- そしてそのパフォーマンスを測定するために 1 行。
分類器の構築は Keras ではほんの少しだけ複雑です。データマンジングは少しだけ変わり、ネットワークをそれを分類器としてインスタンス化する前に何某かのワークを行なう必要がありますが、それ以外は scikit-learn で作業することに非常に似ています。
最初にデータマンジングを少々: scikit-learn の分類器は文字列ラベル e.g., “setosa” を受け取りました。しかし Keras は one-hot エンコードされていることを必要とします。これは次のように見えるデータを :
setosa versicolor setosa virginica ...
次のように見えるテーブルに変換する必要があることを意味します :
setosa versicolor virginica 1 0 0 0 1 0 1 0 0 0 0 1
これを行なう多くの方法があります。もし貴方が pandas に慣れていれば pandas.get_dummies() があり、one-hot-encoding は scikit-learn にあります。ここでは単に Keras ユティリティと numpy を使用します。
def one_hot_encode_object_array(arr): '''One hot encode a numpy array of objects (e.g. strings)''' uniques, ids = np.unique(arr, return_inverse=True) return np_utils.to_categorical(ids, len(uniques)) train_y_ohe = one_hot_encode_object_array(train_y) test_y_ohe = one_hot_encode_object_array(test_y)
ニューラルネットワーク・モデルを構築する
この特定のケースで必要とされるデータ・マンジングを別にすれば、Keras で作業する最も本質的で重要な違いはモデルの構造をそれをインスタンス化して使用する前に指定しなければならないことです。
scikit-learn ではモデルは既成品です。しかし Keras はニューラルネットワーク・ネットワーク・ライブラリです。そのようなものとして、データの特徴/クラスの数が制約を与える一方で、モデル構造の総ての他の局面を決定することができます : 層の数、層のサイズ、層の間の接続の性質、etc.
私達のケースでは、極めて単純なネットワークを構築します。データにより 2 つの選択が成されます。4 つの特徴と 3 つのクラスを持ちますので、入力層は 4 ユニットを、出力層は 3 ユニットを持たなければなりません。隠れ層だけを定義しなければなりません。このプロジェクトのためには1 つの隠れ層だけを持ち、それに 16 ユニットを与えます。
モデルを最も一般的な方法で定義します : 層のシーケンシャルなスタックとして :
model = Sequential()
次の 2 行は入力層サイズ (input_shape=(4,) と隠れ層のサイズと活性化関数を定義します。
model.add(Dense(16, input_shape=(4,))) model.add(Activation('sigmoid'))
… そして次の行は出力層のサイズと活性化関数を定義します。
model.add(Dense(3)) model.add(Activation('softmax'))
最後に最適化するために最適化ストラテジーと損失関数を指定します。またモデルにそれが動作しているときに精度を計算することも指示します。
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=["accuracy"])
ニューラルネットワーク分類器を使用する
構造 model を定義してそれをコンパイルしましたので、API が scikit-learn の分類器に殆ど同一のオブジェクトを持ちました。特に、それは .fit() と .predict() メソッドを持ちます。fit しましょう。
ニューラルネットワークの訓練はしばしば “ミニバッチ処理” の概念を伴います、これはデータのサブセットをネットワークに見せて、重みを調整し、そしてそれからデータの他のサブセットに見せることを意味します。ネットワークが総てのデータを一度見たとき、それは “エポック” と呼ばれます。
ミニバッチ/エポック・ストラテジーの調整は幾分問題に依りますが、この場合 1 のミニバッチを使用します。これはそれを効率的に良くて古い確率的勾配降下にします、i.e. データはネットワークに一度に 1 つの花が見せられ、重みは直ちに調整されます。
model.fit(train_X, train_y_ohe, epochs=100, batch_size=1, verbose=1);
Epoch 1/100 75/75 [==============================] - 0s 3ms/step - loss: 0.1238 - acc: 0.9867 Epoch 2/100 75/75 [==============================] - 0s 832us/step - loss: 0.1254 - acc: 0.9467 Epoch 3/100 75/75 [==============================] - 0s 865us/step - loss: 0.1272 - acc: 0.9600 Epoch 4/100 75/75 [==============================] - 0s 647us/step - loss: 0.1188 - acc: 0.9867 Epoch 5/100 75/75 [==============================] - 0s 684us/step - loss: 0.1216 - acc: 0.9600 Epoch 6/100 75/75 [==============================] - 0s 675us/step - loss: 0.1192 - acc: 0.9600 Epoch 7/100 75/75 [==============================] - 0s 669us/step - loss: 0.1152 - acc: 0.9733 Epoch 8/100 75/75 [==============================] - 0s 696us/step - loss: 0.1220 - acc: 0.9867 Epoch 9/100 75/75 [==============================] - 0s 715us/step - loss: 0.1164 - acc: 0.9733 Epoch 10/100 75/75 [==============================] - 0s 791us/step - loss: 0.1148 - acc: 0.9600 Epoch 11/100 75/75 [==============================] - 0s 659us/step - loss: 0.1145 - acc: 0.9733 Epoch 12/100 75/75 [==============================] - 0s 763us/step - loss: 0.1130 - acc: 0.9733 Epoch 13/100 75/75 [==============================] - 0s 660us/step - loss: 0.1086 - acc: 0.9733 Epoch 14/100 75/75 [==============================] - 0s 687us/step - loss: 0.1074 - acc: 0.9867 Epoch 15/100 75/75 [==============================] - 0s 743us/step - loss: 0.1117 - acc: 0.9867 Epoch 16/100 75/75 [==============================] - 0s 714us/step - loss: 0.1058 - acc: 0.9600 Epoch 17/100 75/75 [==============================] - 0s 704us/step - loss: 0.1048 - acc: 0.9867 Epoch 18/100 75/75 [==============================] - 0s 724us/step - loss: 0.1035 - acc: 0.9867 Epoch 19/100 75/75 [==============================] - 0s 899us/step - loss: 0.1053 - acc: 0.9600 Epoch 20/100 75/75 [==============================] - 0s 857us/step - loss: 0.1036 - acc: 0.9733 Epoch 21/100 75/75 [==============================] - 0s 766us/step - loss: 0.1013 - acc: 0.9733 Epoch 22/100 75/75 [==============================] - 0s 732us/step - loss: 0.0995 - acc: 0.9733 Epoch 23/100 75/75 [==============================] - 0s 769us/step - loss: 0.0989 - acc: 0.9733 Epoch 24/100 75/75 [==============================] - 0s 663us/step - loss: 0.0997 - acc: 0.9733 Epoch 25/100 75/75 [==============================] - 0s 656us/step - loss: 0.0980 - acc: 0.9733 Epoch 26/100 75/75 [==============================] - 0s 694us/step - loss: 0.0992 - acc: 0.9867 Epoch 27/100 75/75 [==============================] - 0s 738us/step - loss: 0.0991 - acc: 0.9733 Epoch 28/100 75/75 [==============================] - 0s 753us/step - loss: 0.0967 - acc: 0.9600 Epoch 29/100 75/75 [==============================] - 0s 832us/step - loss: 0.0942 - acc: 0.9867 Epoch 30/100 75/75 [==============================] - 0s 869us/step - loss: 0.1007 - acc: 0.9733 Epoch 31/100 75/75 [==============================] - 0s 749us/step - loss: 0.0955 - acc: 0.9867 Epoch 32/100 75/75 [==============================] - 0s 786us/step - loss: 0.0917 - acc: 0.9867 Epoch 33/100 75/75 [==============================] - 0s 717us/step - loss: 0.0911 - acc: 0.9733 Epoch 34/100 75/75 [==============================] - 0s 743us/step - loss: 0.0931 - acc: 0.9733 Epoch 35/100 75/75 [==============================] - 0s 684us/step - loss: 0.0893 - acc: 0.9600 Epoch 36/100 75/75 [==============================] - 0s 764us/step - loss: 0.0913 - acc: 0.9867 Epoch 37/100 75/75 [==============================] - 0s 683us/step - loss: 0.0913 - acc: 0.9733 Epoch 38/100 75/75 [==============================] - 0s 737us/step - loss: 0.0908 - acc: 0.9733 Epoch 39/100 75/75 [==============================] - 0s 713us/step - loss: 0.0890 - acc: 0.9733 Epoch 40/100 75/75 [==============================] - 0s 771us/step - loss: 0.0903 - acc: 0.9733 Epoch 41/100 75/75 [==============================] - 0s 687us/step - loss: 0.0872 - acc: 0.9867 Epoch 42/100 75/75 [==============================] - 0s 741us/step - loss: 0.0865 - acc: 0.9600 Epoch 43/100 75/75 [==============================] - 0s 654us/step - loss: 0.0859 - acc: 1.0000 Epoch 44/100 75/75 [==============================] - 0s 683us/step - loss: 0.0878 - acc: 0.9600 Epoch 45/100 75/75 [==============================] - 0s 784us/step - loss: 0.0833 - acc: 0.9867 Epoch 46/100 75/75 [==============================] - 0s 730us/step - loss: 0.0859 - acc: 0.9867 Epoch 47/100 75/75 [==============================] - 0s 747us/step - loss: 0.0850 - acc: 0.9867 Epoch 48/100 75/75 [==============================] - 0s 683us/step - loss: 0.0826 - acc: 0.9867 Epoch 49/100 75/75 [==============================] - 0s 740us/step - loss: 0.0830 - acc: 0.9733 Epoch 50/100 75/75 [==============================] - 0s 825us/step - loss: 0.0853 - acc: 0.9733 Epoch 51/100 75/75 [==============================] - 0s 699us/step - loss: 0.0860 - acc: 0.9867 Epoch 52/100 75/75 [==============================] - 0s 662us/step - loss: 0.0797 - acc: 0.9600 Epoch 53/100 75/75 [==============================] - 0s 738us/step - loss: 0.0809 - acc: 0.9867 Epoch 54/100 75/75 [==============================] - 0s 673us/step - loss: 0.0812 - acc: 0.9733 Epoch 55/100 75/75 [==============================] - 0s 678us/step - loss: 0.0853 - acc: 0.9733 Epoch 56/100 75/75 [==============================] - 0s 689us/step - loss: 0.0815 - acc: 0.9600 Epoch 57/100 75/75 [==============================] - 0s 658us/step - loss: 0.0791 - acc: 0.9733 Epoch 58/100 75/75 [==============================] - 0s 656us/step - loss: 0.0793 - acc: 0.9867 Epoch 59/100 75/75 [==============================] - 0s 668us/step - loss: 0.0795 - acc: 0.9733 Epoch 60/100 75/75 [==============================] - 0s 697us/step - loss: 0.0777 - acc: 0.9733 Epoch 61/100 75/75 [==============================] - 0s 675us/step - loss: 0.0760 - acc: 0.9867 Epoch 62/100 75/75 [==============================] - 0s 648us/step - loss: 0.0744 - acc: 0.9867 Epoch 63/100 75/75 [==============================] - 0s 659us/step - loss: 0.0758 - acc: 0.9867 Epoch 64/100 75/75 [==============================] - 0s 646us/step - loss: 0.0749 - acc: 1.0000 Epoch 65/100 75/75 [==============================] - 0s 675us/step - loss: 0.0749 - acc: 0.9733 Epoch 66/100 75/75 [==============================] - 0s 711us/step - loss: 0.0800 - acc: 0.9733 Epoch 67/100 75/75 [==============================] - 0s 664us/step - loss: 0.0794 - acc: 0.9867 Epoch 68/100 75/75 [==============================] - 0s 699us/step - loss: 0.0787 - acc: 0.9600 Epoch 69/100 75/75 [==============================] - 0s 650us/step - loss: 0.0717 - acc: 0.9867 Epoch 70/100 75/75 [==============================] - 0s 654us/step - loss: 0.0766 - acc: 0.9867 Epoch 71/100 75/75 [==============================] - 0s 872us/step - loss: 0.0738 - acc: 0.9867 Epoch 72/100 75/75 [==============================] - 0s 783us/step - loss: 0.0724 - acc: 0.9867 Epoch 73/100 75/75 [==============================] - 0s 743us/step - loss: 0.0723 - acc: 0.9867 Epoch 74/100 75/75 [==============================] - 0s 757us/step - loss: 0.0718 - acc: 0.9867 Epoch 75/100 75/75 [==============================] - 0s 693us/step - loss: 0.0711 - acc: 0.9867 Epoch 76/100 75/75 [==============================] - 0s 670us/step - loss: 0.0719 - acc: 0.9867 Epoch 77/100 75/75 [==============================] - 0s 759us/step - loss: 0.0729 - acc: 0.9867 Epoch 78/100 75/75 [==============================] - 0s 759us/step - loss: 0.0702 - acc: 0.9867 Epoch 79/100 75/75 [==============================] - 0s 728us/step - loss: 0.0742 - acc: 0.9867 Epoch 80/100 75/75 [==============================] - 0s 752us/step - loss: 0.0732 - acc: 0.9867 Epoch 81/100 75/75 [==============================] - 0s 970us/step - loss: 0.0716 - acc: 0.9867 Epoch 82/100 75/75 [==============================] - 0s 725us/step - loss: 0.0698 - acc: 0.9733 Epoch 83/100 75/75 [==============================] - 0s 724us/step - loss: 0.0716 - acc: 0.9600 Epoch 84/100 75/75 [==============================] - 0s 672us/step - loss: 0.0712 - acc: 0.9867 Epoch 85/100 75/75 [==============================] - 0s 813us/step - loss: 0.0682 - acc: 0.9867 Epoch 86/100 75/75 [==============================] - 0s 755us/step - loss: 0.0684 - acc: 0.9867 Epoch 87/100 75/75 [==============================] - 0s 686us/step - loss: 0.0680 - acc: 0.9867 Epoch 88/100 75/75 [==============================] - 0s 663us/step - loss: 0.0706 - acc: 0.9733 Epoch 89/100 75/75 [==============================] - 0s 662us/step - loss: 0.0707 - acc: 0.9733 Epoch 90/100 75/75 [==============================] - 0s 649us/step - loss: 0.0677 - acc: 0.9867 Epoch 91/100 75/75 [==============================] - 0s 765us/step - loss: 0.0666 - acc: 0.9867 Epoch 92/100 75/75 [==============================] - 0s 771us/step - loss: 0.0686 - acc: 0.9867 Epoch 93/100 75/75 [==============================] - 0s 743us/step - loss: 0.0635 - acc: 0.9867 Epoch 94/100 75/75 [==============================] - 0s 734us/step - loss: 0.0698 - acc: 0.9733 Epoch 95/100 75/75 [==============================] - 0s 785us/step - loss: 0.0653 - acc: 0.9867 Epoch 96/100 75/75 [==============================] - 0s 803us/step - loss: 0.0639 - acc: 0.9867 Epoch 97/100 75/75 [==============================] - 0s 712us/step - loss: 0.0640 - acc: 0.9867 Epoch 98/100 75/75 [==============================] - 0s 667us/step - loss: 0.0632 - acc: 0.9867 Epoch 99/100 75/75 [==============================] - 0s 655us/step - loss: 0.0673 - acc: 0.9867 Epoch 100/100 75/75 [==============================] - 0s 663us/step - loss: 0.0676 - acc: 0.9867
基本的な使用については、コンパイルされた keras モデルと scikit-learn 分類器間の唯一のシンタクス的な API の違いは scikit-learn .score() メソッドの Keras の同値は .evaluate() と呼称されます。
evaluate() はモデルをコンパイルするときに要求した損失関数と任意の他のメトリクスを返します。私達のケースでは、精度を要求しました、これは scikit-learn LogisticRegressionCV 分類器の .score() メソッドからの精度と比較することができます。
loss, accuracy = model.evaluate(test_X, test_y_ohe, verbose=0) print("Accuracy = {:.2f}".format(accuracy))
Accuracy = 0.97
見て取れるように、ニューラルネットワーク・モデルのテスト精度は単純なロジスティック回帰分類器のものよりも良いです。
これは再確認させてくれますが、驚くべきことではありません。非常に単純なニューラルネットワークでさえもロジスティック回帰よりも遥かにより複雑な分類面を学習する柔軟性を持ちますので、それはもちろんロジスティック回帰よりも上手くやります。
そしてニューラルネットワークの一つの危険性のヒントを与えます: overfitting です。ここではテストセットを注意深く保持してそれでパフォーマンスを測定しましたが、97 % 精度は酷く高いように見えますので、何某かの overfitting が進んでいても驚きではありません。(Keras に組み込まれている) dropout の追加でそれに取り組めるかもしれません。それは LogisticRegression 分類器が使用する正則化のニューラルネットワークの同値です。
しかしここでは深入りはしません、というのはニューラルネットワーク・モデルはこの問題についてはやり過ぎですし、精度について心配するのは貴方に見せたいことのポイントではないからです。ポイントは Keras のような高位ライブラリの使用はニューラルネットワーク・モデルを構築し、訓練し、そして適用するために (伝統的なモデルよりも) ほんの少しだけより多くのコードだけを書く必要があることを意味することです。
以上