我相信这是一个愚蠢的问题,但我在其他地方找不到答案,所以我要在这里问一下。
我正在使用keras中的cnn(unet)进行语义图像分割,使用theano后端带有7个标签。因此,每个图像的标签为(7,n_rows,n_cols)。因此,在每个像素的7个图层上,它被单热编码。在这种情况下,正确的错误函数是使用分类交叉熵吗?对我来说似乎是这样,但网络似乎通过二进制交叉熵损失更好地学习。有人能解释一下为什么会这样,并说明原则性的目标是什么吗?
我相信这是一个愚蠢的问题,但我在其他地方找不到答案,所以我要在这里问一下。
我正在使用keras中的cnn(unet)进行语义图像分割,使用theano后端带有7个标签。因此,每个图像的标签为(7,n_rows,n_cols)。因此,在每个像素的7个图层上,它被单热编码。在这种情况下,正确的错误函数是使用分类交叉熵吗?对我来说似乎是这样,但网络似乎通过二进制交叉熵损失更好地学习。有人能解释一下为什么会这样,并说明原则性的目标是什么吗?
在最后一层使用 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
层。我测试了@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
此方法将期望矩阵的排序为 (高度,宽度,通道)。
sigmoid
和二元交叉熵损失,并且一旦训练完成,替换顶层并以softmax
结束,再用分类交叉熵重新训练。第二次训练会很快收敛,但我敢打赌,总体训练时间会减少,并且精度会更高。 - indraforyouaxis=0
是指沿着行求和吗?我会认为应该是axis=-1
,因为你想沿深度求和(但将axis=0
替换为axis=1
不起作用,它会抛出一个错误)。此外,这真的会得到softmax吗?我会认为应该是lambda x: K.exp(x)
。 - AljoSt