如何用Keras构建一个多类卷积神经网络

时间:2017-08-29 12:40:57

标签: python tensorflow keras conv-neural-network image-segmentation

我正在尝试使用带有Tensorflow后端的Keras实现U-Net进行图像分割任务。我有大小(128,96)的图像作为网络的输入以及尺寸(12288,6)的掩模图像,因为它们是扁平的。我有6个不同的类(0-5),它给出了掩模图像的第二部分'形状。它们已使用to_categorical()函数编码为单热标签。目前我只使用一个输入图像,并使用相同的一个作为验证和测试数据。

我希望U-Net执行图像分割,其中0级对应于背景。当我现在仅在几个纪元(1-10)中训练我的U-Net时,得到的预测掩模图像似乎只给每个像素提供随机类。当我训练网络更长(50个以上的纪元)时,所有像素都被归类为背景。由于我使用相同的图像进行训练和测试,我发现这非常奇怪,因为我正在加速网络过度训练。我该如何解决这个问题?我将掩模图像和真实图像提供给网络的方式有问题吗?

我已经尝试过手动给网络加权,以减少对背景的重视,并尝试了不同的损失组合,不同的塑造蒙版图像的方法以及许多其他方面,但没有给出好的结果。

以下是我的网络代码。它基于取自this repository的U-Net。我设法为两个案例训练它并取得了不错的成绩,但我现在还不知道如何将它扩展到更多的课程。

def get_unet(self):

    inputs = Input((128, 96,1))
    #Input shape=(?,128,96,1)

    conv1 = Conv2D(64, (3,3), activation = 'relu', padding = 'same',
      kernel_initializer = 'he_normal', input_shape=(None,128,96,6))(inputs)
    #Conv1 shape=(?,128,96,64)
    conv1 = Conv2D(64, (3,3), activation = 'relu', padding = 'same',
          kernel_initializer = 'he_normal')(conv1)
    #Conv1 shape=(?,128,96,64)
    pool1 = MaxPooling2D(pool_size=(2, 2))(conv1)
    #pool1 shape=(?,64,48,64)


    conv2 = Conv2D(128, 3, activation = 'relu', padding = 'same',
         kernel_initializer = 'he_normal')(pool1)
    #Conv2 shape=(?,64,48,128)
    conv2 = Conv2D(128, 3, activation = 'relu', padding = 'same',
         kernel_initializer = 'he_normal')(conv2)
    #Conv2 shape=(?,64,48,128)
    pool2 = MaxPooling2D(pool_size=(2, 2))(conv2)
    #Pool2 shape=(?,32,24,128)

    conv5 = Conv2D(256, (3,3), activation = 'relu', padding = 'same',
         kernel_initializer = 'he_normal')(pool2)
    conv5 = Conv2D(256, (3,3), activation = 'relu', padding = 'same',
         kernel_initializer = 'he_normal')(conv5)

    up8 = Conv2D(128, 2, activation = 'relu', padding = 'same',
        kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv5))
    merge8 = concatenate([conv2,up8], axis = 3)
    conv8 = Conv2D(128, 3, activation = 'relu', padding = 'same',
         kernel_initializer = 'he_normal')(merge8)
    conv8 = Conv2D(128, 3, activation = 'relu', padding = 'same',
         kernel_initializer = 'he_normal')(conv8)


    up9 = Conv2D(64, (2,2), activation = 'relu', padding = 'same',
        kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv8))
    merge9 = concatenate([conv1,up9], axis = 3)
    conv9 = Conv2D(64, (3,3), activation = 'relu', padding = 'same',
        kernel_initializer = 'he_normal')(merge9)
    conv9 = Conv2D(64, (3,3), activation = 'relu', padding = 'same',
        kernel_initializer = 'he_normal')(conv9)
    conv9 = Conv2D(6, (3,3), activation = 'relu', padding = 'same',
        kernel_initializer = 'he_normal')(conv9)

    conv10 = Conv2D(6, (1,1), activation = 'sigmoid')(conv9)
    conv10 = Reshape((128*96,6))(conv10)

    model = Model(input = inputs, output = conv10)
    model.compile(optimizer = Adam(lr = 1e-5), loss = 'binary_crossentropy',
          metrics = ['accuracy'])

    return model

有谁可以指出我的模型有什么问题?

3 个答案:

答案 0 :(得分:2)

谢谢@Daniel,你的建议最终帮助我让Unet工作。我设法得到的结果不仅仅是在运行500多个时代时将整个图像分类为背景。此外,我没有使用kernel_initializer='he_normal'kernel_initializer='zeros'kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.07)。我使用'sigmoid'激活函数和loss='binary_crossentropy'。我对所有隐藏的卷积层保持“relu”激活。我注意到我的网络有时会陷入局部最小值,而损失不再有所改善,所以我需要重启。

答案 1 :(得分:1)

我不知道你的预测层,据我所知,它必须是一个密集的层,而不是一个卷积层。 也许这就是你的问题。

答案 2 :(得分:1)

根据我的经验,也使用U-net进行细分。它倾向于这样做:

  • 完全黑色或完全白色
  • 经过很长一段时间后,损失似乎被冻结了,它找到了它的方式。

我还使用“训练一个图像”方法来找到收敛,然后添加其他图像就可以了。

但是我不得不尝试很多次,而且它使用的唯一时间就是我使用的时间:

  • 最终激活='sigmoid'
  • loss ='binary_crossentropy'

但我并没有在任何地方使用“relu”......也许这会影响收敛速度......?考虑到“relu”,它只有0或正结果,这个函数中有一个没有渐变的大区域。也许有很多“relu”激活创造了很多没有渐变的“平坦”区域? (必须更好地考虑确认)

尝试使用不同的权重初始化几次(并耐心等待许多时期)。

你的学习成绩也有可能太大。

关于to_categorical():您是否试图绘制/打印面具?他们真的看起来像你期望的那样吗?