使用交叉验证选择最佳阈值:Keras中的二进制分类

时间:2019-04-22 01:20:37

标签: machine-learning keras

我有一个Keras模型,该模型将转换后的向量x作为输入,并输出每个输入值为1的概率。

我想从该模型中进行预测并找到最佳阈值。也就是说,“此值为1”的临界值应为0.23,或应为0.78,或其他。我知道交叉验证是一个很好的工具。

我的问题是如何进行培训。例如,假设我有以下模型(取自here):

def create_baseline():
    # create model
    model = Sequential()
    model.add(Dense(60, input_dim=60, kernel_initializer='normal', activation='relu'))
    model.add(Dense(1, kernel_initializer='normal', activation='sigmoid'))
    # Compile model
    model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
    return model

我训练模型并获得一些输出概率:

model.fit(train_x, train_y)
predictions = model.predict(train_y)

例如,现在我想学习predictions中每个条目的值的阈值,这些阈值将提供最佳的准确性。如何学习此参数,而不是在训练完成后仅选择一个参数?

编辑:例如,说我有这个:

def fake_model(self):

    #Model that returns probability that each of 10 values is 1
    a_input = Input(shape=(2, 10), name='a_input')
    dense_1 = Dense(5)(a_input)
    outputs = Dense(10, activation='sigmoid')(dense_1)

    def hamming_loss(y_true, y_pred):
        return tf.to_float(tf.reduce_sum(abs(y_true - y_pred))) /tf.to_float(tf.size(y_pred))

    fakemodel = Model(a_input, outputs)

    #Use the outputs of the model; find the threshold value that minimizes the Hamming loss
    #Record the final confusion matrix.

如何训练这种端到端的模型?

4 个答案:

答案 0 :(得分:0)

首先,这是您问题的直接答案。您正在考虑使用ROC curve。例如,假设一些数据X_testy_test

from matplotlib import pyplot as plt
from sklearn.metrics import roc_curve
from sklearn.metrics import auc

y_pred = model.predict(X_test).ravel()

fpr, tpr, thresholds = roc_curve(y_test, y_pred)

my_auc = auc(fpr, tpr)

plt.figure(1)
plt.plot([0, 1], [0, 1], 'k--')
plt.plot(fpr, tpr, label='Model_name (area = {:.3f})'.format(my_auc))
plt.xlabel('False positive rate')
plt.ylabel('True positive rate')
plt.title('ROC curve')
plt.legend(loc='best')
plt.show()

plt.figure(2)
plt.xlim(0, 0.2)
plt.ylim(0.8, 1)
plt.plot([0, 1], [0, 1], 'k--')
plt.plot(fpr, tpr, label='Model_name (area = {:.3f})'.format(my_auc))
plt.xlabel('False positive rate')
plt.ylabel('True positive rate')
plt.title('ROC curve close-up')
plt.legend(loc='best')
plt.show()

第二,关于我的评论,here's an example of one attempt.可以在Keras,TF或任何地方完成,尽管他使用XGBoost做到了。

希望有帮助!

答案 1 :(得分:0)

我的第一个想法是蛮力。 您在测试集上分别为每个输入及其对应的预测输出计算度量。
然后,对于每个参数,对0和1之间的阈值进行迭代,直到针对给定的输入/预测对优化度量。

答案 2 :(得分:0)

如果ROC曲线不是您想要的,则可以创建一个自定义Keras层,该层吸收原始模型的输出,并尝试根据给定的实际输出和预测的概率来学习最佳阈值。

此层从预测的概率中减去阈值,乘以相对较大的常数(在这种情况下为100),然后应用S型函数。这是一个曲线图,显示了在三个不同阈值(.3,.5,.7)下的功能。

enter image description here

下面是定义此层以及创建仅由其组成的模型的代码,在拟合原始模型之后,将其输出概率输入该模型并开始训练以获得最佳阈值。

class ThresholdLayer(keras.layers.Layer):
    def __init__(self, **kwargs):
        super(ThresholdLayer, self).__init__(**kwargs)

    def build(self, input_shape):
        self.kernel = self.add_weight(name="threshold", shape=(1,), initializer="uniform",
                                      trainable=True)
        super(ThresholdLayer, self).build(input_shape)

    def call(self, x):
        return keras.backend.sigmoid(100*(x-self.kernel))

    def compute_output_shape(self, input_shape):
        return input_shape

out = ThresholdLayer()(input_layer)
threshold_model = keras.Model(inputs=input_layer, outputs=out)
threshold_model.compile(optimizer="sgd", loss="mse")

答案 3 :(得分:0)

对于许多流行的分类质量指标(准确性,准确性,召回率等),您不能在训练神经网络时学习最佳阈值。

这是因为这些指标不可区分-因此,梯度更新将无法正确设置阈值(或任何其他参数)。因此,在训练大多数参数时,您不得不优化一个很好的平滑损失(如负对数似然),然后通过网格搜索来调整阈值。

当然,您可以提出一个平滑的指标并对其进行优化(有时人们会这样做)。但是在大多数情况下,可以优化对数似然率,获得良好的概率分类器并调整其阈值。例如。如果要优化准确性,则应首先尽可能准确地估计类概率(以接近理想的贝叶斯分类器),然后选择其argmax。