【问题标题】:F1 score for padded outputs in KerasKeras 中填充输出的 F1 分数
【发布时间】:2018-09-24 07:48:45
【问题描述】:

我在 Keras 中有一个 LSTM 序列标记器,用于高度不平衡的数据。因此,我想使用(多类)F1 分数作为模型的主要指标。我有两个问题:

1) 我在数据中使用零填充(因此在我的嵌入中使用mask_zero=True),并且所有损失都是针对掩码数据自动计算的。但是,我认为必须手动完成自定义指标计算的屏蔽?有没有有效的矢量化解决方案?

2) 是否可以将sklearn's f1_score implementation 传递给模型的compile(可能在以某种方式包装之后)?马上,它不起作用,因为显然是一个占位符而不是一个 numpy 数组(我使用 tensorflow 后端..)传递给它。

[UPD] 鉴于我的实现,现在有一个问题:我不确定是否有可能同时屏蔽模型的输出。 因为如果我们不关心模型对“pad”输入位置的输出(它们无论如何都不会造成损失),那么输出中可能还会有一些随机垃圾,这会影响 F1 指标。最好也只有零。

【问题讨论】:

    标签: tensorflow machine-learning scikit-learn keras metrics


    【解决方案1】:

    切换到以下(基于this code):

    import numpy as np
    from keras.callbacks import Callback
    from sklearn.metrics import f1_score
    
    
    class ZeroPaddedF1Score(Callback):
        def on_train_begin(self, logs={}):
            self.val_f1s = []
    
    
        def on_epoch_end(self, epoch, logs={}):
            y_true = np.argmax(self.validation_data[1], axis=-1)
            y_pred = np.argmax(self.model.predict(self.validation_data[0]), axis=-1)
            val_f1 = zero_padded_f1(y_true, y_pred)
            self.val_f1s.append(val_f1)
            print ' - val_f1: %f' % (val_f1)
    
    
    def zero_padded_f1(y_true, y_pred):
        y_pred_flat, y_true_flat = [], []
        for y_pred_i, y_true_i in zip(y_pred.flatten(), y_true.flatten()):
            if y_true_i != 0:
                y_pred_flat.append(y_pred_i)
                y_true_flat.append(y_true_i)
        result = f1_score(y_true_flat, y_pred_flat, average='macro')
        return result
    

    它可能无法与 model.compile 一起使用(因为它使用 numpy 数组操作,因此是一个已编译的模型),但它可以作为回调完成工作。

    【讨论】:

    • 如果模型在实际填充开始之前的某个时间步错误地预测 pad 作为类输出怎么办
    【解决方案2】:

    好的,这是我的尝试。非常欢迎评论!

    主要 F1 得分逻辑取自 here。对于y_predy_true 都作为(batch_size, sequence_length, classes_number) 形状的3D 张量,我们在它们对应的切片上计算单类F1,然后平均结果。 0 类保留用于填充,不计入分数。

    from keras import backend as K
    
    def precision(y_true, y_pred):
        """Precision metric.
    
        Only computes a batch-wise average of precision.
    
        Computes the precision, a metric for multi-label classification of
        how many selected items are relevant.
        """
        true_positives = K.sum(K.round(K.clip(y_true * y_pred, 0, 1)))
        predicted_positives = K.sum(K.round(K.clip(y_pred, 0, 1)))
        precision = true_positives / (predicted_positives + K.epsilon())
        return precision
    
    
    def recall(y_true, y_pred):
        """Recall metric.
    
        Only computes a batch-wise average of recall.
    
        Computes the recall, a metric for multi-label classification of
        how many relevant items are selected.
        """
        true_positives = K.sum(K.round(K.clip(y_true * y_pred, 0, 1)))
        possible_positives = K.sum(K.round(K.clip(y_true, 0, 1)))
        recall = true_positives / (possible_positives + K.epsilon())
        return recall
    
    
    def f1_binary(y_true, y_pred):
        p = precision(y_true, y_pred)
        r = recall(y_true, y_pred)
        return 2 * ((p * r) / (p + r + K.epsilon()))
    
    
    def f1(classes_number, y_true, y_pred):
        result = 0.0
        for class_id in xrange(1, classes_number + 1):
            y_true_single_class = y_true[:,:,class_id]
            y_pred_single_class = y_pred[:,:,class_id]
            f1_single = f1_binary(y_true_single_class, y_pred_single_class)
            result += f1_single / float(classes_number)
        return result
    

    下面是如何将它与 Keras 模型一起使用(classes_number 参数通过wrapped_partial 绑定):

    model.compile(optimizer=opt,
                  loss='categorical_crossentropy',
                  metrics=[wrapped_partial(f1, classes_number)])
    

    【讨论】:

      猜你喜欢
      • 2018-07-16
      • 2017-08-28
      • 2019-10-12
      • 2019-03-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-12
      相关资源
      最近更新 更多