Keras 中的自定义损失函数会惩罚中间层的输出

Custom loss function in Keras that penalizes output from intermediate layer

假设我有一个卷积神经网络来 class 化 MNIST 数字,比如这个 Keras example。这纯粹是为了实验,所以我没有明确的理由或理由说明我为什么这样做,但假设我想规范化或惩罚中间层的输出。我意识到下面的可视化并不对应于 MNIST CNN 示例,而是只有几个完全连接的层。但是,为了帮助形象化我的意思,假设我想对第 4 层中的节点值施加惩罚(我可以接受预激活或 post 激活)。 除了具有典型的 multi-class classification 的分类交叉熵损失项之外,我想在损失函数中添加另一个项,以最小化给定输出的平方和层。这在概念上与 l2 正则化有些相似,只是 l2 正则化惩罚网络中所有权重的平方和。相反,我只对给定层(例如第 4 层)的值感兴趣,而不是网络中的所有权重。

我意识到这需要使用 keras 后端编写自定义损失函数以结合分类交叉熵和惩罚项,但我不确定如何在损失函数中使用中间层作为惩罚项。我将非常感谢有关如何执行此操作的帮助。谢谢!

只需将隐藏层指定为附加输出即可。由于 tf.keras.Models 可以有多个输出,这是完全允许的。然后使用这两个值定义您的自定义损失。

扩展您的示例:

input = tf.keras.Input(...)
x1 = tf.keras.layers.Dense(10)(input)
x2 = tf.keras.layers.Dense(10)(x1)
x3 = tf.keras.layers.Dense(10)(x2)
model = tf.keras.Model(inputs=[input], outputs=[x3, x2])

自定义损失函数我觉得是这样的:

def custom_loss(y_true, y_pred):
  x2, x3 = y_pred
  label = y_true # you might need to provide a dummy var for x2
  return f1(x2) + f2(y_pred, x3) # whatever you want to do with f1, f2

实际上,您感兴趣的是正则化,在 Keras 中有两种不同的内置正则化方法可用于大多数层(例如 DenseConv1DConv2D, 等等):

  • 权重正则化,它惩罚层的权重。通常,您可以在构建图层时使用 kernel_regularizerbias_regularizer 参数来启用它。例如:

     l1_l2 = tf.keras.regularizers.l1_l2(l1=1.0, l2=0.01)
     x = tf.keras.layers.Dense(..., kernel_regularizer=l1_l2, bias_regularizer=l1_l2)
    
  • Activity 正则化 ,它会惩罚层的 输出(即激活)。要启用此功能,您可以在构建图层时使用 activity_regularizer 参数:

     l1_l2 = tf.keras.regularizers.l1_l2(l1=1.0, l2=0.01)
     x = tf.keras.layers.Dense(..., activity_regularizer=l1_l2)
    

    请注意,您可以通过 activity_regularizer 参数 为所有层设置 activity 正则化,甚至是自定义层。

在这两种情况下,惩罚都被加到模型的损失函数中,结果将是最终的损失值,优化器将在训练期间对其进行优化。

此外,除了内置的正则化方法(即 L1 和 L2),您还可以定义自己的自定义正则化方法(请参阅 Developing new regularizers). As always, the documentation 提供的其他信息也可能有帮助。

根据给定层的输入或计算添加损失的另一种方法是使用the add_loss() API。如果您已经在创建自定义层,则可以将自定义损失直接添加到该层。或者可以创建一个自定义层,简单地获取输入,计算并添加损失,然后将未更改的输入传递到下一层。

这是直接从文档中获取的代码(以防 link 被破坏):

from tensorflow.keras.layers import Layer

class MyActivityRegularizer(Layer):
  """Layer that creates an activity sparsity regularization loss."""

  def __init__(self, rate=1e-2):
    super(MyActivityRegularizer, self).__init__()
    self.rate = rate

  def call(self, inputs):
    # We use `add_loss` to create a regularization loss
    # that depends on the inputs.
    self.add_loss(self.rate * tf.reduce_sum(tf.square(inputs)))
    return inputs