ニューラルネットを用いた車の評価の推測
目的
階層型ニューラルネットをKerasで実装し, 実データに適用した学習の例を紹介する。
今回は, Kerasを用いて構築したニューラルネットのモデルを用いて, Car Evaluation Data Setから車の評価を推測してみる.
Car Evaluation Data Setには, それぞれの車が持つ6つの特徴と, その車に対する評価を示すラベルがセットとなったデータが1728個含まれている. このデータのうち半分を学習に用い, もう半分をテストデータとして検証に用いることとする.
各車が持つ6つの特徴は次の通り.
- buying(価格) とても高い, 高い, 普通, 安いの四段階
- maint(管理費) とても高い, 高い, 普通, 安いの四段階
- doors(ドアの数) 2枚, 3枚, 4枚, 5枚以上の四段階
- persons(乗員数) 2人, 4人, 5人以上の四段階
- lug_boot(載せられる荷物の量) 少ない, 普通, 多いの三段階
- safety(安全性) 高い, 普通, 低いの三段階
各車の評価を示すラベルは, unacceptable(良くない), acceptable(まあまあ), good(良い), very-good(とても良い)の四段階に分けられている.
さて, 今回はKerasを用いて次のようなネットワークを構築した.
二つの中間層では, 活性化関数としてsigmoid関数を用いている. また, 過学習を抑えるために, 活性化の前にBatchNormalizaionを挟んだほか, L1正則化によるパラメータの罰則項を損失関数に加えている. 出力層では, softmax関数を用いて, 4個あるクラスそれぞれに所属する確率(合計で1になる)を出力するようにしている. モデルの学習には, Adamを用い, 交差エントロピーを損失関数とする.
6つの特徴を入力するにあたって, 各入力値が-1.0から1.0の範囲に含まれるように調整する. 例えば, 車の価格は[とても高い, 高い, 普通, 安い]の四段階で示されているが, [-1.0, -0.33, 0.33, 1.0]の四段階の値に対応づけることとする.
これらの条件を用いて, L1正則化のハイパーパラメータを変化させながら, 864個の学習データを2000epochミニバッチで学習させた. 2000epochループさせた後の, 残りのテストデータ864個についてのテスト誤差と正答率は次のように変化した.
上のグラフがごとのテスト誤差の変化, 下のグラフがごとの正答率の変化である. この結果から, の時最も汎化性能が高くなったと判断できる.
とした時のepochごとの訓練誤差, テスト誤差, 訓練データとテストデータに対する正答率を示す.
を上記のように選んだところ, 学習誤差もテスト誤差もepochごとに減っていき, 過学習するような挙動は見られなかった.
今回の実験で用いたコードを以下に示す.
import tensorflow as tf import numpy as np import csv from keras.models import Sequential from keras.layers import Dense, Input, BatchNormalization, Activation from keras.activations import sigmoid, softmax from keras.regularizers import l1 from keras.optimizers import Adam from keras.utils import np_utils, plot_model import matplotlib.pyplot as plt information = [ ['vhigh', 'high', 'med', 'low'], ['vhigh', 'high', 'med', 'low'], ['2', '3', '4', '5more'], ['2', '4', 'more'], ['small', 'med', 'big'], ['low', 'med', 'high'], ] def convert_to_value(str_vec): size = len(str_vec) conv_dict = {} for i in range(size): conv_dict[str_vec[i]] = -1.0 + (2.0 / (size - 1)) * i return conv_dict def convert_to_data(row): data = [0] * (len(row) - 1) for i in range(len(row) - 1): data[i] = convert_to_value(information[i])[row[i]] label = {'unacc': 0 , 'acc': 1, 'good': 2, 'vgood': 3}[row[- 1]] label = np_utils.to_categorical(label, 4) return np.array(data), label def read_file(): f = open('car.data', 'r') reader = csv.reader(f) inputs = [] labels = [] for row in reader: data, label = convert_to_data(row) inputs.append(data) labels.append(label) f.close() train_size = int(len(inputs)) rand_idx = np.random.permutation(range(len(inputs))) inputs = np.array(inputs)[rand_idx] labels = np.array(labels)[rand_idx] return inputs[0:train_size], labels[0:train_size] def plot_loss_accuracy(fit, l1_lambda): fig, (loss_f, acc_f) = plt.subplots(2, 1, figsize=(15, 20)) loss_f.plot(fit.history['loss'], label="train") loss_f.plot(fit.history['val_loss'], label="test") loss_f.set_xlabel("epoch") loss_f.set_ylabel("loss") loss_f.legend() acc_f.plot(fit.history['acc'], label="train") acc_f.plot(fit.history['val_acc'], label="test") acc_f.set_xlabel("epoch") acc_f.set_ylabel("accuracy") acc_f.legend() filename = 'car_fit_plot' + str(l1_lambda) + '.png' fig.savefig(filename) plt.close() if __name__ == '__main__': train_inputs, train_labels = read_file() l1_lambda = 0.000001 model = Sequential() model.add(Dense(10, activity_regularizer=l1(l1_lambda), input_shape=(6,))) model.add(BatchNormalization()) model.add(Activation(sigmoid)) model.add(Dense(5, activity_regularizer=l1(l1_lambda))) model.add(BatchNormalization()) model.add(Activation(sigmoid)) model.add(Dense(4, activity_regularizer=l1(l1_lambda))) model.add(BatchNormalization()) model.add(Activation(softmax)) model.compile(optimizer=Adam(), loss='categorical_crossentropy', metrics=['accuracy']) model.summary() plot_model(model, to_file='model.png', show_shapes=True) fit = model.fit(train_inputs, train_labels, epochs=2000, validation_split=0.5) plot_loss_accuracy(fit, l1_lambda) <|| **まとめ