如何在TensorFlow中计算马修斯相关系数

8
我使用TensorFlow Keras创建了一个模型,似乎工作正常。然而,我的主管说计算马修斯相关系数会很有用,以及它已经计算的准确性和损失。我的模型与此处教程中的代码非常相似(https://www.tensorflow.org/tutorials/keras/basic_classification),只是数据集要小得多。是否有预建函数,还是我必须手动获取每个测试的预测并计算?
5个回答

10

没有现成的解决方案,但我们可以通过自定义指标公式计算出来。

您提供的基本分类链接适用于多类别分类问题,而马修斯相关系数专门用于二元分类问题。

假设您的模型按照此类问题的“正常”方式构建(即每条记录的y_pred是表示“True”预测概率的介于0和1之间的数字,标签是精确为01,分别表示地面实况“False”和“True”),则我们可以添加MCC指标如下:

# if y_pred > threshold we predict true. 
# Sometimes we set this to something different to 0.5 if we have unbalanced categories

threshold = 0.5  

def mcc_metric(y_true, y_pred):
  predicted = tf.cast(tf.greater(y_pred, threshold), tf.float32)
  true_pos = tf.math.count_nonzero(predicted * y_true)
  true_neg = tf.math.count_nonzero((predicted - 1) * (y_true - 1))
  false_pos = tf.math.count_nonzero(predicted * (y_true - 1))
  false_neg = tf.math.count_nonzero((predicted - 1) * y_true)
  x = tf.cast((true_pos + false_pos) * (true_pos + false_neg) 
      * (true_neg + false_pos) * (true_neg + false_neg), tf.float32)
  return tf.cast((true_pos * true_neg) - (false_pos * false_neg), tf.float32) / tf.sqrt(x)

我们可以在model.compile调用中包含以下内容:

model.compile(optimizer='adam',
              loss=tf.keras.losses.binary_crossentropy,
              metrics=['accuracy', mcc_metric])

例子

这是一个完整的示例,我们根据mnist数字是否大于4将其分类:

mnist = tf.keras.datasets.mnist

(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
y_train, y_test = 0 + (y_train > 4), 0 + (y_test > 4)

def mcc_metric(y_true, y_pred):
  predicted = tf.cast(tf.greater(y_pred, 0.5), tf.float32)
  true_pos = tf.math.count_nonzero(predicted * y_true)
  true_neg = tf.math.count_nonzero((predicted - 1) * (y_true - 1))
  false_pos = tf.math.count_nonzero(predicted * (y_true - 1))
  false_neg = tf.math.count_nonzero((predicted - 1) * y_true)
  x = tf.cast((true_pos + false_pos) * (true_pos + false_neg) 
      * (true_neg + false_pos) * (true_neg + false_neg), tf.float32)
  return tf.cast((true_pos * true_neg) - (false_pos * false_neg), tf.float32) / tf.sqrt(x)

model = tf.keras.models.Sequential([
  tf.keras.layers.Flatten(input_shape=(28, 28)),
  tf.keras.layers.Dense(128, activation='relu'),
  tf.keras.layers.Dropout(0.2),
  tf.keras.layers.Dense(10, activation='relu'),
  tf.keras.layers.Dense(1, activation='sigmoid')
])

model.compile(optimizer='adam',
              loss=tf.keras.losses.binary_crossentropy,
              metrics=['accuracy', mcc_metric])

model.fit(x_train, y_train, epochs=5)
model.evaluate(x_test, y_test)

输出:

Epoch 1/5
60000/60000 [==============================] - 7s 113us/sample - loss: 0.1391 - acc: 0.9483 - mcc_metric: 0.8972
Epoch 2/5
60000/60000 [==============================] - 6s 96us/sample - loss: 0.0722 - acc: 0.9747 - mcc_metric: 0.9495
Epoch 3/5
60000/60000 [==============================] - 6s 97us/sample - loss: 0.0576 - acc: 0.9797 - mcc_metric: 0.9594
Epoch 4/5
60000/60000 [==============================] - 6s 96us/sample - loss: 0.0479 - acc: 0.9837 - mcc_metric: 0.9674
Epoch 5/5
60000/60000 [==============================] - 6s 95us/sample - loss: 0.0423 - acc: 0.9852 - mcc_metric: 0.9704
10000/10000 [==============================] - 1s 58us/sample - loss: 0.0582 - acc: 0.9818 - mcc_metric: 0.9639
[0.05817381642502733, 0.9818, 0.9638971]

7

预构建函数用于计算马修斯相关系数

sklearn.metrics.matthews_corrcoef(y_true, y_pred, sample_weight=None )

例子:

> from sklearn.metrics import matthews_corrcoef
> y_true = [+1, +1, +1, -1]
> y_pred = [+1, -1, +1, +1]
> matthews_corrcoef(y_true, y_pred) 

请查看文档

但是这并不是在 TensorFlow 中进行计算。 - Stewart_R
1
@Stewart_R 给你提供了这个(需要Tensorflow 2):https://www.tensorflow.org/addons/api_docs/python/tfa/metrics/MatthewsCorrelationCoefficient - ledawg

7

由于提问者接受了使用sklearn的Python版本,因此这里是Stewart_Rs用纯Python的答案:

from math import sqrt
def mcc(tp, fp, tn, fn):

    # https://dev59.com/eLTna4cB1Zd3GeqPBd4T#56875660
    x = (tp + fp) * (tp + fn) * (tn + fp) * (tn + fn)
    return ((tp * tn) - (fp * fn)) / sqrt(x)

它的优点是通用的,不仅适用于评估二元分类。


1
这个问题的其他答案(即,MCC的Keras/Tensorflow实现)在某种程度上是有限制的,因为它需要进行二元分类和只有一个输出列。如果您的设置不是这种情况,该函数将会给出错误的MCC值而没有任何错误提示。
当然,MCC可以计算多类别和多列输出。以下是适用于Keras(Tensorflow)MCC的通用自定义指标函数。
import tensorflow as tf
from keras import backend as K

def keras_calculate_mcc_from_conf(confusion_m):
    """tensor version of MCC calculation from confusion matrix"""
    # as in Gorodkin (2004)
    N = K.sum(confusion_m)
    up = N * tf.linalg.trace(confusion_m) - K.sum(tf.matmul(confusion_m, confusion_m))
    down_left = K.sqrt(N ** 2 - K.sum(tf.matmul(confusion_m, K.transpose(confusion_m))))
    down_right = K.sqrt(N ** 2 - K.sum(tf.matmul(K.transpose(confusion_m), confusion_m)))
    mcc_val = up / (down_left * down_right + K.epsilon())
    return mcc_val


def keras_better_to_categorical(y_pred_in):
    """tensor version of to_categorical"""
    nclass = K.shape(y_pred_in)[1]
    y_pred_argmax = K.argmax(y_pred_in, axis=1)
    y_pred = tf.one_hot(tf.cast(y_pred_argmax, tf.int32), depth=nclass)
    y_pred = tf.cast(y_pred, tf.float32)
    return y_pred


def mcc(y_true, y_pred):
    """To calculate Matthew's correlation coefficient for multi-class classification"""
    # this is necessary to make y_pred values of 0 or 1 because
    # y_pred may contain other value (e.g., 0.6890)
    y_pred = keras_better_to_categorical(y_pred)

    # now it's straightforward to calculate confusion matrix and MCC
    confusion_m = tf.matmul(K.transpose(y_true), y_pred)
    return keras_calculate_mcc_from_conf(confusion_m)


# test mcc
actuals = tf.constant([[1.0, 0], [1.0, 0], [0, 1.0], [1.0, 0]], dtype=tf.float32)
preds = tf.constant([[1.0, 0], [0, 1.0], [0, 1.0], [1.0, 0]], dtype=tf.float32)
mcc_val = mcc(actuals, preds)
print(K.eval(mcc_val))

请注意,由于每个批次大小的度量计算,从Keras打印的MCC值在迭代过程中将是不正确的。您只能信任拟合后调用“evaluate”或“score”的MCC值。这是因为整个样本的MCC不是部分的总和/平均值,与其他指标不同。例如,如果您的批处理大小为1,则在迭代过程中打印的MCC将为零。

0
这里有另一种方法,用于计算多类情况下的马修斯相关系数。更多细节请参考https://en.wikipedia.org/wiki/Phi_coefficient#Multiclass_case
import tensorflow as tf

def mcc_metric(y_true, y_pred, num_classes=3):
    ''' Custom Mathew Correlation Coefficient for multiclass 
        For more details see: 
            "https://en.wikipedia.org/wiki/Phi_coefficient"
        Inputs: 
            y_true (tensor)
            y_pred (tensor)
            num_classes - number of classes
        Outputs:
            mcc - Mathews Correlation Coefficient
        '''
    # obtain predictions here, we can add in a threshold if we would like to
    y_pred = tf.argmax(y_pred, axis=-1)

    # cast to int64
    y_true = tf.squeeze(tf.cast(y_true, tf.int64), axis=-1)
    y_pred = tf.cast(y_pred, tf.int64)

    # total number of samples
    s = tf.size(y_true, out_type=tf.int64)

    # total number of correctly predicted labels
    c = s - tf.math.count_nonzero(y_true - y_pred)

    # number of times each class truely occured
    t = []

    # number of times each class was predicted
    p = []

    for k in range(num_classes):
        k = tf.cast(k, tf.int64)
    
        # number of times that the class truely occured
        t.append(tf.reduce_sum(tf.cast(tf.equal(k, y_true), tf.int32)))

        # number of times that the class was predicted
        p.append(tf.reduce_sum(tf.cast(tf.equal(k, y_pred), tf.int32)))


    t = tf.expand_dims(tf.stack(t), 0)
    p = tf.expand_dims(tf.stack(p), 0)

    s = tf.cast(s, tf.int32)
    c = tf.cast(c, tf.int32)

    num = tf.cast(c*s - tf.matmul(t, tf.transpose(p)), tf.float32)
    dem = tf.math.sqrt(tf.cast(s**2 - tf.matmul(p, tf.transpose(p)), tf.float32)) \
          * tf.math.sqrt(tf.cast(s**2 - tf.matmul(t, tf.transpose(t)), tf.float32))


    mcc = tf.divide(num, dem + 1e-6)

    return mcc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接