keras中是否有基于精度或召回率而不是损失的优化器?

Is there an optimizer in keras based on precision or recall instead of loss?

我正在开发一个只有两个 类、0 和 1 的分割神经网络(0 是背景,1 是我想在图像上找到的对象)。在每张图片上,大约有 80% 的 1 和 20% 的 0。如您所见,数据集不平衡,导致结果错误。我的准确率是 85%,我的损失很低,但这只是因为我的模型擅长寻找背景!

我想将优化器基于另一个指标,例如在这种情况下更有用的精度或召回率。

有人知道如何实现吗?

没有。要执行 'gradient descent',您需要计算梯度。为此,功能需要以某种方式平滑。 Precision/recall 或者 accuracy 不是一个光滑的函数,它只有梯度为无穷大的锐边和梯度为零的平坦区域。因此,您不能使用任何一种数值方法来找到这样一个函数的最小值——您将不得不使用某种组合优化,这将是 NP 难的。

您没有使用精度或召回率来进行优化。您只需将它们作为有效分数进行跟踪以获得最佳权重。不要混合损失、优化器、指标和其他。它们的用途不同。

THRESHOLD = 0.5
def precision(y_true, y_pred, threshold_shift=0.5-THRESHOLD):

    # just in case 
    y_pred = K.clip(y_pred, 0, 1)

    # shifting the prediction threshold from .5 if needed
    y_pred_bin = K.round(y_pred + threshold_shift)

    tp = K.sum(K.round(y_true * y_pred_bin)) + K.epsilon()
    fp = K.sum(K.round(K.clip(y_pred_bin - y_true, 0, 1)))

    precision = tp / (tp + fp)
    return precision


def recall(y_true, y_pred, threshold_shift=0.5-THRESHOLD):

    # just in case 
    y_pred = K.clip(y_pred, 0, 1)

    # shifting the prediction threshold from .5 if needed
    y_pred_bin = K.round(y_pred + threshold_shift)

    tp = K.sum(K.round(y_true * y_pred_bin)) + K.epsilon()
    fn = K.sum(K.round(K.clip(y_true - y_pred_bin, 0, 1)))

    recall = tp / (tp + fn)
    return recall


def fbeta(y_true, y_pred, beta = 2, threshold_shift=0.5-THRESHOLD):   
    # just in case 
    y_pred = K.clip(y_pred, 0, 1)

    # shifting the prediction threshold from .5 if needed
    y_pred_bin = K.round(y_pred + threshold_shift)

    tp = K.sum(K.round(y_true * y_pred_bin)) + K.epsilon()
    fp = K.sum(K.round(K.clip(y_pred_bin - y_true, 0, 1)))
    fn = K.sum(K.round(K.clip(y_true - y_pred, 0, 1)))

    precision = tp / (tp + fp)
    recall = tp / (tp + fn)

    beta_squared = beta ** 2
    return (beta_squared + 1) * (precision * recall) / (beta_squared * precision + recall) 


def model_fit(X,y,X_test,y_test):
    class_weight={
    1: 1/(np.sum(y) / len(y)),
    0:1}
    np.random.seed(47)
    model = Sequential()
    model.add(Dense(1000, input_shape=(X.shape[1],)))
    model.add(Activation('relu'))
    model.add(Dropout(0.35))
    model.add(Dense(500))
    model.add(Activation('relu'))
    model.add(Dropout(0.35))
    model.add(Dense(250))
    model.add(Activation('relu'))
    model.add(Dropout(0.35))
    model.add(Dense(1))
    model.add(Activation('sigmoid'))

    model.compile(loss='binary_crossentropy', optimizer='adamax',metrics=[fbeta,precision,recall])
    model.fit(X, y,validation_data=(X_test,y_test), epochs=200, batch_size=50, verbose=2,class_weight = class_weight)
    return model

正如其他人所说,precision/recall 不能直接用作损失函数。然而,已经发现更好的代理损失函数可以帮助整个 precision/recall 相关函数家族(例如 ROC AUC、固定召回精度等)

研究论文 Scalable Learning of Non-Decomposable Objectives covers this with a method to sidestep the combinatorial optimization by the use of certain calculated bounds, and some Tensorflow code by the authors is available at the tensorflow/models repository. Additionally, there is a followup question on Whosebug 的答案将其改编成可用的 Keras 损失函数。

特别感谢 Francois Chollet 和 Keras issue thread here 的其他参与者,他们提出了该研究论文。您可能还会发现该线程提供了对手头问题的其他有用见解。

我对二进制分类的不平衡数据集有同样的问题,我也想提高召回灵敏度。我发现 tf.keras 中有一个 built-in 函数用于调用,可以在编译语句中使用,如下所示:

   from tensorflow.keras.metrics import Recall, Accuracy   
   model.compile(loss='binary_crossentropy' , optimizer=opt, metrics=[Accuracy(),Recall()])

对于不平衡的数据集有同样的问题,我建议您使用 F1 分数 作为优化器的指标。 Andrew Ng 教导说,为模型设置一个指标是训练模型最简单(最好?)的方法。如果你有 2 个指标,比如精确率和召回率——不清楚哪个更重要。试图对一个指标设置限制显然会影响另一个指标...

F1 分数是召回率和精确率的奇迹 - 这是它们的调和平均值。

不幸的是,我正在使用的 Keras 没有实现 F1 分数作为指标,就像准确率或许多其他 Keras 指标一样 https://keras.io/api/metrics/

我找到了 F1 分数作为 Keras 指标的实现,在每个时期使用: https://medium.com/@aakashgoel12/how-to-add-user-defined-function-get-f1-score-in-keras-metrics-3013f979ce0d

我已经实现了上述文章中的简单函数,模型现在以 F1 分数作为其 Keras 优化器指标进行训练。测试结果:准确率下降了一点,F1分数上升了很多。

处理像您这样的不平衡数据集的推荐方法是使用 class_weights 或 sample_weights。详见型号fitAPI。

引用:

class_weight: Optional dictionary mapping class indices (integers) to a weight (float) value, used for weighting the loss function (during training only). This can be useful to tell the model to "pay more attention" to samples from an under-represented class.

使用与 class 频率成反比的权重,损失将避免仅预测背景 class。

我知道这不是您提出问题的方式,但恕我直言,这是解决您所面临问题的最实用方法。

我认为回调和提前停止机制提供了一种技术,可以引导您尽可能接近您想要实现的目标。请阅读 Jason Brownlee 撰写的以下关于提前停止的文章(读到底!):

https://machinelearningmastery.com/how-to-stop-training-deep-neural-networks-at-the-right-time-using-early-stopping/