如何在 Keras 中进行逐点分类交叉熵损失?

Posted

技术标签:

【中文标题】如何在 Keras 中进行逐点分类交叉熵损失?【英文标题】:How to do point-wise categorical crossentropy loss in Keras? 【发布时间】:2017-08-19 09:28:51 【问题描述】:

我有一个生成 4D 输出张量的网络,其中空间维度(~像素)中每个位置的值将被解释为该位置的类概率。换句话说,输出是(num_batches, height, width, num_classes)。我有相同大小的标签,其中真正的类被编码为 one-hot。我想用这个来计算categorical-crossentropy 损失。

问题 #1: K.softmax 函数需要一个 2D 张量 (num_batches, num_classes)

问题 #2:我不确定应该如何组合每个位置的损失。 reshape 张量到(num_batches * height * width, num_classes) 然后调用K.categorical_crossentropy 是否正确?或者更确切地说,调用K.categorical_crossentropy(num_batches, num_classes) height*width 次并平均结果?

【问题讨论】:

你使用哪个后端? @MarcinMożejko 我使用 TensorFlow - 可以使用 Keras 后端 api 或 TF 自己的函数,都可以。谢谢! 我已经回答了你的问题 :-) 老兄 - 你需要查看我们的答案,因为在其他方面,你的第一个答案(你在筹集赏金之前得到的这个答案)会赢:) 【参考方案1】:

找到this issue确认我的直觉。

简而言之:softmax 将采用 2D 或 3D 输入。如果它们是 3D keras 将采用这样的形状(样本、时间维度、numclasses)并在最后一个上应用 softmax。由于一些奇怪的原因,它不会对 4D 张量这样做。

解决方案:将输出重塑为像素序列

reshaped_output = Reshape((height*width, num_classes))(output_tensor)

然后应用你的 softmax

new_output = Activation('softmax')(reshaped_output) 

然后要么将目标张量重塑为 2D,要么将最后一层重塑为 (width, height, num_classes)。

否则,如果我现在不在手机上,我会尝试使用TimeDistributed(Activation('softmax'))。但不知道这是否可行……稍后再试

我希望这会有所帮助:-)

【讨论】:

【参考方案2】:

只需将输出展平为大小为(num_batches, height * width * num_classes) 的二维张量。您可以使用Flatten 层执行此操作。确保您的 y 以相同的方式展平(通常调用 y = y.reshape((num_batches, height * width * num_classes)) 就足够了)。

对于第二个问题,对所有 width*height 预测使用分类交叉熵与为每个 width*height 预测平均分类交叉熵基本相同(根据分类交叉熵的定义)。

【讨论】:

谢谢!我对 (num_batches, height * width * num_classes) 感到困惑。这不是本质上计算交叉熵,就好像有 num_batches 个样本,每个样本都有 height * width * num_classes 个类?我很确定我希望将每个点都算作一个单独的样本,这与重塑为 (num_batches * height * width, num_classes) 不一样吗?请让我知道这两者在数学上是否相等。 抱歉,仅分类交叉熵在数学方面基本上是相同的。但是将它与 softmax 一起使用在数学方面不会是相同的,因为输出是标准化的,这在我的回答中是一个失误。我认为不可能改变模型中输入中点的批量大小。最好的方法可能是解决并实现您自己的 softmax 激活函数,对每个 height * width 单元格的输出进行标准化。 谢谢!我仍然对如何将其转换为工作代码感到困惑,你能试一试吗?我开始了赏金:)【参考方案3】:

您也不能reshape 任何东西,而是自己定义softmaxloss。这是应用于最后一个输入维度的softmax(如tf 后端):

def image_softmax(input):
    label_dim = -1
    d = K.exp(input - K.max(input, axis=label_dim, keepdims=True))
    return d / K.sum(d, axis=label_dim, keepdims=True)

这里有loss(无需重塑任何东西):

__EPS = 1e-5
def image_categorical_crossentropy(y_true, y_pred):
    y_pred = K.clip(y_pred, __EPS, 1 - __EPS)
    return -K.mean(y_true * K.log(y_pred) + (1 - y_true) * K.log(1 - y_pred))

无需进一步重塑。

【讨论】:

【参考方案4】:

现在您似乎可以简单地在最后一个Conv2D 层上进行softmax 激活,然后指定categorical_crossentropy 损失并在图像上进行训练,而无需任何重塑技巧或任何新的损失函数。我尝试过使用虚拟数据集进行过度拟合,效果很好。试试吧~!

inp = keras.Input(...)
# define your model here
out = keras.layers.Conv2D(classes, (1, 1), activation='softmax') (...)
model = keras.Model(inputs=[inp], outputs=[out], name='unet')
model.compile(loss='categorical_crossentropy',
                      optimizer='adam',
                      metrics=['accuracy'])
model.fit(tensor4d, tensor4d)

您还可以使用sparse_categorical_crossentropy 进行编译,然后使用形状为(samples, height, width) 的输出进行训练,其中输出中的每个像素对应一个类标签:model.fit(tensor4d, tensor3d)

想法是softmaxcategorical_crossentropy 将应用于最后一个轴(您可以查看keras.backend.softmaxkeras.backend.categorical_crossentropy 文档)。

PS。我使用来自tensorflow.keraskeras (tensorflow 2)

更新:我已经对我的真实数据集进行了培训,并且它也可以正常工作。

【讨论】:

以上是关于如何在 Keras 中进行逐点分类交叉熵损失?的主要内容,如果未能解决你的问题,请参考以下文章

交叉熵损失和后勤损失之间有什么区别吗?

如何在 Keras 的 FCN(U-Net)上使用加权分类交叉熵?

语义分割 Keras 的交叉熵损失

如何在 keras 中创建自定义损失函数? (自定义加权二元交叉熵)

详解pytorch中的交叉熵损失函数nn.BCELoss()nn.BCELossWithLogits(),二分类任务如何定义损失函数,如何计算准确率如何预测

Keras 版本的组合交叉熵和校准损失