代码之家  ›  专栏  ›  技术社区  ›  Jasmin

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

  •  1
  • Jasmin  · 技术社区  · 8 年前

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

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

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

    this repository . 我设法把它训练成两个班的情况,取得了很好的效果,但我现在不知道如何将它扩展到更多的班。

    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 回复  |  直到 8 年前
        1
  •  2
  •   Jasmin    8 年前

    谢谢你@Daniel,你的建议最终帮助我让Unet工作。当运行500多个时代时,我得到的结果不仅仅是将整个图像分类为背景。此外,不要使用 kernel_initializer='he_normal' , kernel_initializer='zeros' kernel_initializer=TruncatedNormal(mean=0.0, stddev=0.07) 为我工作。我使用了“sigmoid”激活函数和 loss='binary_crossentropy'

        2
  •  1
  •   Daniel Möller    8 年前

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

    • 在损失似乎冻结了很多时间之后,它发现了它的方式。

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

    • 最终激活='乙状结肠'

    但我没有在任何地方使用“relu”。。。也许这会影响收敛速度。。。?考虑到“relu”(只有0或正结果),该函数中有一个很大的区域没有梯度。也许有很多“relu”激活会创建很多没有梯度的“平坦”区域?(必须仔细考虑才能确认)

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

    你的学习率也有可能太高。


    to_categorical() :您是否尝试绘制/打印口罩?他们真的像你期望的那样吗?

        3
  •  1
  •   Florian    8 年前