使用隐藏层输出作为目标的Keras自定义损失函数

3
我将尝试在Keras中实现一个自编码器,不仅可以最小化重构误差,而且其构建的特征还应该最大化我定义的一个度量。目前我真的不知道如何做到这一点。
以下是我目前的代码片段:
corrupt_data = self._corrupt(self.data, 0.1)

# define encoder-decoder network structure
# create input layer
input_layer = Input(shape=(corrupt_data.shape[1], ))
encoded = Dense(self.encoding_dim, activation = "relu")(input_layer)
decoded = Dense(self.data.shape[1], activation="sigmoid")(encoded)

# create autoencoder
dae = Model(input_layer, decoded)

# define custom multitask loss with wlm measure
def multitask_loss(y_true, y_pred):
    # extract learned features from hidden layer
    learned_fea = Model(input_layer, encoded).predict(self.data)
    # additional measure I want to optimize from an external function
    wlm_measure = wlm.measure(learned_fea, self.labels)
    cross_entropy = losses.binary_crossentropy(y_true, y_pred)
    return wlm_measure + cross_entropy

# create optimizer
dae.compile(optimizer=self.optimizer, loss=multitask_loss)

dae.fit(corrupt_data, self.data, 
                epochs=self.epochs, batch_size=20, shuffle=True, 
                callbacks=[tensorboard])

# separately create an encoder model
encoder = Model(input_layer, encoded)

目前这个功能不能正常工作... 当我查看训练历史时,模型似乎忽略了额外的度量,并仅基于交叉熵损失进行训练。此外,如果我将损失函数更改为仅考虑wlm度量,我会得到错误信息“numpy.float64”对象没有属性“get_shape”(我不知道将我的wlm函数的返回类型更改为张量是否有帮助)。

我认为可能有几个地方出错了。我不知道我是否在自定义损失函数中正确提取了隐藏层的输出。此外,我不知道我的wlm.measure函数是否输出正确——它应该输出numpy.float32还是float32类型的1维张量。

基本上,传统的损失函数只关心输出层的预测标签和真实标签。在我的情况下,我还需要考虑隐藏层的输出(激活),这在Keras中实现起来并不那么简单。

感谢您的帮助!


这里提供了一个简单的解决方案,用于在损失计算中放置中间层:https://stackoverflow.com/questions/62454500/how-to-use-tensorflow-custom-loss-for-a-keras-model - Marco Cerliani
1个回答

3
你不希望在你的自定义损失函数中定义你的已学特征模型。相反,你可以事先定义一个具有两个输出的单一模型:解码器的输出(重构)和编码器的输出(特征表示):
multi_output_model = Model(inputs=input_layer, outputs=[decoded, encoded])

现在您可以编写一个自定义损失函数,只适用于编码器的输出:
def custom_loss(y_true, y_pred):
    return wlm.measure(y_pred, y_true)

编译模型时,您需要传入一组损失函数(如果您为张量命名,则可以传入字典形式):
model.compile(loss=['binary_crossentropy', custom_loss], optimizer=...)

通过传递输出列表来拟合模型:

model.fit(X=X, y=[data_to_be_reconstructed,labels_for_wlm_measure])

感谢您的回答!在这种情况下,模型是两次拟合,每次使用不同的损失吗?最终模型是否共享相同的编码层或者是训练了两个不同的编码层?看起来像是创建了两个独立的模型,但我可能错了...此外,如果编码层用于两个目标,那么我如何指定我想要支持哪个目标?比如说,我更关心优化wlm_measure而非优化重构误差,我该怎么做呢? - Leo Appleseed
不,只创建并针对您提供的损失函数列表的平均值进行优化一个模型。因此只有一个“共享”的编码层。如果您想强调其中一个损失函数,model.compile() 有一个参数 loss_weights 可以让您这样做。请查看文档获取详细信息。 - sdcbr
我明白了,这很有道理!那么现在当我调用预测函数时,我的模型会产生两个输出吗?一个输出是重构的特征(输出层的输出),另一个输出是学习到的特征(编码器层的输出)? - Leo Appleseed
太好了!问题解决了,非常感谢!最后一个问题:如果我要制作一个多输出模型,那么我需要将wlm_measure作为单独的损失函数。wlm_measure需要所有样本的学习特征(维度是编码维度)和标签数组(维度是原始数据集的维度,即样本数)来计算。这与y_true、y_pred格式并不完全匹配。换句话说,我需要访问所有样本的学习特征以计算损失。有什么办法可以做到这一点吗? - Leo Appleseed
抱歉造成困惑。我真正想问的是,对于y_true和y_pred参数,是将该批次中所有样本的y_pred传递进去,还是每次传递该批次中每个样本的y_pred?我需要该批次中所有样本的y_pred来计算度量值。 - Leo Appleseed
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接