使用Keras进行语义分割的交叉熵损失函数

16

我相信这是一个愚蠢的问题,但我在其他地方找不到答案,所以我要在这里问一下。

我正在使用keras中的cnn(unet)进行语义图像分割,使用theano后端带有7个标签。因此,每个图像的标签为(7,n_rows,n_cols)。因此,在每个像素的7个图层上,它被单热编码。在这种情况下,正确的错误函数是使用分类交叉熵吗?对我来说似乎是这样,但网络似乎通过二进制交叉熵损失更好地学习。有人能解释一下为什么会这样,并说明原则性的目标是什么吗?

2个回答

16

在最后一层使用 sigmoid 激活函数时应该使用二元交叉熵损失,它严厉惩罚相反的预测。它不考虑输出是 one-hot 编码的,而且预测值之和应该为 1。但由于错误的预测会严重惩罚模型,因此它会学习正确地分类。

现在要强制使用 one-hot 编码的先验知识,可以使用分类交叉熵和 softmax 激活函数。这是你应该使用的方法。

问题在于,在你的案例中使用 softmax ,因为 Keras 不支持在每个像素上使用 softmax 。

解决方法是使用 Permute 层将维度排列为(n_rows,n_cols,7),然后使用 Reshape 层将其重塑为(n_rows*n_cols,7)。然后添加 softmax 激活层并使用交叉熵损失。数据也应相应地进行重塑。

另一种实现方法是实现深度 softmax :

def depth_softmax(matrix):
    sigmoid = lambda x: 1 / (1 + K.exp(-x))
    sigmoided_matrix = sigmoid(matrix)
    softmax_matrix = sigmoided_matrix / K.sum(sigmoided_matrix, axis=0)
    return softmax_matrix

然后将其用作lambda层:

model.add(Deconvolution2D(7, 1, 1, border_mode='same', output_shape=(7,n_rows,n_cols)))
model.add(Permute(2,3,1))
model.add(BatchNormalization())
model.add(Lambda(depth_softmax))
如果使用tf image_dim_ordering,则可以省去Permute层。
更多参考请查看这里

非常感谢您详细的回答!我选择了reshape、softmax和分类交叉熵。您认为这两种方法在速度或最终准确性方面是否存在实质性的性能差异?再次感谢! - TSW
我自己没有处理过这种情况,但你可以检查一下它们两个。另外一个你可以尝试的事情是首先创建一个模型,最后一层为 sigmoid 和二元交叉熵损失,并且一旦训练完成,替换顶层并以 softmax 结束,再用分类交叉熵重新训练。第二次训练会很快收敛,但我敢打赌,总体训练时间会减少,并且精度会更高。 - indraforyou
嗨indraforyou,我也在处理语义分割案例。掩蔽图像表示为(1,n_rows,n_cols)。对于这种情况,我可以使用sigmoid和二进制交叉熵吗?是否需要包含任何特定的程序? - user297850
@user297850 对于单通道,您不需要做任何特殊处理。您可以简单地使用sigmoid和二元交叉熵。 - indraforyou
@indraforyou,我对你的函数有点困惑。axis=0是指沿着行求和吗?我会认为应该是axis=-1,因为你想沿深度求和(但将axis=0替换为axis=1不起作用,它会抛出一个错误)。此外,这真的会得到softmax吗?我会认为应该是lambda x: K.exp(x) - AljoSt

0

我测试了@indraforyou的解决方案,认为提出的方法有一些错误。由于评论部分不允许适当的代码段,这里是我认为修复后的版本:

def depth_softmax(matrix):

    from keras import backend as K

    exp_matrix = K.exp(matrix)
    softmax_matrix = exp_matrix / K.expand_dims(K.sum(exp_matrix, axis=-1), axis=-1)
    return softmax_matrix

此方法将期望矩阵的排序为 (高度,宽度,通道)。


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