Keras:多输出和自定义损失函数的清晰实现吗?

时间:2019-01-28 11:25:59

标签: python keras deep-learning

来自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没有自动使用logit,而是提供了S形激活陷入损失。因此,培训在最后一层使用activation="linear"会更好,更快。在TensorFlow中,我只需定义两个张量logits=xoutput=sigmoid(x)即可在任何自定义损失函数中使用logits,并在绘图或其他应用程序中使用output

我将如何在Keras中做这样的事情?

此外,如果我有多个输出,如何在自定义损失函数中使用它们?例如VAE的KL差异或GAN的亏损条件。

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

1 个答案:

答案 0 :(得分:1)

  

在TensorFlow中,我仅定义两个Tensors 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中构建模型的各种模式。