データアナリスト養成の道--keras学習におけるmetricsとcallbacksノート


complieのmetricsパラメータ
model.compile(...metrics=['accuracy'])

以上のようにmetricsにはargumentsが2つあり、y_true,y_predは、損失関数、(mseなど)と精度(accuracyなど)を受信し、トレーニングセットと検証セット(eg:loss:..acc..mae..val_loss:..val_acc..val_mae.)に作用します.もちろん、この性能の評価結果は実際に訓練には使用されず、(参考として)(各batch、出力:loss acc mae(後2つはmetricsを設定して得られる)、各epoch(1ラウンド)、出力:loss、acc mae...val_loss…val_acc… val_mae)metricsの損失関数と精度関数は、以下のように自分で書き換えることができます.
def top_3_accuracy(y_true,y_pred):
#  metrics  
    return metrics.top_k_categorical_accuracy(y_true,y_pred,k=3)

model.compile(loss='categorical_crossentropy',
              optimizer=sgd,
              metrics=[metrics.mae,top_3_accuracy])
import keras.backend as K
 #  metrics accuracy 
def mean_pred(y_true, y_pred):
    return K.mean(y_pred)

model.compile(optimizer='rmsprop',
              loss='binary_crossentropy',
              metrics=['accuracy', mean_pred])

fitのcallbacksパラメータ:
from keras.callbacks import ModelCheckpoint

model = Sequential()
model.add(Dense(10, input_dim=784, kernel_initializer='uniform'))
model.add(Activation('softmax'))
model.compile(loss='categorical_crossentropy', optimizer='rmsprop')

'''
saves the model weights after each epoch if the validation loss decreased
'''
checkpointer = ModelCheckpoint(filepath='/tmp/weights.hdf5', verbose=1, save_best_only=True)
model.fit(x_train, y_train, batch_size=128, epochs=20, verbose=0, validation_data=(X_test, Y_test), callbacks=[checkpointer])
class LossHistory(keras.callbacks.Callback):
    def on_train_begin(self, logs={}):
        self.losses = []

    def on_batch_end(self, batch, logs={}):
        self.losses.append(logs.get('loss'))

model = Sequential()
model.add(Dense(10, input_dim=784, kernel_initializer='uniform'))
model.add(Activation('softmax'))
model.compile(loss='categorical_crossentropy', optimizer='rmsprop')

history = LossHistory()
model.fit(x_train, y_train, batch_size=128, epochs=20, verbose=0, callbacks=[history])

print(history.losses)
# outputs
'''
[0.66047596406559383, 0.3547245744908703, ..., 0.25953155204159617, 0.25901699725311789]
'''

私にとってcallbacksは検証セットデータ評価の参考として以下のように便利です.fit_generatorのcallbacksパラメータはRocAucインタフェースを書きます
class RocAuc(keras.callbacks.Callback):
    def __init__(self,validation_generate,interval=1):
        self.interval=interval
        self.validation_generate=validation_generate
    def on_epoch_end(self,epoch, logs={}):
    #  epoch, 
        x_val,y_val=next(self.validation_generate)
        #print(y_val)
        if epoch % self.interval == 0:
            try:
                y_pred=self.model.predict(x_val,verbose=0)
                score=roc_auc_score(y_val,y_pred)
                print('
ROC_AUC - epoch:%d - score:%.6f
'
% (epoch + 1, score*100)) except: print('
epoch:%d only one class!!
'
% (epoch + 1)) def validata(iter_test): for index in iter_test: yield index vli=validata(iter_test) rocauc=RocAuc(validation_generate=vli) model.fit_generator(...callbacks=[rocauc])

もちろんearlyStopppingも入れますがroc_への転送が必要ですauc_valパラメータ、
class RocAuc(keras.callbacks.Callback):
    def __init__(self,validation_generate,interval=1):
        self.interval=interval
        self.validation_generate=validation_generate
    def on_train_begin(self,logs={}):
    # roc_auc_val 
        self.roc_auc_score=[]
        if not ('roc_auc_val' in self.params['metrics']):
            self.params['metrics'].append('roc_auc_val')
    def on_epoch_end(self,epoch, logs={}):
        logs['roc_auc_val']=float('-inf')
        x_val,y_val=next(self.validation_generate)
        #print(y_val)
        if epoch % self.interval == 0:
            try:
                y_pred=self.model.predict(x_val,verbose=0)
                score=roc_auc_score(y_val,y_pred)
                logs['roc_auc_val']=score*100
                print('
ROC_AUC - epoch:%d - score:%.6f
'
% (epoch + 1, score*100)) except: logs['roc_auc_val']='cant compute!!' print('
epoch:%d only one class!!
'
% (epoch + 1)) self.roc_auc_score.append(logs.get('roc_auc_val')) def validata(iter_test): for index in iter_test: yield index vli=validata(iter_test) model.fit_generator(...callbacks=[rocauc, EarlyStopping(monitor='roc_auc_val',patience=30, verbose=2,mode='max')]) print(rocauc.roc_auc_score)

もちろんplotを追加するbatch,epochのloss,acc出力傾向を表示することもできる.(たとえば、私たちのモデルが収束しているかどうかを見てみましょう(ソースデータが大きすぎる場合は、一部(.sample(xxx))を抽出して、モデルのLoss,val_を見てみましょう.loss,徐々に下がるかどうか(主にlossを見る)acc,val_accが徐々に成長しているかどうか、lossが平衡している/accが最大に達しているかどうか、変わらないかどうかは、learningRateを変えることを考慮することができます)
class  LossHistory(keras.callbacks.Callback):
    def __init__(self):
        self.loss={'count':[],'batch':[],'epoch':[]}
        self.acc={'count':[],'batch':[],'epoch':[]}
        self.val_loss={'count':[],'batch':[],'epoch':[]}
        self.val_acc={'count':[],'batch':[],'epoch':[]}
    def on_batch_end(self,batch,logs=[]):
        self.loss['batch'].append(logs.get('loss'))
        self.acc['batch'].append(logs.get('acc'))
        self.loss['count'].append(batch)
        self.acc['count'].append(batch)
    def on_epoch_end(self,epoch,logs=[]):
        self.val_loss['epoch'].append(logs.get('loss'))
        self.val_acc['epoch'].append(logs.get('acc'))
        self.val_loss['epoch'].append(logs.get('val_loss'))
        self.val_acc['epoch'].append(logs.get('val_acc'))
        self.val_loss['count'].append(epoch)
        self.val_acc['count'].append(epoch)
    def loss_plot(self,loss_type):
        iters=range(len(loss_type)-1)
        plt.figure()
        if loss_type=='batch':
            plt.plot(iters,self.acc[loss_type],'g',label='train_acc')
            plt.plot(iters,self.loss[loss_type],'b',label='train_loss')
        if loss_type=='epoch':
            plt.plot(iters,self.val_acc[loss_type],'r',label='val_acc')
            plt.plot(iters,self.val_loss[loss_type],'r',label='val_loss')
        plt.grid(True)
        plt.xlabel(loss_type)
        plt.ylabel('acc-loss')
        plt.legend(loc='upper right')
        plt.show()