【问题标题】:How to create a custom keras loss function with opencv?如何使用 opencv 创建自定义 keras 损失函数?
【发布时间】:2019-06-27 16:06:45
【问题描述】:

我正在使用 keras 开发机器学习模型,我注意到可用的损失函数在我的测试集上并没有给出最好的结果。

我使用的是 Unet 架构,其中我输入 (16,16,3) 图像,网络还输出 (16,16,3) 图片(自动编码器)。我注意到,也许改进模型的一种方法是,如果我使用一个损失函数来比较净输出和地面实况之间的梯度(拉普拉斯算子)上的像素与像素。但是,我没有找到任何可以处理这种应用程序的教程,因为它需要对来自网络的每个输出图像使用 opencv 拉普拉斯函数。

损失函数是这样的:

def laplacian_loss(y_true, y_pred):

  # y_true already is the calculated gradients, only needs to compute on the y_pred
  # calculates the gradients for each predicted image
  y_pred_lap = []
  for img in y_pred:
    laplacian = cv2.Laplacian( np.float64(img), cv2.CV_64F )
    y_pred_lap.append( laplacian )

  y_pred_lap = np.array(y_pred_lap)

  # mean squared error, according to keras losses documentation
  return K.mean(K.square(y_pred_lap - y_true), axis=-1)

有人做过类似的损失计算吗?

【问题讨论】:

  • 请花点时间格式化您的代码示例,使用 3 个反引号 (`) 表示代码块的开始和结束。
  • 检查以下是否有帮助:stackoverflow.com/questions/56750620/…
  • 我认为这有帮助,但我真的不知道 y_true 和 y_pred 的形状和类型。我的模型训练批量大小等于 256。它会改变什么吗?
  • K.shape(tensor) 返回一个带有形状的张量。

标签: python-3.x opencv keras computer-vision


【解决方案1】:

鉴于上面的代码,在考虑均方误差之前,它似乎相当于使用 Lambda() 层作为在图像中应用该变换的输出层。

无论是作为 Lambda() 层还是在损失函数中实现;转换需要使 Tensorflow 了解如何计算梯度。最简单的做法可能是使用 Tensorflow 数学运算重新实现 cv2.Laplacian 计算。

为了直接使用 cv2 库,您需要创建一个函数来计算 cv2 库内部发生的梯度;这似乎更容易出错。

梯度下降优化依赖于能够计算从输入到损失的梯度;然后回来。中间的任何运算都必须是可微的;并且 Tensorflow 必须了解自动微分工作的数学运算;或者您需要手动添加它们。

【讨论】:

    【解决方案2】:

    我设法找到了一个简单的解决方案。主要特点是梯度计算实际上是一个二维滤波器。有关它的更多信息,请关注关于拉普拉斯内核的link。在这件事上,我的网络的输出必须由拉普拉斯内核过滤。为此,我创建了一个具有固定权重的额外卷积层,与拉普拉斯核完全一样。之后,网络将有两个输出(一个是所需的图像,另一个是梯度的图像)。因此,也有必要定义这两种损失。

    为了更清楚,我将举例说明。在网络的末端,你会得到类似的东西:

    channels = 3 # number of channels of network output
    lap = Conv2D(channels , (3,3), padding='same', name='laplacian') (net_output)
    model = Model(inputs=[net_input], outputs=[net_out, lap])
    

    定义您希望如何计算每个输出的损失:

    # losses for output, laplacian and gaussian
    losses = {
    "enhanced": "mse",
    "laplacian": "mse"
    }
    lossWeights = {"enhanced": 1.0, "laplacian": 0.6}
    

    编译模型:

    model.compile(optimizer=Adam(), loss=losses, loss_weights=lossWeights)
    

    定义拉普拉斯核,将其值应用到上述卷积层的权重中,并将 trainable 设置为 False(因此不会更新)。

    bias = np.asarray([0]*3)
    # laplacian kernel
    l = np.asarray([
      [[[1,1,1],
      [1,-8,1],
      [1,1,1]
      ]]*channels
      ]*channels).astype(np.float32)
    bias = np.asarray([0]*3).astype(np.float32)
    wl = [l,bias]
    model.get_layer('laplacian').set_weights(wl)
    model.get_layer('laplacian').trainable = False
    

    在训练时,请记住您需要两个值作为基本事实:

    model.fit(x=X, y = {"out": y_out, "laplacian": y_lap})
    

    观察:不要使用 BatchNormalization 层!如果您使用它,拉普拉斯层中的权重将被更新!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-02-20
      • 2020-11-26
      • 2020-12-19
      • 2017-12-18
      • 2020-03-27
      • 1970-01-01
      • 1970-01-01
      • 2018-10-28
      相关资源
      最近更新 更多