ManningのMEAP(出版前の本のできた部分から読めるサービス)でTensorFlowとTheanoの両方に対応したラッパーツールkerasの作者Francois Chollet氏の「Deep Learning with Python」が発売されました。
3章では、機械学習でよく使われる以下の手法をニューラルネットワークを使って実現する方法を紹介しています。
ここでは、最初の二値分類の例題をSageのjupyterノートブックで試しながら、Deep Learningへの理解を深めたいと思います。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
二値分類では、Sequential, Dense, to_categoricalを使用するため、最初にインポートしておきます。
from keras.models import Sequential
from keras.layers import Dense
from keras.utils.np_utils import to_categorical
kerasにいくつかのデータセットが組み込まれています。この内、二値分類ではIMDBのデータセットを使用します。
IMDBは、映画に対する感想を肯定的と否定的にラベル付けされたデータセットです。50,000のIMDB映画レビューのデータセット(単語をインデックス変換して加工済み)をkeras.datasets.imdbパッケージのload_data関数を使って取り込みます。25,000が学習用(train_data)、残りの25,000がテスト用(test_data)として使用します。
nb_wordsで使用する単語数を良く出てくる上位10000語に限定して、分析を行います。
from keras.datasets import imdb
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(nb_words=10000)
train_dataの最初のデータを見てみましょう。型はリストで、長さが218、リストの内容は単語のインデックスが整数値で入っています。
print len(train_data), len(test_data)
print type(train_data[0]), len(train_data[0])
np.array(train_data[0]).flatten()
ラベルには、肯定の場合1、否定の場合0がセットされています。またインデックスの最大値が9999ですから10000個の単語用のインデックスが入っていることも確認できました。
print train_labels[0:10]
max([max(sequence) for sequence in train_data])
どのような単語が映画のレビューに含まれていたかを確認するために、単語インデックスもロードしておきます。
# word_index is a dictionary mapping words to an integer indice
word_index = imdb.get_word_index()
# we reverse it, mapping integer indices to words
reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])
# we decode the review; note that our indices were offset by 3
# because 0, 1 and 2 are reserved indices for "padding", "start of sequence", and "unknown".
decoded_review = ' '.join([reverse_word_index.get(i - 3, '?') for i in train_data[0]])
最初のレビューの単語を表示みると、以下のようになりました。
print [reverse_word_index[idx] for idx in train_data[0]]
レビューに含まれている単語のインデックスを、インデックスが含まれている場合1、それ以外は0の長さ10000のベクトルに変えます。 それをすべてのレビューに適応してレビュー数x10000のマトリックスを作ります。
kerasへの入力データは、astype関数でfloat32の実数に変換します。
def vectorize_sequences(sequences, dimension=10000):
results = np.zeros((len(sequences), dimension))
# create an all-zero matrix of shape (len(sequences), dimension)
for i, sequence in enumerate(sequences):
results[i, sequence] = 1. # set specific indices of results[i] to 1s
return results
# our vectorized training data
x_train = vectorize_sequences(train_data)
# our vectorized test data
x_test = vectorize_sequences(test_data)
# our vectorized labels:
y_train = np.asarray(train_labels).astype('float32')
y_test = np.asarray(test_labels).astype('float32')
モデルは3層から成り、最初の2層に16個の隠しユニットを設け、活性化関数をReLuとし、最後に二値化します。
model = Sequential()
model.add(Dense(16, activation='relu', input_dim=10000))
model.add(Dense(16, activation='relu'))
model.add(Dense(1, activation='sigmoid'))
model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy'])
モデルができたので、25,000のデータの内10,000を検証用に使用します。
x_val = x_train[:10000]
partial_x_train = x_train[10000:]
y_val = y_train[:10000]
partial_y_train = y_train[10000:]
モデルを学習用データで訓練します。最初のエポックには時間が掛かりますが、それ以降は短時間で計算します。
history = model.fit(partial_x_train, partial_y_train, nb_epoch=20, batch_size=512, validation_data=(x_val, y_val))
history_dict = history.history
history_dict.keys()
例題ではmatplotlibを使って収束の様子を可視化していますが、ここではpandasのグラフ機能を使って学習用データの損失関数の値lossと検証用データの損失関数の値val_lossをプロットしてみます。
グラフをカッコよく表示するために、seabornをインポートしています。
import seaborn
# history_dictをpandasnのデータフレームに変換
d = pd.DataFrame(history_dict)
# loss, val_lossをプロット
d[['loss', 'val_loss']].plot()
plt.show()
同様に精度(acc, val_acc)についてもプロットしてみます。
d[['acc', 'val_acc']].plot()
plt.show()
loss, val_lossのグラフからエポック値が4を超えると検証用データの損失関数の値が大きくなり、過学習の様相を呈してきます。 acc, val_accからはエポック値が4でも88%程度の精度が確保できそうだと期待できます。
テスト用のデータでモデルを性能を検証するために、nb_epoch=4で計算したモデルを準備し、evaluateメソッドを使ってテスト用データについて肯定/否定の評価を予想してみましょう。
model = Sequential()
model.add(Dense(16, activation='relu', input_dim=10000))
model.add(Dense(16, activation='relu'))
model.add(Dense(1, activation='sigmoid'))
model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy'])
model.fit(x_train, y_train, nb_epoch=4, batch_size=512)
results = model.evaluate(x_test, y_test)
results
芸術的な域では精度は95%になることから、88%の精度は、簡単な例としてはまあまあの値だと思います。
model.predict(x_test)
同じ処理をScikit-Learnのロジスティック回帰(LogisticRegression)で計算してみましょう。
最初に、sklearn.linear_modelからLogisticRegressionをモデル(Model)としてインポートします。 次に、modelを生成して、学習用データで訓練します。計算はkerasの場合よりも長く掛かります。
from sklearn.linear_model import LogisticRegression as Model
model = Model()
model.fit(x_train, y_train)
計算の尺度は、metricsパッケージを使って求めます。
精度は、86%とニューラルネットと同等の結果となりました。
import sklearn.metrics as metrics
predicted = model.predict(x_test)
print metrics.confusion_matrix(y_test, predicted)
print metrics.accuracy_score(y_test, predicted)