Keras的填充输出的F1得分

时间:2018-04-13 21:59:45

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

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

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

2)是否可以将sklearn's f1_score implementation传递给模型compile(可能以某种方式包装后)?蝙蝠,它没有工作,因为显然占位符被传递到它而不是一个numpy数组(我使用tensorflow后端..)

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

2 个答案:

答案 0 :(得分: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数组运行,因此已经编译过模型),但它可以作为回调来完成。

答案 1 :(得分:0)

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

主要F1得分逻辑取自here。对于作为形状y_pred的3D张量的y_true(batch_size, sequence_length, classes_number),我们计算单个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)])