如何实现多类语义分割?

Posted

技术标签:

【中文标题】如何实现多类语义分割?【英文标题】:How to implement multi-class semantic segmentation? 【发布时间】:2017-10-09 13:17:25 【问题描述】:

我能够使用具有二元分类的标记图像训练 U-net。

但我很难弄清楚如何在 Keras/Theano 中为 多类 分类(4 类)配置最终层。

我有 634 张图像和对应的 634 个掩码,它们是 unit8 和 64 x 64 像素。

我的面具不是黑色 (0) 和白色 (1),而是在 3 个类别和背景中使用颜色标记的对象,如下所示:

黑色 (0),背景 红色 (1),对象等级 1 绿色 (2),对象等级 2 黄色 (3),对象等级 3

在训练运行之前,包含掩码的数组被 one-hot 编码如下:

mask_train = to_categorical(mask_train, 4)

这使得mask_train.shape(634, 1, 64, 64) 变为(2596864, 4)

我的模型严格遵循 Unet 架构,但最后一层似乎有问题,因为我无法展平结构以匹配 one-hot 编码数组。

[...]
up3 = concatenate([UpSampling2D(size=(2, 2))(conv7), conv2], axis=1)
conv8 = Conv2D(128, (3, 3), activation='relu', padding='same')(up3)
conv8 = Conv2D(128, (3, 3), activation='relu', padding='same')(conv8)

up4 = concatenate([UpSampling2D(size=(2, 2))(conv8), conv1], axis=1)
conv9 = Conv2D(64, (3, 3), activation='relu', padding='same')(up4)
conv10 = Conv2D(64, (3, 3), activation='relu', padding='same')(conv9)

# here I used number classes = number of filters and softmax although
# not sure if a dense layer should be here instead
conv11 = Conv2D(4, (1, 1), activation='softmax')(conv10)

model = Model(inputs=[inputs], outputs=[conv11])

# here categorical cross entropy is being used but may not be correct
model.compile(optimizer='sgd', loss='categorical_crossentropy',
              metrics=['accuracy'])

return model

您对如何修改模型的最后部分以便成功训练有什么建议吗?我得到了各种形状不匹配的错误,并且几次我设法让它运行,损失在整个 epoch 中都没有改变。

【问题讨论】:

【参考方案1】:

如果您使用的是 channels_first,您的目标应该是 (634,4,64,64)。 或者 (634,64,64,4) 如果是 channels_last。

你的目标的每个频道都应该是一个类。每个通道都是 0 和 1 的图像,其中 1 表示像素是该类,0 表示像素不是该类。

然后,您的目标是 634 个组,每组包含四个图像,每个图像具有 64x64 像素,其中像素 1 表示存在所需的特征。

我不确定结果是否会正确排序,但您可以尝试:

mask_train = to_categorical(mask_train, 4)
mask_train = mask_train.reshape((634,64,64,4)) 
#I chose channels last here because to_categorical is outputing your classes last: (2596864,4)

#moving the channel:
mask_train = np.moveaxis(mask_train,-1,1)

如果排序不正常,您可以手动进行:

newMask = np.zeros((634,4,64,64))

for samp in range(len(mask_train)):
    im = mask_train[samp,0]
    for x in range(len(im)):
        row = im[x]
        for y in range(len(row)):
            y_val = row[y]
            newMask[samp,y_val,x,y] = 1

【讨论】:

我使用的是 Theano 后端,所以这意味着通道优先——你认为我模型中的最后一层看起来正确吗? 定义通道是第一个还是最后一个是“keras”,而不是 theano。默认是最后一个频道。 是的,但是我在 .keras.json 中为 Theano 正确设置了 Keras,所以我的关注点转向了模型,因为我不确定在最后阶段应该如何塑造它跨度> 要查看最后一层是否正常,请执行model.summary() 并查看其输出是否为(None,64,64,4)。这似乎是正确的,因为你有 4 个过滤器,它会给你四个通道,但我不能说卷积会导致 (64,64)。如果你在所有层都使用padding = 'same',包括最后一层,应该没问题。 有趣的想法,谢谢—.summary() 显示 (None, 4, 64, 64) 用于最后一层...【参考方案2】:

有点晚了,但你应该试试

mask_train = to_categorical(mask_train, num_classes=None)

这将为mask_train.shape 生成(634, 4, 64, 64),并为每个单独的类生成一个二进制掩码(单热编码)。

最后一个卷积层、激活和损失看起来很适合多类分割。

【讨论】:

以上是关于如何实现多类语义分割?的主要内容,如果未能解决你的问题,请参考以下文章

多类语义分割模型评估

多类语义分割 - 输出激活?

如何为多类语义分割预处理 RGB 分割掩码?

在 RGB 图像上绘制多类语义分割透明覆盖

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

keras/tensorflow中语义图像分割的多类加权损失