【问题标题】:Keras: clean implementation for multiple outputs and custom loss functions?Keras:多个输出和自定义损失函数的干净实现?
【发布时间】:2019-01-28 20:14:27
【问题描述】:

来自 TensorFlow,我觉得在 Keras 中实现除基本顺序模型之外的任何其他东西都可能非常棘手。自动发生的事情太多了。在 TensorFlow 中,您始终知道您的占位符(输入/输出)、形状、结构……因此很容易,例如,设置自定义损失。

定义多个输出和自定义损失函数的简洁方法是什么?

我们以一个简单的自动编码器为例,使用 MNIST:

(X_train, Y_train), (X_test, Y_test) = mnist.load_data()
X_train = X_train.reshape(-1, 28, 28, 1)

短卷积编码器:

enc_in = Input(shape=(28, 28, 1), name="enc_in")
x = Conv2D(16, (3, 3))(enc_in)
x = LeakyReLU()(x)
x = MaxPool2D()(x)
x = Conv2D(32, (3, 3))(x)
x = LeakyReLU()(x)
x = Flatten()(x)
z = Dense(100, name="z")(x)

enc = Model(enc_in, z, name="encoder")

解码器的类似架构。我们不关心填充和卷积导致的维度减少,所以我们只是在最后应用双线性调整大小以再次匹配(batch, 28, 28, 1)

def resize_images(inputs, dims_xy):
    x, y = dims_xy
    return Lambda(lambda im: K.tf.image.resize_images(im, (y, x)))(inputs)

# decoder
dec_in = Input(shape=(100,), name="dec_in")
x = Dense(14 * 14 * 8)(dec_in)
x = LeakyReLU()(x)
x = Reshape((14, 14, 8))(x)
x = Conv2D(32, (3, 3))(x)
x = LeakyReLU()(x)
x = UpSampling2D()(x)
x = Conv2D(16, (3, 3))(x)
x = LeakyReLU()(x)
x = Conv2D(1, (3, 3), activation="linear")(x)
dec_out = resize_images(x, (28, 28))

dec = Model(dec_in, dec_out, name="decoder")

我们定义了自己的 MSE 以提供一个简单的示例...

def custom_loss(y_true, y_pred):
    return K.mean(K.square(y_true - y_pred))

...最后构建我们的完整模型:

outputs = dec(enc(enc_in))
ae = Model(enc_in, outputs, name="ae")
ae.compile(optimizer=Adam(lr=1e-4), loss=custom_loss)

# training
ae.fit(x=X_train, y=X_train, batch_size=256, epochs=10)

如果我在解码器的最后一层定义 activation="sigmoid" 以获得漂亮的图像(输出间隔 [0.0, 1.0]),则训练损失会发散,因为 Keras 没有自动使用 logits,而是将 sigmoid 激活输入到失利。因此,在最后一层使用activation="linear" 进行训练会更好更快。在 TensorFlow 中,我只需定义两个张量 logits=xoutput=sigmoid(x) 以便能够在任何自定义损失函数中使用 logits 并在绘图或其他应用程序中使用 output

我怎么会在 Keras 中做这样的事情?

此外,如果我有多个输出,如何在自定义损失函数中使用它们?就像 VAE 的 KL 散度或 GAN 的损失项。

functional API guide 不是很有帮助(尤其是当您将其与 TensorFlow 的超广泛指南进行比较时),因为它仅涵盖基本的 LSTM 示例,您不必必须自己定义任何内容,但是只使用预定义的损失函数。

【问题讨论】:

  • 你的意思是一个输出只是'悬空'而不用于训练,对吧?
  • @mrgloom 是的,完全正确。
  • 我认为可以通过定义具有一个头和一些悬空输出的模型,然后您可以创建从keras.callbacks.Callback 派生的CustomCallback,其中在纪元结束时,您可以像这样从悬空输出层获得输出stackoverflow.com/questions/41711190/… 并将其传递给 tensorboard。

标签: python keras deep-learning


【解决方案1】:

在 TensorFlow 中,我只需定义两个张量 logits=x 和 output=sigmoid(x) 即可在任何自定义损失函数中使用 logits,并输出用于绘图或其他应用程序。

在 Keras 中你做的完全一样:

x = Conv2D(1, (3, 3), activation="linear")(x)
dec_out = resize_images(x, (28, 28))  # Output tensor used in training, for the loss function

training_model = Model(dec_in, dec_out, name="decoder")

...

sigmoid = Activation('sigmoid')(dec_out)
inference_model = Model(dec_in, sigmoid)

training_model.fit(x=X_train, y=X_train, batch_size=256, epochs=10)

prediction = inference_model.predict(some_input)

在 Keras 世界中,如果您有一个输出张量,您的生活会变得更加轻松。然后,您可以获得适用于它的标准 Keras 功能。对于两个输出/损失,一种可能的解决方法是在输出之前将它们连接起来,然后在损失函数中再次拆分。一个很好的例子是 SSD 实现,它有分类和定位损失:https://github.com/pierluigiferrari/ssd_keras/blob/master/keras_loss_function/keras_ssd_loss.py#L133

总的来说,我不明白那些抱怨。可以理解,一个新框架一开始会让人沮丧,但 Keras 很棒,因为它可以在您需要标准的东西时变得简单,而在您需要超越时可以灵活。 Keras model zoo 中复杂模型的实现数量是一个很好的理由。通过阅读该代码,您可以了解在 Keras 中构建模型的各种模式。

【讨论】:

  • 感谢您的澄清。查看 SSD 文件,这是否意味着我们会做类似fit(x=X, y=[labels, aux_labels]) 之类的事情,并为具有辅助输出的模型同样定义我们的模型Model(inputs, [outputs, aux_outputs]?那么我的custom_loss 函数将如何接收它的参数呢? y_pred 的结构会是[main_outputs, aux_outputs] 的列表,那么y_true 是否由[labels, aux_labels] 组成?
  • 在 SSD 实现中,作者更喜欢将标签编码到一个单一的矩阵中:github.com/pierluigiferrari/ssd_keras/blob/master/… 这里,boxes 部分将用于回归损失,classes - 用于分类。但是,拥有两个输入和输出可以像您编写的那样简单。看到这个:keras.io/getting-started/functional-api-guide/…
猜你喜欢
  • 1970-01-01
  • 2019-01-11
  • 1970-01-01
  • 2020-04-12
  • 1970-01-01
  • 2018-05-25
  • 2021-05-23
  • 2021-10-25
  • 2020-12-19
相关资源
最近更新 更多