简体   繁体   English

用另一层替换预训练的 model 中的 Keras 层

[英]Replacing a Keras layer in a pretrained model with another layer

I am using Keras with Tensorflow version 2.7 as backend.我正在使用 Keras 和 Tensorflow 版本 2.7 作为后端。 I am referring to the stackoverflow post at Removing then Inserting a New Middle Layer in a Keras Model .我指的是 stackoverflow 上的帖子Removing then Inserting a New Middle Layer in a Keras Model I aim to instantiate an Imag.net-pretrained VGG16 model and replace every MaxPooling2D layer by the AveragePooling2D layer:我的目标是实例化一个 Imag.net 预训练 VGG16 model 并用 AveragePooling2D 层替换每个 MaxPooling2D 层:

import os
from tensorflow import keras
from tensorflow.keras import backend as K
from tensorflow.keras.layers import *
from tensorflow.keras import applications
from tensorflow.keras.models import Model
model_input = (224,224,3)
model = applications.VGG16(include_top=False,
                           weights='imagenet',
                           input_shape=model_input)
model.summary()

for layer in tuple(model.layers):
    layer_type = type(layer).__name__
    if layer.__name__ == 'MaxPooling2D':
        pool_name = layer.name + "_averagepooling2d"
        pool = AveragePooling2D() if layer_type == "MaxPooling2D" else pool(name=pool_name)
        model.add(pool)        

model.summary()

I get the following error:我收到以下错误:

  File "C:\Users\AppData\Local\Temp\2/ipykernel_26864/1200445239.py", line 15, in <module>
    if layer.__name__ == 'MaxPooling2D':

AttributeError: 'InputLayer' object has no attribute '__name__'

Also, I am not sure if this is the right way to replace the MaxPooling layers with the AveragePooling layer in all types of pretrained models including those with skip connections and dense blocks.此外,我不确定这是否是在所有类型的预训练模型(包括具有跳过连接和密集块的模型)中将 MaxPooling 层替换为 AveragePooling 层的正确方法。 Requesting code correction in this regard.请求在这方面进行代码更正。

Here is one of the approaches to accomplish that:这是实现该目标的方法之一:

Original vgg16原始vgg16

import tensorflow as tf
vgg=tf.keras.applications.vgg16.VGG16(
    include_top=True,
    weights='imagenet',
    input_tensor=None,
    input_shape=None,
    pooling=None,
    classes=1000,
    classifier_activation='softmax'
)
vgg.summary()

Output: Output:

在此处输入图像描述

Modified vgg16修改后的 vgg16

#One of the following two:
#model_input=vgg.input
model_input=tf.keras.Input(shape=(224, 224, 3,))

x=model_input
for layer in vgg.layers[1:]:
    if isinstance(layer, tf.keras.layers.MaxPooling2D):
        kwargs=layer.get_config()
        x=tf.keras.layers.AveragePooling2D(**kwargs)(x)
    else:
        x=layer(x)
model=tf.keras.Model(inputs=model_input, outputs=x, name="vgg_avg")
model.summary()

Output: Output:

在此处输入图像描述


Notes笔记

By replacing the MaxPooling2D layers with their AveragePooling2D counterparts, the originally-optimized weights may not be optimal anymore.通过将MaxPooling2D层替换为对应的AveragePooling2D层,最初优化的权重可能不再是最优的。 So, some level of tuning (with small learning rate) might be needed.因此,可能需要某种程度的调优(学习率较小)。

Keras offers to define a customized model, in which it allows one to customize the layers according to their requirements Keras Custom Model . Keras 提供定义一个自定义的 model,其中它允许根据他们的要求自定义层Keras Custom Model Though vgg16 model has an argument to define the pooling type, it is only global (ie, applicable only to the output of last convolution block of the model) Keras Vgg16 .虽然 vgg16 model 有定义池化类型的参数,但它只是全局的(即仅适用于模型最后一个卷积块的 output) Keras Vgg16

One can define the custom model as shown below,可以定义自定义 model,如下所示,

import os
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import backend as K
from keras.engine import training
from keras import layers
from keras.layers import Dense
from keras.applications import imagenet_utils
from keras.utils import layer_utils

def MyVgg16Model(
                 include_top=True,
                 weights="imagenet",
                 input_tensor=None,
                 input_shape=None,
                 pooling=None,
                 classes=1000,
                 classifier_activation="softmax"):

                
    input_shape = imagenet_utils.obtain_input_shape(
                                            input_shape,
                                            default_size=224,
                                            min_size=32,
                                            data_format=K.image_data_format(),
                                            require_flatten=include_top,
                                            weights=weights,
                                        )
                                        
    if input_tensor is None:
        img_input = layers.Input(shape=input_shape)
    else:
        if not K.is_keras_tensor(input_tensor):
            img_input = layers.Input(tensor=input_tensor, shape=input_shape)
        else:
            img_input = input_tensor

    # Block 1
    x = layers.Conv2D(
        64, (3, 3), activation="relu", padding="same", name="block1_conv1"
    )(img_input)
    x = layers.Conv2D(
        64, (3, 3), activation="relu", padding="same", name="block1_conv2"
    )(x)
    x = layers.AveragePooling2D((2, 2), strides=(2, 2), name="block1_pool")(x)

    # Block 2
    x = layers.Conv2D(
        128, (3, 3), activation="relu", padding="same", name="block2_conv1"
    )(x)
    x = layers.Conv2D(
        128, (3, 3), activation="relu", padding="same", name="block2_conv2"
    )(x)
    x = layers.AveragePooling2D((2, 2), strides=(2, 2), name="block2_pool")(x)

    # Block 3
    x = layers.Conv2D(
        256, (3, 3), activation="relu", padding="same", name="block3_conv1"
    )(x)
    x = layers.Conv2D(
        256, (3, 3), activation="relu", padding="same", name="block3_conv2"
    )(x)
    x = layers.Conv2D(
        256, (3, 3), activation="relu", padding="same", name="block3_conv3"
    )(x)
    x = layers.AveragePooling2D((2, 2), strides=(2, 2), name="block3_pool")(x)

    # Block 4
    x = layers.Conv2D(
        512, (3, 3), activation="relu", padding="same", name="block4_conv1"
    )(x)
    x = layers.Conv2D(
        512, (3, 3), activation="relu", padding="same", name="block4_conv2"
    )(x)
    x = layers.Conv2D(
        512, (3, 3), activation="relu", padding="same", name="block4_conv3"
    )(x)
    x = layers.AveragePooling2D((2, 2), strides=(2, 2), name="block4_pool")(x)

    # Block 5
    x = layers.Conv2D(
        512, (3, 3), activation="relu", padding="same", name="block5_conv1"
    )(x)
    x = layers.Conv2D(
        512, (3, 3), activation="relu", padding="same", name="block5_conv2"
    )(x)
    x = layers.Conv2D(
        512, (3, 3), activation="relu", padding="same", name="block5_conv3"
    )(x)
    x = layers.AveragePooling2D((2, 2), strides=(2, 2), name="block5_pool")(x)

    if include_top:
        # Classification block
        x = layers.Flatten(name="flatten")(x)
        x = Dense(4096, activation="relu", name="fc1")(x)
        x = Dense(4096, activation="relu", name="fc2")(x)

        imagenet_utils.validate_activation(classifier_activation, weights)
        x = layers.Dense(
            classes, activation=classifier_activation, name="predictions"
        )(x)
    else:
        if pooling == "avg":
            x = layers.GlobalAveragePooling2D()(x)
        elif pooling == "max":
            x = layers.GlobalMaxPooling2D()(x)

    if input_tensor is not None:
        inputs = layer_utils.get_source_inputs(input_tensor)
    else:
        inputs = img_input
    # Create model.
    model = training.Model(inputs, x, name="vgg16")

    return model

model_input = (224,224,3)
model = MyVgg16Model(include_top=False,
                           weights='imagenet',
                           input_shape=model_input)
model.summary()

In the model summary, you can see the changes as shown below,在model总结中,可以看到如下图的变化,

Model: "vgg16"
_________________________________________________________________
 Layer (type)                Output Shape              Param #
=================================================================
 input_1 (InputLayer)        [(None, 224, 224, 3)]     0

 block1_conv1 (Conv2D)       (None, 224, 224, 64)      1792

 block1_conv2 (Conv2D)       (None, 224, 224, 64)      36928

 block1_pool (AveragePooling  (None, 112, 112, 64)     0
 2D)

 block2_conv1 (Conv2D)       (None, 112, 112, 128)     73856

 block2_conv2 (Conv2D)       (None, 112, 112, 128)     147584

 block2_pool (AveragePooling  (None, 56, 56, 128)      0
 2D)

 block3_conv1 (Conv2D)       (None, 56, 56, 256)       295168

 block3_conv2 (Conv2D)       (None, 56, 56, 256)       590080

 block3_conv3 (Conv2D)       (None, 56, 56, 256)       590080

 block3_pool (AveragePooling  (None, 28, 28, 256)      0
 2D)

 block4_conv1 (Conv2D)       (None, 28, 28, 512)       1180160

 block4_conv2 (Conv2D)       (None, 28, 28, 512)       2359808

 block4_conv3 (Conv2D)       (None, 28, 28, 512)       2359808

 block4_pool (AveragePooling  (None, 14, 14, 512)      0
 2D)

 block5_conv1 (Conv2D)       (None, 14, 14, 512)       2359808   

 block5_conv2 (Conv2D)       (None, 14, 14, 512)       2359808

 block5_conv3 (Conv2D)       (None, 14, 14, 512)       2359808

 block5_pool (AveragePooling  (None, 7, 7, 512)        0
 2D)

=================================================================
Total params: 14,714,688
Trainable params: 14,714,688
Non-trainable params: 0
_________________________________________________________________

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM