如何使用opencv创建自定义keras损失函数?

时间:2019-06-27 16:06:45

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

我正在使用keras开发机器学习模型,并且注意到可用的损失函数无法在我的测试集上提供最佳结果。

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

损失函数如下:

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)

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

2 个答案:

答案 0 :(得分:1)

考虑到上面的代码,在考虑均方误差之前,这似乎等同于将Lambda()层用作在图像中应用该变换的输出层。

无论它是作为Lambda()层还是在损失函数中实现的;转换必须使Tensorflow理解如何计算梯度。最简单的方法是使用Tensorflow数学运算来重新实现cv2.Laplacian计算。

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

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

答案 1 :(得分:0)

我设法达成一个简单的解决方案。主要特征是梯度计算实际上是2D滤波器。有关此信息的更多信息,请遵循关于拉普拉斯内核的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)

定义拉普拉斯内核,将其值应用于上述卷积层的权重,并将可训练的值设置为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层!如果您使用它,那么拉普拉斯图层中的权重将被更新!