首页 文章

如何在Keras中下载和跳过与CNN无对应的VGG权重?

提问于
浏览
5

我想遵循卷积神经网络(CNN)方法here . 但是,github中的这段代码使用了Pytorch,而我使用的是Keras .

我想重现方框6,7和8,其中下载了来自ImageNet上的VGG-16的预训练权重,并用于使CNN更快地收敛 .

特别地,存在一个部分(框8),其中从VGG-16下载并跳过权重,其在 SegNet (CNN模型)中没有对应物 . 在我的工作中,我使用名为 U-Net 的CNN模型而不是 Segnet . 我正在使用的 U-Net Keras代码可以找到here .

我是Keras的新手,非常感谢Keras代码中有关如何下载和跳过与我的 U-Net 型号无对应的VGG权重的任何见解 .

2 回答

  • 6

    此解决方案草图如下所示:

    • 初始化VGG-16并使用适当的 weights='imagenet' 标志加载ImageNet权重:
    vgg_16 = keras.applications.vgg16.VGG16(weights='imagenet')
    
    • 初始化您的模型:
    model = Model()  # or Sequential()
    ...  # Define and compile your model
    
    • 对于要复制的每个图层:
    i_vgg = ...  # Index of the layer you want to copy
    i_mod = ...  # Index of the corresponding layer in your model
    weights = vgg_16.layers[i_vgg].get_weights()
    model.layers[i_mod].set_weights(weights)
    

    如果您不想花时间查找每个图层的索引,可以使用图层构造函数中的 name='some_name' 参数为相关图层指定名称,然后按如下方式访问权重:

    layer_dict = dict([(layer.name, layer) for layer in model.layers])
    weights = layer_dict['some_name'].get_weights()
    layer_dict['some_name'].set_weights(weights)
    

    资料来源:

    干杯

  • 1

    您正在寻址的技术称为 "Transfer Learning" - 当使用不同数据集上的预训练模型作为模型的一部分作为更好收敛的起点时 . 它背后的直觉很简单:我们假设在对如此庞大且丰富的数据集进行训练之后,该模型的卷积核将学习有用的表示 .

    在您的特定情况下,您希望在底部堆叠 VGG16 权重,在顶部堆叠解卷积块 . 我会一步一步地指出你是 Keras 的新手 . 此答案按照分步教程进行组织,并将为您提供在您自己的代码中使用的小片段 .


    装载重量

    在您链接到上面的 PyTorch 代码中,首先定义模型,然后才复制权重 . 我发现这种方法很丰富,因为它包含许多不必要的代码 . 在这里,我们首先加载 VGG16 ,然后将其他图层堆叠在顶部 .

    from keras import applications
    from keras.layers import Input
    
    # Loading without top layers, since you only need convolution. Note that by not
    # specifying the shape of top layers, the input tensor shape is (None, None, 3),
    # so you can use them for any size of images.
    vgg_model = applications.VGG16(weights='imagenet', include_top=False)
    
    # If you want to specify input tensor shape, e.g. 256x256 with 3 channels:
    input_tensor = Input(shape=(256, 256, 3))
    vgg_model = applications.VGG16(weights='imagenet',
                                   include_top=False,
                                   input_tensor=input_tensor)
    
    # To see the models' architecture and layer names, run the following
    vgg_model.summary()
    

    使用底部的VGG16定义U-Net计算图

    如前一段所述,您无需定义模型并复制权重 . 只需在 vgg_model 上堆叠其他图层:

    # Import the layers to be used in U-Net
    from keras.layers import ...
    
    # From the U-Net code you provided
    def make_conv_block(nb_filters, input_tensor, block):
        ...
    
    # Creating dictionary that maps layer names to the layers
    layers = dict([(layer.name, layer) for layer in vgg_model.layers])
    
    # Getting output tensor of the last VGG layer that we want to include.
    # I don't know much about U-Net, but according to the code you provided,
    # you don't need the last pooling layer, right?
    vgg_top = layers['block5_conv3'].output
    
    # Now getting bottom layers for multi-scale skip-layers
    block1_conv2 = layers['block1_conv2'].output
    block2_conv2 = layers['block2_conv2'].output
    block3_conv3 = layers['block3_conv3'].output
    block4_conv3 = layers['block4_conv3'].output
    
    # Stacking the remaining layers of U-Net on top of it (modified from
    # the U-Net code you provided)
    up6 = Concatenate()([UpSampling2D(size=(2, 2))(vgg_top), block4_conv3])
    conv6 = make_conv_block(256, up6, 6)
    up7 = Concatenate()([UpSampling2D(size=(2, 2))(conv6), block3_conv3])
    conv7 = make_conv_block(128, up7, 7)
    up8 = Concatenate()([UpSampling2D(size=(2, 2))(conv7), block2_conv2])
    conv8 = make_conv_block(64, up8, 8)
    up9 = Concatenate()([UpSampling2D(size=(2, 2))(conv8), block1_conv2])
    conv9 = make_conv_block(32, up9, 9)
    conv10 = Conv2D(nb_labels, (1, 1), name='conv_10_1')(conv9)
    x = Reshape((nb_rows * nb_cols, nb_labels))(conv10)
    x = Activation('softmax')(x)
    outputs = Reshape((nb_rows, nb_cols, nb_labels))(x)
    

    我想强调一下,我们在本段中所做的只是定义 U-Net 的计算图 . 此代码专门为 VGG16 编写,但您可以根据需要对其他体系结构进行修改 .


    创建模型

    在上一步之后,我们得到了一个计算图(我假设您使用 Tensorflow 后端为 Keras . 如果您使用 Theano ,我建议您切换到 Tensorflow ,因为此框架现已达到成熟状态) . 现在,我们需要做以下事情:

    • 在此计算图之上创建模型

    • 冻结底层,因为您不想破坏预先训练过的砝码

    # Creating new model. Please note that this is NOT a Sequential() model
    # as in commonly found tutorials on the internet.
    from keras.models import Model
    custom_model = Model(inputs=vgg_model.input, outputs=outputs)
    
    # Make sure that the pre-trained bottom layers are not trainable.
    # Here, I freeze all the layers of VGG16 (layers 0-18, including the
    # pooling ones.
    for layer in custom_model.layers[:19]:
        layer.trainable = False
    
    # Do not forget to compile it before training
    custom_model.compile(loss='your_loss',
                         optimizer='your_optimizer',
                         metrics=['your_metrics'])
    

    “我感到困惑”

    假设你是 Keras 的新手和一般的深度学习(正如你在问题中所承认的那样),我建议阅读以下文章以进一步了解Keras上的 Fine TuningTransfer Learning 的过程:

    当您学习框架时,文档是您最好的朋友 . 幸运的是, Keras 有一个令人难以置信的documentation .


    问答

    我们放在VGG之上的去卷积块来自UNET架构(即从6到9)?请确认 .

    是的,它与here相同,只是跳过连接层的名称不同(例如 block1_conv2 而不是 conv1

    我们省略了转换层(即conv1到conv5) . 你能和我分享为什么会这样吗?

    我们不会离开或抛出VGG网络中的任何图层 . VGG16 网络架构和 U-Net (最高 conv5 )的底层架构非常相似 . 实际上,它们由以下格式的 5 块组成:

    +-----------------+-------------------+
    | VGG conv blocks | U-Net conv blocks |
    +-----------------+-------------------+
    | blockX_conv1    | convN             |
    | ...             | poolN             |
    | blockX_convN    |                   |
    | blockX_pool     |                   |
    +-----------------+-------------------+
    

    Here是一个更好的可视化 . 因此, VGG16U-Net 的底部之间的唯一区别是 VGG16 的每个块包含多个卷积层而不是一个 . 这就是为什么,将 conv3 连接到 conv6 的替代方法是将 block3_conv3 连接到 conv6 . U-Net 架构保持不变,只是在底部有更多的卷积层 .

    无论如何要将最大池合并到转换层中(在您看来,我们在这里做了什么,将它们排除在外,你会说这是无关紧要的吗?)

    我们不会把它们排除在外 . 我丢弃的唯一汇集层是 block5_pool (这是 VGG16 底部的最后一层) - 因为在原始 U-Net (参见code)中,似乎底部的最后一个卷积块后面没有池化层(我们有 conv5 但没有 pool5 ) . 我保留了 VGG16 的所有层次 .

    我们看到在卷积块上使用了Maxpooling . 如果我们想将Segnet与VGG结合起来,我们是否也只是简单地删除这些池层(就像我们在这里用Unet做的那样)?

    正如我在上面的问题中所解释的那样,我们不会丢弃任何池化层 . 但是,您需要堆叠不同类型的池化层而不是默认 VGG16 中使用的简单 MaxPooling2D ,因为 SegNet 保留了最大索引 . 这可以通过tf.nn.max_pool_with_argmax并使用replacing middle layers of Keras model的技巧来实现(我赢得了't cover the detailed information in this answer to keep it clean). The replacement is harmless and doesn' t需要重新训练,因为合并图层不包含任何训练过的权重 .

    这里的U-NET与我使用的不同,你能说出两者之间的这种差异有什么影响?

    这是一个更浅的U-Net . 原始问题中的一个在底部有5个卷积块( conv1 - conv5 ),而后者只有3个 . 根据数据选择需要多少个块(例如,对于简单数据,您可能只想使用2个单元格) -3块,而灰质或组织分割可能需要5个块以获得更好的质量 . 请参阅this链接以了解卷积内核"see" .

    另外,您如何看待VGGSegnet . 它是否使用了您在问答中提到的中间层的技巧?它是否相当于我最初发布的Pytorch代码?

    有趣 . 这是一个不正确的实现,并不等同于您发布的Pytorch代码 . 我在该存储库中打开了issue .

    最后一个问题....在转移学习中,将训练前的模型(即带有预训练重量的模型)置于底部始终是一条规则吗?

    一般来说是 . 将卷积内核视为“特征”:第一层检测小边缘和颜色 . 以下图层将这些边和颜色组合成更复杂的检测,如“黄线”或“蓝色圆圈” . 然后,上卷积层基于下层的检测将更抽象的形状检测为“眼睛”,“鼻子”等 . 因此,替换底层(而上层取决于底部表示)是不合逻辑的 .

相关问题