Python 如何用Keras建立多类卷积神经网络

Python 如何用Keras建立多类卷积神经网络,python,tensorflow,keras,conv-neural-network,image-segmentation,Python,Tensorflow,Keras,Conv Neural Network,Image Segmentation,我正在尝试用Keras和Tensorflow后端实现一个用于图像分割任务的U-Net。我有大小为128,96的图像作为网络的输入,还有大小为12288,6的掩码图像,因为它们是扁平的。我有6个不同的类0-5,它给出了遮罩图像形状的第二部分。它们已使用to_分类函数编码为一个热标签。目前,我只使用一个输入图像,也使用相同的图像作为验证和测试数据 我希望U-Net执行图像分割,其中类0对应于背景。当我现在只训练我的U-Net几个时代1-10时,得到的预测掩模图像似乎只是给每个像素随机分类。当我训练网

我正在尝试用Keras和Tensorflow后端实现一个用于图像分割任务的U-Net。我有大小为128,96的图像作为网络的输入,还有大小为12288,6的掩码图像,因为它们是扁平的。我有6个不同的类0-5,它给出了遮罩图像形状的第二部分。它们已使用to_分类函数编码为一个热标签。目前,我只使用一个输入图像,也使用相同的图像作为验证和测试数据

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

我曾尝试手动给网络赋予权重,以减少对背景的强调,并尝试了不同的损失组合、不同的掩模图像塑造方法以及其他许多方法,但都没有取得好的效果

下面是我的网络代码。它基于从中获取的U形网。我设法把它训练成两个班的情况,效果很好,但现在我不知道如何把它扩展到更多的班

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

有人能指出我的模型有什么问题吗?

我看不到你的预测层,据我所知,它一定是密集层,而不是卷积层。
也许这就是你的问题。

我没有看到你的预测层,据我所知,它一定是密集层,而不是卷积层。
也许这就是你的问题。

根据我的经验,也有一个U-net用于细分。它倾向于这样做:

选择全黑或全白 在损失似乎冻结了很多时间之后,它终于找到了方向。 我还使用“只训练一幅图像”的方法来找到收敛点,然后添加其他图像就可以了

但是我试了很多次,唯一一次它运行得很快的是我使用:

最终激活='乙状结肠' 损失='二进制交叉熵' 但是我没有在任何地方使用relu…也许这会影响一点收敛速度。。。?考虑到relu,它只有0或正结果,这个函数中有一个很大的区域没有梯度。也许有很多relu激活会创建很多没有梯度的平坦区域?你必须好好想想才能确认

尝试几次,耐心等待不同权重初始化的多个时代

你的学习率也可能太高


即将分类:您是否尝试过绘制/打印您的面具?它们真的像你期望的那样吗?

以我的经验,也有一个U-net用于细分。它倾向于这样做:

选择全黑或全白 在损失似乎冻结了很多时间之后,它终于找到了方向。 我还使用“只训练一幅图像”的方法来找到收敛点,然后添加其他图像就可以了

但是我试了很多次,唯一一次它运行得很快的是我使用:

最终激活='乙状结肠' 损失='二进制交叉熵' 但是我没有在任何地方使用relu…也许这会影响一点收敛速度。。。?考虑到relu,它只有0或正结果,这个函数中有一个很大的区域没有梯度。也许有很多relu激活会创建很多没有梯度的平坦区域?你必须好好想想才能确认

尝试几次,耐心等待不同权重初始化的多个时代

你的学习率也可能太高


即将分类:您是否尝试过绘制/打印您的面具?它们真的像你期望的那样吗?

谢谢你@Daniel,你的建议最终帮助我让Unet发挥作用。当运行500多个历代时,我得到的结果不仅仅是将整个图像分类为背景。另外,与使用kernel\u initializer='he\u normal',kernel\u initializer='zeros'或kernel\u initializer=TruncatedNormalmean=0.0不同,stddev=0.07对我有效。我使用了“sigmoid”激活函数和loss='binary\u crossentropy'。我为所有隐藏的卷积层保留了“relu”激活。我注意到我的网络有时会陷入本地最小值,丢失情况不再改善,因此我需要重新启动

谢谢你@Daniel,你的建议最终帮助我让Unet开始工作。当运行500多个历代时,我得到的结果不仅仅是将整个图像分类为背景。而且相反
使用kernel_initializer='he_normal',kernel_initializer='zeros'或kernel_initializer=TruncatedNormalmean=0.0,stddev=0.07对我来说是有效的。我使用了“sigmoid”激活函数和loss='binary\u crossentropy'。我为所有隐藏的卷积层保留了“relu”激活。我注意到我的网络有时会陷入本地最小值,丢失情况不再改善,因此我需要重新启动

谢谢,弗洛。您可以指定添加预测层的含义吗?我正在遵循的架构,我不知道如何为网络添加一个密集层进行预测。谢谢,@Flo。您可以指定添加预测层的含义吗?我正在遵循的架构,我不知道如何为网络添加一个密集层进行预测。谢谢你的回答!我会试试你的建议!我使用的是带二元交叉熵损失的sigmoid激活。您对隐藏层使用了哪种激活方式?关于to_分类函数,我正在绘制遮罩,它们在转换为一个热标签并返回时看起来很好。但我不太确定这是否是Keras网络的正确标签格式。这个想法正是为了绘制一个热编码的面具,而不是原始的面具。正如它所说,一个hot通常只有一个值为1,其余的值都是0。这永远不会做一个面具你需要很多的1来做一个面具,但是我不习惯to_分类函数来知道它到底在做什么。但是你可能不应该把它和遮罩一起使用,你需要每个通道都有很多1。如果你的MAK来自于值在0到255之间的图像,你所要做的就是将图像除以255。我不认为,我理解你的意思。我有大小为128x96x6的遮罩,其中128x96是图像的像素大小,我有6个不同的类。在每个通道中,我有0和1,始终指示像素是否属于通道的相应类别。所以你说我不应该对掩模图像进行热编码?你关于如何组织掩模数据的想法是完美的。如果你描述的是y_训练,就像你把它传递给fit方法一样,那么你不必担心我说的话。我只是建议你在y_训练数据中绘制一个由一个或两个通道形成的图像。听起来像是你在绘制原始面具,然后才使用“分类”。目的只是检查to_Category是否真的给了你有效的面具。谢谢。是的,我的y_训练是我描述的面具的扁平版,所以它的尺寸不是128x96x6,而是128*96x6。但我也分别绘制了通道,它们精确地显示了应该显示的掩模部分。你能记得你的unet培训大约需要多少个阶段吗?我已经运行了5000现在,但这并没有什么不同。谢谢你的回答!我会试试你的建议!我使用的是带二元交叉熵损失的sigmoid激活。您对隐藏层使用了哪种激活方式?关于to_分类函数,我正在绘制遮罩,它们在转换为一个热标签并返回时看起来很好。但我不太确定这是否是Keras网络的正确标签格式。这个想法正是为了绘制一个热编码的面具,而不是原始的面具。正如它所说,一个hot通常只有一个值为1,其余的值都是0。这永远不会做一个面具你需要很多的1来做一个面具,但是我不习惯to_分类函数来知道它到底在做什么。但是你可能不应该把它和遮罩一起使用,你需要每个通道都有很多1。如果你的MAK来自于值在0到255之间的图像,你所要做的就是将图像除以255。我不认为,我理解你的意思。我有大小为128x96x6的遮罩,其中128x96是图像的像素大小,我有6个不同的类。在每个通道中,我有0和1,始终指示像素是否属于通道的相应类别。所以你说我不应该对掩模图像进行热编码?你关于如何组织掩模数据的想法是完美的。如果你描述的是y_训练,就像你把它传递给fit方法一样,那么你不必担心我说的话。我只是建议你在y_训练数据中绘制一个由一个或两个通道形成的图像。听起来像是你在绘制原始面具,然后才使用“分类”。目的只是检查to_Category是否真的给了你有效的面具。谢谢。是的,我的y_训练是我描述的面具的扁平版,所以它的尺寸不是128x96x6,而是128*96x6。但我也分别绘制了通道,它们精确地显示了应该显示的掩模部分。你能记得你的unet培训大约需要多少个阶段吗?我现在已经运行了5000次,但这并没有什么不同。