Keras/Tensorflow:单输出的组合损失函数

Posted

技术标签:

【中文标题】Keras/Tensorflow:单输出的组合损失函数【英文标题】:Keras/Tensorflow: Combined Loss function for single output 【发布时间】:2019-01-13 06:51:14 【问题描述】:

我的模型只有一个输出,但我想结合两种不同的损失函数:

def get_model():
    # create the model here
    model = Model(inputs=image, outputs=output)

    alpha = 0.2
    model.compile(loss=[mse, gse],
                      loss_weights=[1-alpha, alpha]
                      , ...)

但它抱怨我需要有两个输出,因为我定义了两个损失:

ValueError: When passing a list as loss, it should have one entry per model outputs. 
The model has 1 outputs, but you passed loss=[<function mse at 0x0000024D7E1FB378>, <function gse at 0x0000024D7E1FB510>]

我是否可以在不必创建另一个损失函数的情况下编写我的最终损失函数(因为这会限制我在损失函数之外更改 alpha)?

我该怎么做(1-alpha)*mse + alpha*gse


更新:

我的两个损失函数都等效于任何内置 keras 损失函数的函数签名,接受 y_truey_pred 并为损失返回张量(可以使用 K.mean() 将其简化为标量),但我相信,只要它们返回有效的损失,这些损失函数的定义方式就不会影响答案。

def gse(y_true, y_pred):
    # some tensor operation on y_pred and y_true
    return K.mean(K.square(y_pred - y_true), axis=-1)

【问题讨论】:

你能添加你的 gse 函数吗? 这里有一个简单的解决方案:***.com/questions/62861773/… 【参考方案1】:

并不是说这个答案特别解决了原始问题,我想写它是因为在尝试使用 keras.models.load_model 加载具有自定义损失的 keras 模型时会发生相同的错误,并且在任何地方都没有得到正确的回答。具体来说,按照keras github repository中的VAE示例代码,使用model.save保存后加载VAE模型时会出现此错误。

解决方案是使用vae.save_weights('file.h5') 仅保存权重,而不是保存完整模型。但是,在使用 vae.load_weights('file.h5') 加载权重之前,您必须再次构建和编译模型。

以下是一个示例实现。

class VAE():
    def build_model(self): # latent_dim and intermediate_dim can be passed as arguments
        def sampling(args):
            """Reparameterization trick by sampling from an isotropic unit Gaussian.
            # Arguments
                args (tensor): mean and log of variance of Q(z|X)
            # Returns
                z (tensor): sampled latent vector
            """

            z_mean, z_log_var = args
            batch = K.shape(z_mean)[0]
            dim = K.int_shape(z_mean)[1]
            # by default, random_normal has mean = 0 and std = 1.0
            epsilon = K.random_normal(shape=(batch, dim))
            return z_mean + K.exp(0.5 * z_log_var) * epsilon

        # original_dim = self.no_features
        # intermediate_dim = 256
        latent_dim = 8
        inputs = Input(shape=(self.no_features,))
        x = Dense(256, activation='relu')(inputs)
        x = Dense(128, activation='relu')(x)
        x = Dense(64, activation='relu')(x)
        z_mean = Dense(latent_dim, name='z_mean')(x)
        z_log_var = Dense(latent_dim, name='z_log_var')(x)
        # use reparameterization trick to push the sampling out as input
        # note that "output_shape" isn't necessary with the TensorFlow backend
        z = Lambda(sampling, output_shape=(latent_dim,), name='z')([z_mean, z_log_var])
        # instantiate encoder model
        encoder = Model(inputs, [z_mean, z_log_var, z], name='encoder')


        # build decoder model
        latent_inputs = Input(shape=(latent_dim,), name='z_sampling')
        x = Dense(32, activation='relu')(latent_inputs)
        x = Dense(48, activation='relu')(x)
        x = Dense(64, activation='relu')(x)
        outputs = Dense(self.no_features, activation='linear')(x)

        # instantiate decoder model
        decoder = Model(latent_inputs, outputs, name='decoder')

        # instantiate VAE model
        outputs = decoder(encoder(inputs)[2])
        VAE = Model(inputs, outputs, name='vae_mlp')
        reconstruction_loss = mse(inputs, outputs)
        reconstruction_loss *= self.no_features
        kl_loss = 1 + z_log_var - K.square(z_mean) - K.exp(z_log_var)
        kl_loss = K.sum(kl_loss, axis=-1)
        kl_loss *= -0.5
        vae_loss = K.mean(reconstruction_loss + kl_loss)
        VAE.add_loss(vae_loss)
        VAE.compile(optimizer='adam')
        return VAE

现在,

vae_cls = VAE()
vae = vae_cls.build_model()
# vae.fit()
vae.save_weights('file.h5')

加载模型并预测(如果在不同的脚本中,则需要导入VAE 类),

vae_cls = VAE()
vae = vae_cls.build_model()
vae.load_weights('file.h5')
# vae.predict()

最后,区别:[ref]

Keras model.save 保存,

    模型权重 模型架构 模型编译详细信息(损失函数和指标) 模型优化器和正则化器状态

Keras model.save_weights 仅保存模型权重。 Keras model.to_json() 保存模型架构。

希望这对尝试变分自动编码器的人有所帮助。

【讨论】:

【参考方案2】:

loss 函数应该是一个函数。你给你的模型一个包含两个函数的列表

尝试:

def mse(y_true, y_pred):
    return K.mean(K.square(y_pred - y_true), axis=-1)

model.compile(loss= (mse(y_true, y_pred)*(1-alpha) + gse(y_true, y_pred)*alpha),
              , ...)

【讨论】:

你的意思是loss=(mse*(1-alpha) + gse*alpha) 我不认为我们可以在 Python 中对函数进行算术运算,但无论如何我尝试了但得到了TypeError: unsupported operand type(s) for *: 'function' and 'float' 什么是gse函数? 这与您在答案中定义 mse 的方式完全相同。问题是它不知道function * float 是什么意思,所以它只是抱怨unsupported operand【参考方案3】:

为损失指定一个自定义函数:

model = Model(inputs=image, outputs=output)

alpha = 0.2
model.compile(
    loss=lambda y_true, y_pred: (1 - alpha) * mse(y_true, y_pred) + alpha * gse(y_true, y_pred),
    ...)

或者,如果您不想让丑陋的 lambda 成为实际函数:

def my_loss(y_true, y_pred):
    return (1 - alpha) * mse(y_true, y_pred) + alpha * gse(y_true, y_pred)

model = Model(inputs=image, outputs=output)

alpha = 0.2
model.compile(loss=my_loss, ...)

编辑:

如果你的alpha 不是全局常量,你可以有一个“损失函数工厂”:

def make_my_loss(alpha):
    def my_loss(y_true, y_pred):
        return (1 - alpha) * mse(y_true, y_pred) + alpha * gse(y_true, y_pred)
    return my_loss

model = Model(inputs=image, outputs=output)

alpha = 0.2
my_loss = make_my_loss(alpha)
model.compile(loss=my_loss, ...)

【讨论】:

通常当我定义自定义损失时,我必须在加载模型时在 dict 中传递它,但是如果我传递一个 lambda,我是否需要以某种方式传递它还是它会正常工作? @SaravanabalagiRamachandran 不确定您指的是什么字典...您是在谈论模型定义部分,还是检查点,还是其他?您通常需要将您定义的自定义损失放在哪里? 如果我保存一个带有自定义损失的模型,我将不得不告诉 keras 在加载相同的模型时它们是什么。所以import gse from somewhere 然后load_model(model_file, custom_objects='gse':gsecustom_objects dict...! @SaravanabalagiRamachandran 啊,我明白了,抱歉,我不知道 Keras 模型的这个 API。我只使用 TensorFlow 工具保存和恢复了模型,我不确定 Keras 机制是如何工作的,所以我不确定,但似乎所有非标准函数(包括 lambdas,我怀疑)都有以custom_objects 传递或由CustomObjectScope 提供... 是的,它似乎无法按预期获得匿名函数。保存后加载模型时抛出ValueError: Unknown loss function:&lt;lambda&gt;错误。【参考方案4】:

是的,定义您自己的自定义损失函数并在编译时将其传递给loss 参数:

def custom_loss(y_true, y_pred):
    return (1-alpha) * K.mean(K.square(y_true-y_pred)) + alpha * gse

(不确定gse 是什么意思)。看看在 Keras 中如何实现 vanilla loss 会有所帮助:https://github.com/keras-team/keras/blob/master/keras/losses.py

【讨论】:

gse 是一个类似于mse 的函数(更新了问题),所以我们必须在调用时将y_truey_pred 传递给它,我们不能这样做@ 987654329@. 因为gse是一个函数。尝试将其写成上面的单个自定义损失函数。 对不起,如果我误解了你,但 alpha 将是一个标量,如 0.10.2 并且 keras 不知道如何将函数 gsemse 与标量alpha 会抛出TypeError: unsupported operand type(s) for *: 'function' and 'float',不是吗? 对不起,我的意思是 gse 是一个函数。如果你把它写成一个像上面一样的自定义损失函数,用gse替换它的定义,你还会得到错误吗? 明白了,您建议创建另一个包含两个损失的损失函数。但是当我定义模型时无法更改 alpha :(

以上是关于Keras/Tensorflow:单输出的组合损失函数的主要内容,如果未能解决你的问题,请参考以下文章

如何在keras tensorflow中将图像作为输入并获取另一个图像作为输出

多路输出的单路损耗

为啥 Keras 损失在第一个 epoch 之后急剧下降?

Keras,Tensorflow:将两个不同的模型输出合并为一个

让 Keras / Tensorflow 输出 OneHotCategorical,但操作没有梯度

具有对数损失的 TensorFlow 单 sigmoid 输出与具有稀疏 softmax 交叉熵损失的两个线性输出,用于二进制分类