はらぺこらいおん

日々、思ったことを。

Kerasで2種類(クラス)への分類

前回と前々回にセットアップしたKerasを使って2種類の分類を行ってみたいと思います。

前回と前々回のセットアップ記事はこちらからどうぞ。

pictzzz.hatenablog.com

pictzzz.hatenablog.com

概要

入力に対して2種類の分類を行う用途は意外と多く、有用だと思います。 例えば、性別、年齢、居住地、母国語、といった属性データに対して、 ある製品を購入した、していないの分類や、 テレマーケティングの結果、購入してくれるか、してくれないか、明日の株価は上がるのか下がるのかといったことなどなど、 様々な用途に使えると思います。

使用データ

今回は仮想的に以下のような構造を持ったcsvから学習を行うことを考えてみます。 このデータはテレマーケティングを行ってみて、購入したかどうかの結果がデータに反映されています。

#train.csv

ID,名前,性別,居住地,年齢,預金残高,契約年数,累積通話時間,購入したか
1,太郎,男,東京,20,100000,1,3,0
2,花子,女,大阪,40,1000000,10,10,1
・
・
・

このようなデータが1万レコードほどあると想定してください。
最後の購入したかは0が購入しなかった、1が購入したとなります。

対して、テストしてみるファイルには購入したかが入っていません。 イメージとしてはこれからどの人に優先的にアプローチをかけていけば良いかを 判断したいと考えてください。

#test.csv

ID,名前,性別,居住地,年齢,預金残高,契約年数,累積通話時間
1,一郎,男,北海道,51,11300000,4,5
2,さくら,女,沖縄,43,131000000,14,12
・
・
・

使用するモデル

多層パーセプトロンMLP)とします。Kerasではplotを利用することでモデルを画像として保存することができます。

from keras.utils.visualize_util import plot

plot(model, to_file='model.png')

実際に保存されたモデルがこちらです。

f:id:pictzzz:20170126141022p:plain

Denseと書かれているレイヤーは全結合レイヤー(fully connected layer)となります。

各レイヤーのユニット数は以下のようにしてます。
53(input) -> 500 -> 1000 -> 50 -> 2(out)

レイヤーの数やユニット数はいろいろ変えてみて、試してください。

入力、出力のユニット数

出力

今回は購入した(True)か購入しなかった(False)かの判定ですので、出力のユニット数は2となります。 出力ユニットが[1, 0]の場合、購入した、[0, 1]の場合、購入しなかったと判断します。

入力

入力は入力ユニットにデータを1つ1つ入れていきます。 ユニット数が53になっているのは、one hotエンコーディングでデータを入力するためです。

例えば、性別は、データの中に男性、女性の2種類がありますが、このままの状態ですと ネットワークで扱いにくいため、one hotエンコーディングというものを行います。

One-hot - Wikipedia

これを適用すると以下のようなデータに変換できます。

男性 = 10
女性 = 01

同じように年月の月のデータがある場合は、以下のように変換できます。

"jan" =  [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
"feb" =  [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
"mar" =  [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
"apr" =  [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
"may" =  [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
"jun" =  [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]
"jul" =  [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]
"aug" =  [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]
"sep" =  [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]
"oct" =  [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0]
"nov" =  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
"dec" =  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]

Kerasには自動でone hotエンコーディングを行ってくれる機能があります。 to_categoricalでone hotではないデータと分類するクラスの数(例えば月の場合は12)を指定すると、 one hotなデータを得られます。

from keras.utils import np_utils

nb_class = 12
one_hot_dataset = np_utils.to_categorical(not_one_hot_dataset, nb_classes)

その他テクニック

その他のテクニックとしては、正規化と中間値補完があります。

正規化では例えば預金残高が大きな数字になっていますのでこれを0〜1の間に収まるように、 入力データ全体の中で最大の預金残高で全ての預金残高の値を割ります。

中間値補完では例えば年齢の入力データの中に年齢が入っていない人がいる場合、 データを代表するような値(例えば、中間値)で代替することを行います。

データの前処理で用意するデータ

以上のような処理を行い、以下の5つのデータを準備します。

  • 学習用入力データ
  • 学習用教師データ
  • テスト用入力データ
  • テスト用教師データ
  • 新しい入力データ

学習用入力データと教師データ、テスト用入力データと教師データは対になっている必要があります。
学習用とテスト用のデータは同じ形式のものですが、学習で使ったデータと同じものでネットワークを評価してしまうと、 未知のデータに対する精度がわからないため、学習用データから一部を学習に使わずにテストに使う用として、 予め別に保持しておきます。 Kerasの場合は、テスト用データは別に準備しなくても学習用データの 一部を自動でテスト用データとして扱うことができます。 新しい入力データは、学習に使わず、答えが未知のデータとなります。このデータに対して、予測を行います。

モデルの作成

モデルは上に記載したように、いくつかレイヤーを重ねたものとしています。 Kerasでは簡単にモデルを変更することができます。

今回のモデルは以下のように記述します。

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

model = Sequential()

model.add(Dense(500, input_shape=(53,)))
model.add(Activation('sigmoid'))
model.add(Dropout(0.2))

model.add(Dense(1000))
model.add(Activation('sigmoid'))
model.add(Dropout(0.2))

model.add(Dense(50))
model.add(Activation('sigmoid'))
model.add(Dropout(0.2))

model.add(Dense(2))
model.add(Activation('softmax'))

活性化関数にはsigmoidとsoftmaxを使っています。 ドロップアウトには0.2を指定しています。ドロップアウトは指定された確率で各ユニットが無効になり、 ネットワークは無効になった以外のユニットだけで予測をする必要が出てきます。 これにより過学習という特定のデータに対して強く適合したネットワークになることを緩和できると言われています。

モデルを定義したらコンパイルを行います。今回は2つのユニットに出力を行うので損失関数にcategorical_crossentropyを指定します。 もし、1つのユニットに出力を行う場合は、binary_crossentropyを指定します。

最適化アルゴリズムにはadamを指定しました。 その他のアルゴリズムもKerasには定義されています。 詳しくは下記の公式URLを参照してみてください。 最適化 - Keras Documentation

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

学習

学習にあたり、過学習を防ぐ意味でも、アーリーストップを設定することができます。 アーリーストップは値が収束した時点で学習をストップするものになります。

from keras.callbacks import EarlyStopping

early_stopping = EarlyStopping(monitor='val_loss', patience=2)

Kerasの学習は以下のようにして行います。

hist = model.fit(x_train, y_train,
                 batch_size=batch_size,
                 verbose=1,
                 nb_epoch=nb_epoch,
                 validation_split=0.1,
                 callbacks=[early_stopping])

指定しているパラメーターの意味は以下のようになります。

  • x_train:学習データ
  • y_train:学習データの教師データ
  • batch_size:バッチサイズ(*)
  • verbose:ログ出力をプログレスバーで行う(0:出力なし、2:ログ出力)
  • nb_epoch:エポック数(全ての学習データに対するイテレーション数)
  • validation_split:学習データの中で検証に使うデータの割合
  • callbacks:学習中に行う処理のリスト(今回はアーリーストップを指定)

(*)バッチサイズ:複数のデータの誤差をまとめて、1回で更新を行う、ミニバッチ法の設定値になります。

評価

学習した結果の評価にはevaluateを使用します。

score = model.evaluate(x_test, y_test)

print('Test loss:', score[0])
print('Test accuracy:', score[1])

このように指定することでモデルの精度と誤差関数の値を出力することができます。

モデルの保存

学習済みのモデルの保存は以下のようにsaveを使います。

model.save('mlp_model.h5')

学習済みのモデルをロードする際には、load_modelを使います。

from keras.models import load_model

model = load_model('mlp_model.h5')
model.summary()

学習データの活用

学習したモデルに対して予測を行うにはpredictを使います。

y = model.predict(x)

print(y)

predictにはnumpy arrayで複数のデータを渡して、一度に予測することができます。

今回のモデルではpredictの出力は2つあり、[1, 0]が購入した(True)を示しているので、 y[i][0]がi番目の人が購入する確率を示すことになります。

i,y[0]
1,0.6803011298
2,0.6074721813
3,0.0003010118
4,0.0002940704
5,0.0291656982
6,0.0007526643
7,0.0005948166
8,0.0001619863

真理値で知りたい場合はroundで四捨五入を行うことで0 or 1の結果を得ることができます。