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

Posted

技术标签:

【中文标题】keras 中是不是有基于精度或召回而不是损失的优化器?【英文标题】:Is there an optimizer in keras based on precision or recall instead of loss?keras 中是否有基于精度或召回而不是损失的优化器? 【发布时间】:2019-02-02 03:30:56 【问题描述】:

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

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

有人知道如何实现吗?

【问题讨论】:

我们的优化器基于损失而不是准确度、精确度或召回率等指标有非常基本的(即数学)原因;在Cost function training target versus accuracy desired goal 中查看我的回答(这是关于损失与准确性,但同样的论点也适用于其他措施)。 优化基于凸函数。您无法优化精度或召回率。您必须将其放入评估指标中并使用它来获得最佳迭代 谢谢!也许我的问题没有很好地提出。我的意思是我如何才能将我的训练建立在精确度上?可能吗 ?还是我应该只跟踪各个时期的精度并希望它降低? 不确定您为什么认为我们不理解您的问题;我们做到了,并且我们上面的答案(我的和@Alexis)都成立(如果您仍然想知道,也许我们的 cmets 不够清楚......) @VikasNS 请仔细阅读答案;您不能将 AUROC 用作 loss 函数 【参考方案1】:

没有。要进行“梯度下降”,您需要计算梯度。为此,该功能需要以某种方式平滑。 Precision/recall 或accuracy 不是一个平滑函数,它只有梯度为无穷大的锐边和梯度为零的平坦位置。因此,您不能使用任何类型的数值方法来找到此类函数的最小值 - 您必须使用某种组合优化,这将是 NP 难的。

【讨论】:

【参考方案2】:

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

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

【讨论】:

这里是[另一种方法]:(***.com/questions/42606207/…),我不知道为什么这两个代码在相同的阈值下会产生不同的结果,并且它们都与我计算的值不同预测结果(而 keras_metrics.precision() 返回 0.5 阈值的正确答案)。 Recent research from 2017 对该领域的研究表明,可以通过使用新的代理损失函数来优化精度/召回系列中的统计数据,例如precision-at-fixed-recall 等。作者报告了使用新的代理损失与基线损失函数相比,所选指标的相对改进。我在下面写了一个答案来反映这些发现。看到这个领域发展如此之快令人兴奋! 为什么 fbeta 中的 beta 被硬编码为两个?不应该是参数吗?【参考方案3】:

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

研究论文Scalable Learning of Non-Decomposable Objectives 涵盖了这一点,其中介绍了一种通过使用某些计算边界来回避组合优化的方法,作者的一些 Tensorflow 代码可在tensorflow/models 存储库中找到。此外,还有一个后续问题 on *** 有一个答案,可以将其调整为可用的 Keras 损失函数。

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

【讨论】:

【参考方案4】:

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

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

【讨论】:

选择的(任何)度量标准与正在优化的内容无关,这由loss 参数(此处为二进制交叉熵)给出。损失和度量是完全不同的东西,它们不应该混淆(有关详细信息,请参阅接受的答案)。【参考方案5】:

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

F1 分数是召回率和准确率的神童——它是它们的调和平均值。

不幸的是,我正在使用的 Keras 没有实现 F1 分数作为指标,比如有一个用于准确性的指标,或者许多其他 Keras 指标https://keras.io/api/metrics/。

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

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

【讨论】:

在您的链接帖子中,您使用 F1 作为指标,not 作为损失(即要优化的数量),它仍然是二元交叉熵。损失和指标是完全不同的东西,不要混淆(有关详细信息,请参阅接受的答案)。【参考方案6】:

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

引用:

class_weight:可选字典将类索引(整数)映射到权重(浮点)值,用于加权损失函数(仅在训练期间)。这对于告诉模型“更加关注”来自代表性不足的类的样本很有用。

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

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

【讨论】:

【参考方案7】:

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

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

【讨论】:

这没有提供问题的答案。一旦你有足够的reputation,你就可以comment on any post;相反,provide answers that don't require clarification from the asker。 - From Review

以上是关于keras 中是不是有基于精度或召回而不是损失的优化器?的主要内容,如果未能解决你的问题,请参考以下文章

在 Keras 分类神经网络中进行精度交易以获得更好的召回率

在 Keras 模型中优化准确性而不是损失

如何使用精度(而不是准确度)在 Keras 中优化 CNN

全局多标签性能评估的平均精度/召回率是不是正确?

在 Keras 中实现批次相关的损失

Keras 中基于输入数据的自定义损失函数