多类CNN的宏指标(调用/F1...)

时间:2019-05-22 16:09:30

标签: python tensorflow machine-learning keras

我使用CNN对不平衡数据集进行图像分类。我对tensorflow后端完全陌生。这是多类问题(不是multilabel),我有16个类。类是一种热编码。

我想为每个时期计算MACRO指标:F1,精度和召回率。

我找到了打印这些宏指标的代码,但仅适用于验证集 发件人:https://medium.com/@thongonary/how-to-compute-f1-score-for-each-epoch-in-keras-a1acd17715a2

class Metrics(Callback):

 def on_train_begin(self, logs={}):
  self.val_f1s = []
  self.val_recalls = []
  self.val_precisions = []

 def on_epoch_end(self, epoch, logs={}):
  val_predict = (np.asarray(self.model.predict(self.validation_data[0]))).round()
  val_targ = self.validation_data[1]
  _val_f1 = f1_score(val_targ, val_predict,average='macro')
  _val_recall = recall_score(val_targ, val_predict,average='macro')
  _val_precision = precision_score(val_targ, val_predict,average='macro')
  self.val_f1s.append(_val_f1)
  self.val_recalls.append(_val_recall)
  self.val_precisions.append(_val_precision)
  print (" — val_f1: %f — val_precision: %f — val_recall %f" % (_val_f1, _val_precision, _val_recall))
  return

metrics = Metrics()

由于我们使用

,我什至不确定此代码是否真的有效
 val_predict = (np.asarray(self.model.predict(self.validation_data[0]))).round()

如果发生多类分类,ROUND会导致错误吗?

我使用此代码在训练集上打印指标(仅回想一下,因为这对我来说是重要指标)(由于在model.compute中使用了验证集,因此也要进行计算) 代码改编自:Custom macro for recall in keras



def recall(y_true,y_pred):
     true_positives = K.sum(K.round(K.clip(y_true * y_pred, 0, 1)))
     possible_positives = K.sum(K.round(K.clip(y_true, 0, 1)))
     return  true_positives / (possible_positives + K.epsilon())

def unweightedRecall(y_true, y_pred):
    return (recall(y_true[:,0],y_pred[:,0]) + recall(y_true[:,1],y_pred[:,1])+recall(y_true[:,2],y_pred[:,2]) + recall(y_true[:,3],y_pred[:,3])
            +recall(y_true[:,4],y_pred[:,4]) + recall(y_true[:,5],y_pred[:,5])
            +recall(y_true[:,6],y_pred[:,6]) + recall(y_true[:,7],y_pred[:,7])
            +recall(y_true[:,8],y_pred[:,8]) + recall(y_true[:,9],y_pred[:,9])
            +recall(y_true[:,10],y_pred[:,10]) + recall(y_true[:,11],y_pred[:,11])
            +recall(y_true[:,12],y_pred[:,12]) + recall(y_true[:,13],y_pred[:,13])
            +recall(y_true[:,14],y_pred[:,14]) + recall(y_true[:,15],y_pred[:,15]))/16.    



我通过以下方式运行模型

model.compile(optimizer="adam", loss="categorical_crossentropy",metrics=[unweightedRecall,"accuracy"])   #model compilation with unweightedRecall metrics

train =model.fit_generator(image_gen.flow(train_X, train_label, batch_size=64),epochs=100,verbose=1,validation_data=(valid_X, valid_label),class_weight=class_weights,callbacks=[metrics],steps_per_epoch=len(train_X)/64)  #run the model

VALIDATION宏调用不同于2个不同的代码。

即(查看 val_unweightedRecall val_recall

Epoch 10/100
19/18 [===============================] - 13s 703ms/step - loss: 1.5167 - unweightedRecall: 0.1269 - acc: 0.5295 - val_loss: 1.5339 - val_unweightedRecall: 0.1272 - val_acc: 0.5519
 — val_f1: 0.168833 — val_precision: 0.197502 — val_recall 0.15636

为什么用两个不同的代码在宏验证调用中具有不同的价值?

奖金问题:对于已经尝试过此方法的人来说,真的值得根据我们感兴趣的指标(例如,回忆)使用自定义损失或权重的分类交叉熵产生相同的结果吗? / p>

1 个答案:

答案 0 :(得分:1)

让我回答两个问题,但顺序相反:

您不能将Recall用作自定义损失的基础:它不是凸面的!如果您不完全理解为什么不能将Recall或precision或f1用作损失,请花点时间查看损失的作用(毕竟这是模型中的一个巨大参数)。

实际上,该回合旨在解决二进制问题。正如他们所说,如果不是你,那就是另一个。但是在您的情况下,这是错误的。让我们去扔代码:

val_predict = (np.asarray(self.model.predict(self.validation_data[0]))).round()

由内而外,他获取数据(self.validation_data [0;])并预测一个数字(输出1个神经元)。这样,他计算出概率为1。如果该概率大于0.5,则该回合将其转换为1,如果小于1,则将其转换为0。如您所见,这对您来说是错误的。在某些情况下,您不会预测任何课程。继此错误之后,其余的也是错误的。

现在,解决方案。您想在每个步骤中计算平均召回率。顺便说一句,“但它仅适用于验证集”。是的,这是有意的,您可以使用验证来验证模型,而不是火车,否则就作弊了。

所以召回率等于所有积极因素中的真实积极因素。让我们开始吧!

def recall(y_true, y_pred):
     recall = 0
     pred = K.argmax(y_pred)
     true = K.argmax(y_true)
     for i in range(16):
         p = K.cast(K.equal(pred,i),'int32')
         t = K.cast(K.equal(true,i),'int32')
         # Compute the true positive
         common = K.sum(K.dot(K.reshape(t,(1,-1)),K.reshape(p,(-1,1))))
         # divide by all positives in t
         recall += common/ (K.sum(t) + K.epsilon)
     return recall/16

这使您平均回想所有班级。 您可以打印每个类的值。

如有任何疑问,请告诉我!

有关二进制调用的实现,请参见this question,其中的代码也从中改编。