keras自定义指标函数如何将2个模型输出提供给单个指标评估函数

时间:2019-06-24 10:48:24

标签: python-3.x tensorflow keras deep-learning

我有一个CNN对象检测模型,该模型具有两个带有张量名称'classification''regression'的头(输出)。

我想定义一个度量标准函数,该函数同时接受两个输出,以便它可以查看回归预测,以确定要保留和使用的索引这些索引以从分类预测中选择张量并计算一些指标。

我目前在this link的帮助下定义的指标函数:

from tensorflow.python.keras.metrics import MeanMetricWrapper

class Accuracy2(MeanMetricWrapper):

    def __init__(self, name='dummyAccuracy', dtype=None):
        super(Accuracy2, self).__init__(metric_calculator_func, name, dtype=dtype)
        self.true_positives = self.add_weight(name='lol', initializer='zeros')

    @classmethod
    def from_config(cls, config):
        if 'fn' in config:
          config.pop('fn')
        return super(Accuracy2, cls).from_config(config)


    def update_state(self, y_true, y_pred, sample_weight=None):
      print("==@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@===")
      print("Y-True {}".format(y_true))
      print("Y-Pred {}".format(y_pred))
      print("==@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@===")

      update_ops = [self.true_positives.assign_add(1.0)]
      return tf.group(update_ops)

    def result(self):
      return self.true_positives

    def reset_states(self):
      # The state of the metric will be reset at the start of each epoch.
      self.true_positives.assign(0.)

我在模型编译期间将其称为:

training_model.compile(
    loss={
        'regression'    : regression_loss(),
        'classification': classification_loss()
    },
    optimizer=keras.optimizers.Adam(lr=lr, clipnorm=0.001),
    metrics=[Accuracy2()]
)
tf.estimator.train_and_evaluate 期间的

屏幕日志为:

  

INFO:tensorflow:损失= 0.0075738616,步长= 31(11.941秒)

     

INFO:tensorflow:global_step / sec:4.51218

     

INFO:tensorflow:损失= 0.01015341,步长= 36(1.108秒)

     

INFO:tensorflow:将40个检查点保存到/tmp/tmpcla2n3gy/model.ckpt中。

     

INFO:tensorflow:调用model_fn。   == @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ =====   Tensor(“ IteratorGetNext:1”,shape =(?, 120087,5),dtype = float32,device = / device:CPU:0)   张量(“ regression / concat:0”,shape =(?,?,4),dtype = float32)   == @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ =====   == @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ =====   Tensor(“ IteratorGetNext:2”,shape =(?, 120087,2),dtype = float32,device = / device:CPU:0)   Tensor(“ classification / concat:0”,shape =(?,?,1),dtype = float32)   == @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ===

     

INFO:tensorflow:完成调用model_fn。

     

INFO:tensorflow:于2019-06-24T08:20:35Z开始评估   INFO:tensorflow:Graph已完成。   2019-06-24 13:50:36.457345:I tensorflow / core / common_runtime / gpu / gpu_device.cc:1512]添加可见的gpu设备:0   2019-06-24 13:50:36.457398:I tensorflow / core / common_runtime / gpu / gpu_device.cc:984]具有强度1边缘矩阵的设备互连StreamExecutor:   2019-06-24 13:50:36.457419:I tensorflow / core / common_runtime / gpu / gpu_device.cc:990] 0   2019-06-24 13:50:36.457425:I tensorflow / core / common_runtime / gpu / gpu_device.cc:1003] 0:N   2019-06-24 13:50:36.457539:I tensorflow / core / common_runtime / gpu / gpu_device.cc:1115]创建了TensorFlow设备(/ job:localhost /副本:0 /任务:0 /设备:GPU:0和9855 MB内存)->物理GPU(设备:0,名称:GeForce RTX 2080 Ti,pci总线ID:0000:01:00.0,计算能力:7.5)

     

INFO:tensorflow:从/tmp/tmpcla2n3gy/model.ckpt-40恢复参数

     

INFO:tensorflow:正在运行local_init_op。

     

INFO:tensorflow:已运行local_init_op。

     

INFO:tensorflow:评估[10/100]

     

INFO:tensorflow:评估[20/100]

     

INFO:tensorflow:评估[30/100]

     

INFO:tensorflow:评估[40/100]

     

INFO:tensorflow:评估[50/100]

     

INFO:tensorflow:评估[60/100]

     

INFO:tensorflow:评估[70/100]

     

INFO:tensorflow:评估[80/100]

     

INFO:tensorflow:评估[90/100]

     

INFO:tensorflow:评估[100/100]

     

INFO:tensorflow:在2019-06-24-08:20:44完成评估

     

INFO:tensorflow:全局步骤40的保存格:_focal = 0.0016880237,_smooth_l1 = 0.0,dummyAccuracy = 100.0,global_step = 40,损失= 0.0016880237

此行:

==@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@===
Tensor("IteratorGetNext:1", shape=(?, 120087, 5), dtype=float32, device=/device:CPU:0)
Tensor("regression/concat:0", shape=(?, ?, 4), dtype=float32)
==@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@===
==@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@===
Tensor("IteratorGetNext:2", shape=(?, 120087, 2), dtype=float32, device=/device:CPU:0)
Tensor("classification/concat:0", shape=(?, ?, 1), dtype=float32)
==@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@===

显示Accuracy2()两次被调用两次,分别是回归分类。 但我希望通过将回归分类一起添加到其中

3 个答案:

答案 0 :(得分:1)

如果该指标中不需要y_true

这是一个丑陋的答案,但是....

您必须制作一个图层才能为您计算指标。使用Lambda

鉴于regOutclassOut是您在模型创建中的输出张量,而不是像Model(inputs, [regOut,classOut])这样的模型创建,您将:

def metricFunc(modelOutputs):
    regressionOutput = modelOutputs[0]
    classOutput = modelOuptuts[1]

    #calculate metric
    return calculatedMetric


metricTensor = Lambda(metricFunc, name='metric_layer')([regOut,classOut])

使指标成为模型的输出:

model = Model(inputs, [regOut, classOut, metricTensor])

创建虚拟损失和虚拟指标进行编译:

def dummyLoss(true,pred):
    return K.zeros(K.shape(true)[:1])

def dummyMetric(true,pred):
    return pred

正在编译:

model.compile(loss = [regLoss, classLoss, dummyLoss], 
              metrics={'metric_layer':dummyMetric}, 
              optimizer=...)

这还要求您为metricTensor使用虚拟张量进行训练:

model.fit(x_train, [y_reg,y_class,np.zeros(y_reg.shape[:1])], ...)

答案 1 :(得分:1)

如果同时需要两个y_true值作为度量标准。

在这种情况下,我们将需要整理数据,以便可以将其合并为单个数组。 这将需要固定大小的输出。

假设您有regOutclassOut作为张量。如果它们是2D,只需串联它们,否则:

regOut = Flatten()(regOut) #only if regOut is 3D or more
classOut = Flatten()(classOut) #only if classOut is 3D or more

out = Concatenate()([regOut,classOut])

使用以下单个输出制作模型:

model = Model(inputs, out)

对数据集执行相同操作

y_reg_train = y_reg_train.reshape((y_reg_train.shape[0], -1))
y_class_train = y_clas_trains.reshape((y_class_train.shape[0], -1))
y_train = np.concatenate([y_reg_train, y_class_train], axis=-1)

#same for y_val

然后创建一个将两者分开的指标:

def metric(y_true, y_pred):

    reg_true = y_true[:,:flattened_size_of_reg]
    class_true = y_true[:, flattened_size_of_reg:]

    reg_pred = y_pred[:,:flattened_size_of_reg]
    class_pred = y_pred[:, flattened_size_of_reg:]

    #calculate the metric

    return value

使用组合输出进行训练:

model.fit(x_train, y_train, ...)

答案 2 :(得分:0)

让我向您展示实现此目标的一种优雅方法。

  • 首先,定义一个包装度量标准的外部函数,以便您可以传递回归张量reg_out

    def metric_func(reg_out):
        def metric(y_true, class_out):
            return your_metric(reg_out, class_out, y_true)
        return metric
    
  • 接下来,通过设置产生它的层的参数class_out来命名分类张量name。例如:

    class_out = Dense(1, name='class_out')(something)
    
  • 最后,如下设置model.compile的参数metrics

    model.compile(...,
                  metrics={'class_out': metric_func(reg_out)})