为啥训练多类语义分割的unet模型中的分类交叉熵损失函数非常高?

Posted

技术标签:

【中文标题】为啥训练多类语义分割的unet模型中的分类交叉熵损失函数非常高?【英文标题】:why categorical cross entropy loss function in training unet model for multiclass semantic segmentation is very high?为什么训练多类语义分割的unet模型中的分类交叉熵损失函数非常高? 【发布时间】:2020-11-01 01:29:30 【问题描述】:

我想使用 Unet 模型对 CMR 图像数据集进行语义分割。该模型非常适用于其他 CMR 图像,但在将其应用于新数据集时,它的行为很奇怪。我使用分类交叉熵作为损失函数将掩码分割成 4 个类别,包括背景。这是 Unet 模型(我从一个 github 页面得到它,现在我不记得地址了)我正在使用:

def down_block(x, filters, kernel_size=(3, 3), padding="same", strides=1):
    c = keras.layers.Conv2D(filters, kernel_size, padding=padding, strides=strides, activation="relu")(x)
    c = keras.layers.Conv2D(filters, kernel_size, padding=padding, strides=strides, activation="relu")(c)
    p = keras.layers.MaxPool2D((2, 2), (2, 2))(c)
    return c, p

def up_block(x, skip, filters, kernel_size=(3, 3), padding="same", strides=1):
    us = keras.layers.UpSampling2D((2, 2))(x)
    concat = keras.layers.Concatenate()([us, skip])
    c = keras.layers.Conv2D(filters, kernel_size, padding=padding, strides=strides, activation="relu")(concat)
    c = keras.layers.Conv2D(filters, kernel_size, padding=padding, strides=strides, activation="relu")(c)
    return c

def bottleneck(x, filters, kernel_size=(3, 3), padding="same", strides=1):
    c = keras.layers.Conv2D(filters, kernel_size, padding=padding, strides=strides, activation="relu")(x)
    c = keras.layers.Conv2D(filters, kernel_size, padding=padding, strides=strides, activation="relu")(c)
    return c

def UNet(image_size, nclasses=4, filters=64):
    f = [16, 32, 64, 128, 256]
    inputs = keras.layers.Input((image_size, image_size,1))
    
    p0 = inputs
    c1, p1 = down_block(p0, f[0]) #128 -> 64 ##(do we aim to get 16 feature maps? isn't is by using different masks?)
    c2, p2 = down_block(p1, f[1]) #64 -> 32
    c3, p3 = down_block(p2, f[2]) #32 -> 16
    c4, p4 = down_block(p3, f[3]) #16->8
    
    bn = bottleneck(p4, f[4])
    
    u1 = up_block(bn, c4, f[3]) #8 -> 16
    u2 = up_block(u1, c3, f[2]) #16 -> 32
    u3 = up_block(u2, c2, f[1]) #32 -> 64
    u4 = up_block(u3, c1, f[0]) #64 -> 128
    
    outputs = keras.layers.Conv2D(nclasses, (1, 1), padding="same", activation="softmax")(u4)
    model = keras.models.Model(inputs, outputs)
    return model
image_size = 256
model = UNet(image_size)
optimizer = keras.optimizers.SGD(lr=0.0001, momentum=0.9)
model.compile(optimizer= optimizer, loss='sparse_categorical_crossentropy' , metrics=["accuracy"])

我还使用了to_categorical 函数来处理蒙版图像。问题是预测的掩码是一个空白图像,这可能是因为它只预测背景类,因为数据集不平衡。此外,损失值从 1.4 左右开始下降到 1.3,这表明模型学习得很少。 如果有人向我解释解决方案,我将不胜感激......

附:我应该平衡数据集的拳头吗?如果是怎么办?

【问题讨论】:

【参考方案1】:

你的方法有两个问题,首先你说你使用了“to_categorical function”,这不是处理“Sparse categorical crossentropy loss”的方法。

如果您想对掩码数据使用“to_categorical”函数,则必须使用“CategoricalCrossEntropy”损失。

现在,如果您想使用带有“0,1,2,3”等标签的 RAW 掩码数据,您可以使用“Sparse Categorical CrossEntropy loss”,但使用“From logits = True”:

model.compile(optimizer= optimizer, 
             loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
             metrics=["accuracy"])

并且不要在最后一层使用 softmax 作为激活,它会返回你的类的“边缘”(在最好的情况下)而不是它们的语义分割(不使用激活函数就可以了)

【讨论】:

感谢您的回答。是的,你是对的,我在损失函数方面犯了一个错误,但分类交叉熵的问题仍然存在。我认为这是因为机器没有通过定义的模型通过数据集进行学习。所以,我改变了模型架构,问题就解决了。

以上是关于为啥训练多类语义分割的unet模型中的分类交叉熵损失函数非常高?的主要内容,如果未能解决你的问题,请参考以下文章

深度学习:使用UNet做图像语义分割,训练自己制作的数据集,详细教程

基于MindSpore复现UNet—语义分割

Pytorch之图像分割(多目标分割,Multi Object Segmentation)

多类语义分割模型评估

如何实现多类语义分割?

Unet 语义分割模型(Keras)| 以细胞图像为例