多类别稀疏分类交叉熵 TruePositives 指标 不兼容的形状: [2,128] 与 [2,64]

我正在尝试为每个类别添加各种简单的指标。tf.keras.metrics.TruePositives, tf.keras.metrics.Precision ... 当最后一个Dense层为两个或更多时,这会导致下面的崩溃。

InvalidArgumentError: 不兼容的形状: [2,128] 与 [2,64]     [[{{node metrics_12/fp/LogicalAnd}}]]

如果我仅使用accuracy作为指标,它就能工作。我很确定我遗漏了一些基本的东西。因为我在TensorFlow和深度学习方面只是一个业余爱好者。我做错了什么?我如何为每个类别获取指标(主要是真/假阳性/阴性)?(示例代码只有0,1类别,在实际应用中还有更多类别)

Colab链接: https://colab.research.google.com/drive/1aAz1pfN6ttBp8nU6rZgo8OA_Hwdseyg8

#%%from typing import List, Set, Dict, Tuple, Optional, Anyimport numpy as npimport tensorflow as tffrom tensorflow.keras.models import Sequentialfrom tensorflow.keras.layers import Dense, Dropout, BatchNormalization, CuDNNLSTM, LSTM, Flatten#%%# 为演示目的创建随机训练值。## train_x 是[#   [#        [0.3, 0.54 ... 0.8],#        [0.4, 0.6 ... 0.55],#        ...#   ],#   [#        [0.3, 0.54 ... 0.8],#        [0.4, 0.6 ... 0.55],#        ...#   ],#   ...# ]## train_y 是train_x序列的对应分类,总是0或1# [0, 1, 0, 1, 0, ... 0]SAMPLES_CNT = 1000train_x = np.random.rand(SAMPLES_CNT,5,4)train_y = np.vectorize(lambda x: int(round(x)))(np.random.rand(SAMPLES_CNT))val_x = np.random.rand(int(SAMPLES_CNT * 0.1),5,4)val_y = np.vectorize(lambda x: int(round(x)))(np.random.rand(int(SAMPLES_CNT * 0.1)))#%%shape = Tuple[int, int]model = Sequential()model.add(LSTM(32,input_shape=train_x.shape[1:], return_sequences=True, stateful=False))model.add(Dropout(0.2))model.add(BatchNormalization())model.add(LSTM(32,input_shape=train_x.shape[1:], return_sequences=False, stateful=False))model.add(Dropout(0.2))model.add(BatchNormalization())model.add(Dense(16, activation="relu"))model.add(Dropout(0.2))model.add(Dense(2, activation="softmax"))metrics = [    'accuracy',    tf.keras.metrics.TruePositives(name='tp'),    tf.keras.metrics.FalsePositives(thresholds=[0.5, 0.5], name='fp'),    tf.keras.metrics.TrueNegatives(name='tn'),    tf.keras.metrics.FalseNegatives(name='fn'),    tf.keras.metrics.Precision(name='precision'),    tf.keras.metrics.Recall(name='recall'),    tf.keras.metrics.AUC(name='auc'),]model.compile(    optimizer=tf.keras.optimizers.Adam(lr=0.001, decay=1e-6),    loss='sparse_categorical_crossentropy',    metrics=metrics)fit = model.fit(    train_x, train_y,    batch_size=64,    epochs=2,    validation_data=(val_x, val_y),    shuffle=False,)for i, val in enumerate(model.metrics_names):    print(model.metrics_names[i], fit.history[val][:1])

回答:

但是,如果你确实想知道如何在多类别问题中做到这一点,那么我们需要创建自定义指标。

首先,我必须说,我认为在分类问题中使用这些指标并没有太大意义(在多个类别中只有一个正确类别 – 'softmax' + 'categorical_crossentropy')。这种问题不是“二元”的,因此没有“阳性”和“阴性”,而是多个类别中的一个正确类别。

如果你把它看作是各个类别,并将每个类别视为一个二元类别,你会得到类似这样的结果:

  • 每次模型正确分类一个类别时,意味着1个TP + 4个TN(注意有多少真阴性,因为有不止两种结果)
  • 每次模型错误分类一个类别时,意味着1个FP + 1个FN + 3个TN

如果你将这些数字加起来,它们实际上并没有太大意义。(或许我错过了某种专门用于计算分类问题的这些指标的方法… 然而,你可以使用下面的所有内容来处理这个问题,了解上述内容)。

另一方面,你可以为多个二元类别获得良好的指标(其中每个类别独立于其他类别,并且可以有多个正确类别 – 'sigmoid' + 'binary_crossentropy')。

在这种情况下,你可以遵循两种方法:

  • 按类别获取指标
  • 以某种方式平均所有类别的指标(你可以在sklearn文档中看到一些平均类型的示例)

按类别指标

这些对应于sklearn文档中的'binary'平均模式。

方案1:

将每个类别作为一个独立的模型输出,在编译时,为每个输出设置所有这些指标。TensorFlow将单独查看每个输出并无问题地计算所有内容。

方案2:

这应该作为每个类别的独立指标来完成。因此,我们可以为此创建一个考虑类别索引的包装器。

我将提供一些示例。请注意,这些示例都不能是“稀疏”的,因为可能有多个正确类别,因此,在这种情况下,地面true数据将具有形状(samples, classes),就像预测的pred值一样。

对于每个指标,你可以创建一个像这样的包装器:

#类别获取器def get_class(true, pred, index):    #获取类别    true = true[:, index]    pred = pred[:, index]    #四舍五入pred - 你可以选择不同的阈值    pred = K.cast(K.greater(pred, 0.5), K.floatx())    return true, pred#类别包装器def some_metric_per_class(class_index):    def the_actual_metric(true, pred):        true, pred = get_class(class_index)        return calculations    return the_actual_metric

这样的包装器可以像这样使用:

metrics = [some_metric_per_class(i) for i in range(n_classes)]metrics += [some_other_metric_per_class(i) for i in range(n_classes)]model.compile(metrics = metrics, ...)

这里

现在,每个以下指标应该有自己的包装器(我在这里没有写出来,以避免不必要的重复):

def TP(true, pred):    true, pred = get_class(class_index)    return K.sum(true * pred)def FP(true, pred):    true, pred = get_class(class_index)    return K.sum(pred * (1 - true))def TN(true, pred):    true, pred = get_class(class_index)    return K.sum((1-true) * (1-pred))def FN(true, pred):    true, pred = get_class(class_index)    return K.sum((1-pred) * true)def precision(true, pred):    true, pred = get_class(class_index)    TP = K.sum(true * pred)    TP_and_FP = K.sum(pred)    return K.switch(K.equal(TP_and_FP, 0), 1, TP / TP_and_FP)def recall(true, pred):    true, pred = get_class(class_index)    TP = K.sum(true * pred)    TP_and_FN = K.sum(true)    return K.switch(K.equal(TP_and_FN, 0), 1, TP / TP_and_FN)def AUC(true, pred):    true, pred = get_class(class_index)     #我们希望严格的1D数组 - 不能有(batch, 1)这样的情况    true= K.flatten(true)    pred = K.flatten(pred)        #此批次中元素的总数    totalCount = K.shape(true)[0]        #按降序排序预测值    values, indices = tf.nn.top_k(pred, k = totalCount)           #根据上述预测排序地面真实值             sortedTrue = K.gather(true, indices)        #获取已排序的负元素    negatives = 1 - sortedTrue        #每阈值的真阳性计数    TPCurve = K.cumsum(sortedTrue)        #曲线下面积    auc = K.sum(TPCurve * negatives)       #将结果标准化在0和1之间    totalCount = K.cast(totalCount, K.floatx())    positiveCount = K.sum(true)    negativeCount = totalCount - positiveCount    totalArea = positiveCount * negativeCount    return  auc / totalArea

关于AUC的解释

重要:精确度、召回率和AUC不会是精确值,因为Keras按批次计算指标,然后平均每个批次的结果。

平均指标

这些可能只在“精确度”和“召回率”上有意义。所以我只为这两个指标做这个处理。

这里不需要包装器,或者不需要单独的输出。truepred数据与之前的示例中一样,形状为(samples, classes)

在这里,我们使用相同的计算,但现在我们将所有类别保持在一起,并决定如何平均它们。

def base_metrics(true, pred):    #四舍五入pred - 你可以选择不同的阈值    pred = K.cast(K.greater(pred, 0.5), K.floatx())    TP = K.sum(true * pred, axis=0)    TP_and_FP = K.sum(pred, axis=0)    TP_and_FN = K.sum(true, axis=0)    return TP, TP_and_FP, TP_and_FNdef precision_micro(true, pred):    TP, TP_FP, TP_FN = base_metrics(true, pred)    TP = K.sum(TP)    TP_FP = K.sum(TP_FP)    return K.switch(K.equal(TP_FP, 0), 1, TP / TP_FP)def precision_macro(true, pred):    TP, TP_FP, TP_FN = base_metrics(true, pred)    precision = K.switch(K.equal(TP_FP, 0), 1, TP / TP_FP)    return K.mean(precision)

你可以对recall_microrecall_macro做同样的事情,但使用TP_FN而不是TP_FP

Related Posts

使用LSTM在Python中预测未来值

这段代码可以预测指定股票的当前日期之前的值,但不能预测…

如何在gensim的word2vec模型中查找双词组的相似性

我有一个word2vec模型,假设我使用的是googl…

dask_xgboost.predict 可以工作但无法显示 – 数据必须是一维的

我试图使用 XGBoost 创建模型。 看起来我成功地…

ML Tuning – Cross Validation in Spark

我在https://spark.apache.org/…

如何在React JS中使用fetch从REST API获取预测

我正在开发一个应用程序,其中Flask REST AP…

如何分析ML.NET中多类分类预测得分数组?

我在ML.NET中创建了一个多类分类项目。该项目可以对…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注