kerasを使ってk分割交差検証をやってみたので、その方法を備忘録として整理しておきます。kerasにはk分割交差検証の機能が用意されていないので、自分で作ってやる必要があります。
k分割交差検証にはsklearnモジュールを使う
kerasにはk分割交差検証機能がありませんが、scikit-learnモジュールにその機能があるのでそれを利用します。
scikit-learnをインストールしていない場合は、インストールしておきましょう。
from sklearn.model_selection import KFold
from sklearn.model_selection import train_test_split
X_train,X_test,Y_train,Y_test =train_test_split(X,Y,test_size=0.2)
まずは、データを学習データと教師データに分けます。この時に活躍するのがsklearnのtrain_test_split。教師データの割合を指定するだけでランダムにデータを分けてくれます。
次にk交差分割検証をしたいニューラルネットワークのモデルを定義しておきます。
def build_model():
model=models.Sequential()
model.add(layers.Dense(96,activation='relu',input_shape=(78,)))
model.add(layers.BatchNormalization())
model.add(layers.Dropout(0.4))
model.add(layers.Dense(96,activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.Dropout(0.4))
model.add(layers.Dense(4,activation='softmax'))
sgd = optimizers.RMSprop(lr=0.001)
model.compile(optimizer=sgd,loss='categorical_crossentropy',metrics=['acc'])
return model
kFoldを使う
学習データと教師データを作ったら、さっそくk分割交差検証用のバリデーション(検証)データを作ります。
ここで活躍するのがsklearnのkFold!!
kf = KFold(n_splits=3, shuffle=True)
all_loss=[]
all_val_loss=[]
all_acc=[]
all_val_acc=[]
ep=300
for train_index, val_index in kf.split(X_train,Y_train):
train_data=X_train[train_index]
train_label=Y_train[train_index]
val_data=X_train[val_index]
val_label=Y_train[val_index]
model=build_model()
history=model.fit(train_data,
train_label,
epochs=ep,
batch_size=8,
validation_data=(val_data,val_label))
loss=history.history['loss']
val_loss=history.history['val_loss']
acc=history.history['acc']
val_acc=history.history['val_acc']
all_loss.append(loss)
all_val_loss.append(val_loss)
all_acc.append(acc)
all_val_acc.append(val_acc)
ave_all_loss=[
np.mean([x[i] for x in all_loss]) for i in range(ep)]
ave_all_val_loss=[
np.mean([x[i] for x in all_val_loss]) for i in range(ep)]
ave_all_acc=[
np.mean([x[i] for x in all_acc]) for i in range(ep)]
ave_all_val_acc=[
np.mean([x[i] for x in all_val_acc]) for i in range(ep)]
何がどうなっているのかわからないと思うので説明します。
まず最初にデータを何分割するか指定します。n_splitsは分割回数、今回は3分割にしました。shuffle=Trueでデータをランダムに分けてくれます。
kf = KFold(n_splits=3, shuffle=True)
そして
for train_index, val_index in kf.split(X_train,Y_train):
for文でこんな風にしてやると、3回ループが完成します。
k分割交差検証は、ループごとに学習データとバリデーションデータが異なる仕組みですが、kf.splitによる3回ループはそれに対応してくれています。
kf.splitは、ループごとに学習データとバリデーションデータのインデックス番号を出力してくれます。それらを「train_index」「val_index」として、for文で利用します。
train_data=X_train[train_index]
train_label=Y_train[train_index]
val_data=X_train[val_index]
val_label=Y_train[val_index]
numpyで上のように[]内にインデックス番号のリストを入れてやると、「0番目、10番目、13番目、20番目のデータを抽出!」といった感じで学習データと教師データをしっかりと作ることができます。
これだけでk交差分割検証のデータは完成です。
あとは、通常の機械学習と同じです。モデルにデータを入れて学習させて学習結果を考察します。
k分割交差検証の結果をどう扱うか
k分割交差検証を実践できたとして、その結果をどう扱えば良いのでしょうか。私はここで少し挫折しかけました・・・。
その扱い方が
loss=history.history['loss']
より下のコードとなっていますが、わかりにくいので補足説明しておきます。
何をしているのかというと、各エポックごとに3回ループの平均値を取得しています。
例えば、100回目のエポック時に
acc:0.91
loss:0.03
acc:0.95
loss:0.02
acc:0.89
loss:0.04
だとすれば、その平均値は
acc:0.916666
loss:0.03
となりますが、この平均値を1エポック目〜300エポック目分まで全て求めています。
そして、accとlossの平均値を見て、作成したモデルを評価するわけです。
何を評価するかというと、
を私は見ました。
・・・が、ここで私は悩みました。
「いい感じの数値が出たのはいいけど、見てるのはループ全体の平均値だし、これと同じ数値が出力できるモデルって結局作れないよね?」と。ループごとに損失関数(loss)のバラツキが大きいと特に感じます。
そこで私は、k分割交差検証でデータを確認した後、通常のホールドアウト法でモデルを作り直すことにしました。
k分割交差検証は学習データが少ない場面で使われることが多く、少ない学習データでホールドアウト法を用いると結果にバラツキが生じやすくなります。
なので、k分割交差検証でわかっている平均値よりいい数値が出るまでホールドアウト法でモデルを作り続けます。(あまり数値が良すぎると過学習の可能性があるので、あくまで平均値付近の数値を目安にします。)そして、いい感じの数値でモデルを作れたら、そのモデルを実戦投入してみるというのが私のk分割交差検証の使い方です。
・・・この使い方があっているのかわかりませんが、k分割交差検証を使うことで結果のバラツキ具合を事前に把握できるので、モデルを作りやすくなります。
k分割交差検証で計算された平均値は信用できるのか
最後に1つ疑問に思ったのは、「k分割交差検証で計算されたaccとかlossの値ってそもそも信用できるの?」って話です。
k分割交差検証は、4回前後のループですることが多いような気がしていますが、たった4回の平均値を信用して良いのでしょうか。
これについては正直わかりませんでした。しかし、ちょっと言い方がややこしいですが、「4回ループで平均値を求める処理を3回繰り返して、3回の平均値の平均値を信じる」って方法はありなんじゃないかと思いました。ループ回数自体を増やす方がシンプルなんですが、ループ数を増やすと、バリデーションデータがだんだん少なくなるのでダメだなと考えました。
つまりは、処理ごとに数値にバラツキのあるaccとlossの母平均と母分散を求めたいわけです。
だとすれば、統計学でいう「中心極限定理」が適用できるので標本数を増やしてやれば、その平均値はだんだん母平均に近づくんじゃないかと思ったわけです。でも、ループ数はむやみに増やせないので、「3回ループを4回繰り返す(計12回)」みたいな回りくどい方法になると。
以上、k分割交差検証についてのメモ・考察でした。最後の方は我流なので正しいかどうかわかりません。プロの人たちがどのようにデータ処理をしているのか気になります。あと、統計についての知識が乏しいので、もう少し実践の中で統計を学ぶ必要を感じました。
コメント