如何下载并跳过与 Keras 中的 CNN 没有对应项的 VGG 权重?
How can I download and skip VGG weights that have no counterpart with my CNN in Keras?
我想遵循卷积神经网络 (CNN) 方法 here。但是,github 中的这段代码使用的是 Pytorch,而我使用的是 Keras。
我想重现方框 6、7 和 8,其中从 ImageNet 上的 VGG-16 下载了预训练的权重,用于使 CNN 收敛得更快。
特别是,有一部分(方框 8)从 VGG-16 下载并跳过了在 SegNet
(CNN 模型)中没有对应的权重。在我的工作中,我使用的是名为 U-Net
而不是 Segnet
的 CNN 模型。 U-Net
我正在使用的 Keras 代码可以在 here.
中找到
我是 Keras 的新手,非常感谢 Keras 代码中关于如何下载和跳过与我的 U-Net
模型没有对应的 VGG 权重的任何见解。
解决方案草图如下所示:
初始化 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)
来源:
- Loading VGG
- Getting weights from layer(FChollet 是 Keras 的创造者)
- Getting and setting weights
干杯
您要解决的技术称为 "Transfer Learning" - 当将不同数据集上的预训练模型用作模型的一部分作为更好的收敛性。其背后的直觉很简单:我们假设在 ImageNet
这样庞大而丰富的数据集上进行训练后,模型的卷积核将学习到有用的表征。
在您的特定情况下,您希望在底部堆叠 VGG16
权重,在顶部堆叠反卷积块。我会一步一步来,因为你指出你是 Keras
的新手。这个答案被组织成一个循序渐进的教程,并将提供小片段供您在自己的代码中使用。
加载权重
在您 link 上面的 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()
定义U-Net计算图,底部为VGG16
如前一段所述,您无需定义模型并复制权重。只需将其他图层堆叠在 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'])
"I got confused"
假设您是 Keras
和一般深度学习的新手(正如您在问题中承认的那样),我建议您阅读以下文章以进一步了解 的过程Keras 上的微调 和迁移学习:
- How CNNs see the world - 一篇很棒的短文,可以让您直观地了解迁移学习背后的肮脏魔法。
- Building powerful image classification models using very little data - 这个会让你更深入地了解如何调整学习率和"release"冻结层。
当您学习框架时,文档是您最好的朋友。幸运的是,Keras
有一个不可思议的documentation。
问答
The deconvolution blocks we put on top of VGG are from the UNET achitecture (i.e. up6 to conv10)? Please confirm.
是的,它与here相同,只是跳过连接层的名称不同(例如block1_conv2
而不是conv1
)
We leave out the conv layers (i.e., conv1 to conv5). Can you please share with me as to why this is so?
我们不会从 VGG 网络中留下或丢弃任何层。 VGG16
的网络架构和U-Net
(一直到conv5
)的底层架构非常相似。事实上,它们由以下格式的 5
个块组成:
+-----------------+-------------------+
| VGG conv blocks | U-Net conv blocks |
+-----------------+-------------------+
| blockX_conv1 | convN |
| ... | poolN |
| blockX_convN | |
| blockX_pool | |
+-----------------+-------------------+
Here 是更好的可视化。因此,VGG16
和 U-Net
的底部之间的唯一区别是 VGG16
的每个块包含多个卷积层而不是一个。这就是为什么将 conv3
连接到 conv6
的替代方法是将 block3_conv3
连接到 conv6
。 U-Net
架构保持不变,只是在底部增加了更多的卷积层。
Is there anyway to incorporate the Max pooling in the conv layers (in your opinion what are we doing here by leaving them out, and would you say it is insignificant?)
我们不会遗漏它们。我扔掉的唯一池化层是 block5_pool
(这是 VGG16
底部的最后一层)——因为在原来的 U-Net
中(参考 code)似乎底部的最后一个卷积块后面没有池化层(我们有 conv5
但没有 pool5
)。我保留了 VGG16
.
的所有图层
We see Maxpooling being used on the convolution blocks. Would we also just simply drop these pooling layers (as we are doing here with Unet) if we wanted to combine Segnet with VGG?
正如我在上面的问题中所解释的,我们不会删除任何池化层。
但是,您需要堆叠不同类型的池化层而不是默认 VGG16
中使用的简单 MaxPooling2D
,因为 SegNet
保留最大索引。这可以通过 tf.nn.max_pool_with_argmax
and using the trick of 实现(为了保持清晰,我不会在此答案中涵盖详细信息)。替换是无害的,不需要重新训练,因为池化层不包含任何训练过的权重。
The U-NET from here is different from what I am using, can you tell what is the impact of such a difference between the two?
这是一个更浅的U-Net。原始问题中的底部有 5 个卷积块(conv1
- conv5
),而后者只有 3 个。根据数据选择所需的块数(例如,对于简单数据,您可能只想使用 2-3 个块的细胞,而灰质或组织分割可能需要 5 个块以获得更好的质量。请参阅 this link 以了解什么是卷积核 "see" .
Also, what do you think about the VGGSegnet from here. Does it use the trick of the middle layers you mentioned in Q&A? And is it the equivalent of the Pytorch code I initially posted?
有意思。这是一个不正确的实现,并不等同于您发布的 Pytorch 代码。我在该存储库中打开了一个 issue。
Final question....is it always a rule in Transfer Learning to put the pretrained model (i.e., the model w/ pretrained weights) at the bottom?
一般是这样。将卷积核想象成 "features":第一层检测小边缘、颜色。以下层将这些边缘和颜色组合成更复杂的检测,如 "yellow lines" 或 "blue circle"。然后上层卷积层在下层检测的基础上检测更抽象的形状,如"eyes"、"nose"等。所以替换底层(而上层取决于底层表示)是不合逻辑的。
我想遵循卷积神经网络 (CNN) 方法 here。但是,github 中的这段代码使用的是 Pytorch,而我使用的是 Keras。
我想重现方框 6、7 和 8,其中从 ImageNet 上的 VGG-16 下载了预训练的权重,用于使 CNN 收敛得更快。
特别是,有一部分(方框 8)从 VGG-16 下载并跳过了在 SegNet
(CNN 模型)中没有对应的权重。在我的工作中,我使用的是名为 U-Net
而不是 Segnet
的 CNN 模型。 U-Net
我正在使用的 Keras 代码可以在 here.
我是 Keras 的新手,非常感谢 Keras 代码中关于如何下载和跳过与我的 U-Net
模型没有对应的 VGG 权重的任何见解。
解决方案草图如下所示:
初始化 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)
来源:
- Loading VGG
- Getting weights from layer(FChollet 是 Keras 的创造者)
- Getting and setting weights
干杯
您要解决的技术称为 "Transfer Learning" - 当将不同数据集上的预训练模型用作模型的一部分作为更好的收敛性。其背后的直觉很简单:我们假设在 ImageNet
这样庞大而丰富的数据集上进行训练后,模型的卷积核将学习到有用的表征。
在您的特定情况下,您希望在底部堆叠 VGG16
权重,在顶部堆叠反卷积块。我会一步一步来,因为你指出你是 Keras
的新手。这个答案被组织成一个循序渐进的教程,并将提供小片段供您在自己的代码中使用。
加载权重
在您 link 上面的 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()
定义U-Net计算图,底部为VGG16
如前一段所述,您无需定义模型并复制权重。只需将其他图层堆叠在 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'])
"I got confused"
假设您是 Keras
和一般深度学习的新手(正如您在问题中承认的那样),我建议您阅读以下文章以进一步了解 的过程Keras 上的微调 和迁移学习:
- How CNNs see the world - 一篇很棒的短文,可以让您直观地了解迁移学习背后的肮脏魔法。
- Building powerful image classification models using very little data - 这个会让你更深入地了解如何调整学习率和"release"冻结层。
当您学习框架时,文档是您最好的朋友。幸运的是,Keras
有一个不可思议的documentation。
问答
The deconvolution blocks we put on top of VGG are from the UNET achitecture (i.e. up6 to conv10)? Please confirm.
是的,它与here相同,只是跳过连接层的名称不同(例如block1_conv2
而不是conv1
)
We leave out the conv layers (i.e., conv1 to conv5). Can you please share with me as to why this is so?
我们不会从 VGG 网络中留下或丢弃任何层。 VGG16
的网络架构和U-Net
(一直到conv5
)的底层架构非常相似。事实上,它们由以下格式的 5
个块组成:
+-----------------+-------------------+
| VGG conv blocks | U-Net conv blocks |
+-----------------+-------------------+
| blockX_conv1 | convN |
| ... | poolN |
| blockX_convN | |
| blockX_pool | |
+-----------------+-------------------+
Here 是更好的可视化。因此,VGG16
和 U-Net
的底部之间的唯一区别是 VGG16
的每个块包含多个卷积层而不是一个。这就是为什么将 conv3
连接到 conv6
的替代方法是将 block3_conv3
连接到 conv6
。 U-Net
架构保持不变,只是在底部增加了更多的卷积层。
Is there anyway to incorporate the Max pooling in the conv layers (in your opinion what are we doing here by leaving them out, and would you say it is insignificant?)
我们不会遗漏它们。我扔掉的唯一池化层是 block5_pool
(这是 VGG16
底部的最后一层)——因为在原来的 U-Net
中(参考 code)似乎底部的最后一个卷积块后面没有池化层(我们有 conv5
但没有 pool5
)。我保留了 VGG16
.
We see Maxpooling being used on the convolution blocks. Would we also just simply drop these pooling layers (as we are doing here with Unet) if we wanted to combine Segnet with VGG?
正如我在上面的问题中所解释的,我们不会删除任何池化层。
但是,您需要堆叠不同类型的池化层而不是默认 VGG16
中使用的简单 MaxPooling2D
,因为 SegNet
保留最大索引。这可以通过 tf.nn.max_pool_with_argmax
and using the trick of
The U-NET from here is different from what I am using, can you tell what is the impact of such a difference between the two?
这是一个更浅的U-Net。原始问题中的底部有 5 个卷积块(conv1
- conv5
),而后者只有 3 个。根据数据选择所需的块数(例如,对于简单数据,您可能只想使用 2-3 个块的细胞,而灰质或组织分割可能需要 5 个块以获得更好的质量。请参阅 this link 以了解什么是卷积核 "see" .
Also, what do you think about the VGGSegnet from here. Does it use the trick of the middle layers you mentioned in Q&A? And is it the equivalent of the Pytorch code I initially posted?
有意思。这是一个不正确的实现,并不等同于您发布的 Pytorch 代码。我在该存储库中打开了一个 issue。
Final question....is it always a rule in Transfer Learning to put the pretrained model (i.e., the model w/ pretrained weights) at the bottom?
一般是这样。将卷积核想象成 "features":第一层检测小边缘、颜色。以下层将这些边缘和颜色组合成更复杂的检测,如 "yellow lines" 或 "blue circle"。然后上层卷积层在下层检测的基础上检测更抽象的形状,如"eyes"、"nose"等。所以替换底层(而上层取决于底层表示)是不合逻辑的。