用不同的通道替换预训练模型的输入层?

Posted

技术标签:

【中文标题】用不同的通道替换预训练模型的输入层?【英文标题】:Replacing the input layer of a pre-trained model with different channels? 【发布时间】:2021-08-04 22:48:47 【问题描述】:

我想重新使用 MobiletNetv2 的预训练权重,但图像具有 12 个通道。我知道这需要增加重量,但这没关系,因为无论如何我都想重新训练。我找不到让它工作的方法。

import tensorflow as tf

class CNN(tf.keras.Model):
    def __init__(self):
        super(CNN, self).__init__()
        self.input_layer = tf.keras.layers.InputLayer(input_shape=(None, 224, 224, 12))
        self.base = tf.keras.applications.MobileNetV2(input_shape=(224, 224, 3),
                                                      include_top=False,
                                                      weights='imagenet')
        _ = self.base._layers.pop(0)
        self.flat1 = tf.keras.layers.Flatten()
        self.dens3 = tf.keras.layers.Dense(10)

    def call(self, x, **kwargs):
        x = self.input_layer(x)
        x = self.base(x)
        x = self.flat1(x)
        x = self.dens3(x)
        return x

model = CNN()
model.build(input_shape=(None, 224, 224, 12))

ValueError: Input 0 is in compatible with layer mobilenetv2_1.00_224: expected shape=(None, 224, 224, 3), found shape=(None, 224, 224, 12)

我尝试像其他答案一样弹出第一层。

【问题讨论】:

【参考方案1】:

您可以将每个通道用作独立图像...您将每个通道作为形状 (224,224,1) 的图像,重复每个通道以获得图像 (224,224,3)。最后,您将所有 12 张图片输入到同一个移动网络。

这会生成 12 个输出,您可以对它们进行平均或以不同方式组合。

这只是我没有在实践中测试过的一种可能性,但也可以作为一个很好的起点。

Pythonic 这就是我的意思的代码:

def split_channels(image):
    channels = tf.split(image, 12, axis=-1)
    return [tf.repeat(c, [3], axis=-1) for c in channels]
    
inp = Input((224,224,12))
channels = Lambda(split_channels)(inp)
base_model = tf.keras.applications.MobileNetV2(
    input_shape=(224, 224, 3), include_top=False, weights='imagenet')
avg = Average()([base_model(c) for c in channels])
x = Flatten()(avg)
out = Dense(10)(x)

model = Model(inp, out)

【讨论】:

【参考方案2】:

最简单的方法之一(在这种情况下)是将多通道输入 (H, W, C > 3) 传递给 Conv2D(3, 3, padding='same') 层,然后是 预训练模型强>。

class CNN(tf.keras.Model):
    def __init__(self):
        super(CNN, self).__init__()
        self.base = tf.keras.applications.MobileNetV2(input_shape=(224, 224, 3),
                                                      include_top=False,
                                                      weights='imagenet')
        self.conv = tf.keras.layers.Conv2D(3, 3, padding='same')
        self.flat1 = tf.keras.layers.Flatten()
        self.dens3 = tf.keras.layers.Dense(10)

    def call(self, x, **kwargs):
        x = self.conv(x)
        x = self.base(x)
        x = self.flat1(x)
        x = self.dens3(x)
        return x
    
    def build_graph(self):
        x = tf.keras.Input(shape=(224, 224, 12))
        return tf.keras.Model(inputs=[x], outputs=self.call(x))

model = CNN()
model.build(input_shape=(None, 224, 224, 12))

它只是完成这项工作。

model(tf.ones((1, 224, 224, 12))).shape # TensorShape([1, 10])
model.build_graph().summary()

Model: "model_3"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_13 (InputLayer)        [(None, 224, 224, 12)]    0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 224, 224, 3)       327       
_________________________________________________________________
mobilenetv2_1.00_224 (Functi (None, 7, 7, 1280)        2257984   
_________________________________________________________________
flatten_4 (Flatten)          (None, 62720)             0         
_________________________________________________________________
dense_4 (Dense)              (None, 10)                627210    
=================================================================
Total params: 2,885,521
Trainable params: 2,851,409
Non-trainable params: 34,112
_____________________________________________

另外,请参阅this 答案,它可能会有所帮助。

【讨论】:

【参考方案3】:

可以加载两个模型,一个具有 12 个通道的输入形状,另一个具有正常的 12 个通道。然后,只需将 3 通道模型的权重加载到 12 通道模型中,从第 2 层或第 3 层开始。

这里是进行重量转移的地方:

for i in range(3, len(self.base.layers)):
            self.base.layers[i].set_weights(base_weights.layers[i].get_weights())

这就是全部内容:

import tensorflow as tf

h, w, c = 224, 224, 3


class CNNModel(tf.keras.Model):
    def __init__(self):
        super(CNNModel, self).__init__()
        self.base = tf.keras.applications.MobileNetV2(input_shape=(h, w, 12),
                                                      include_top=False,
                                                      weights=None)
        base_weights = tf.keras.applications.MobileNetV2(input_shape=(h, w, c),
                                                         include_top=False,
                                                         weights='imagenet')

        for i in range(3, len(self.base.layers)):
            self.base.layers[i].set_weights(base_weights.layers[i].get_weights())

        del base_weights
        self.pool = tf.keras.layers.GlobalAveragePooling2D()
        self.drop1 = tf.keras.layers.Dropout(0.25)
        self.out = tf.keras.layers.Dense(1, activation='sigmoid')

    def call(self, x, training=None, **kwargs):
        x = self.base(x)
        x = self.pool(x)
        x = self.drop1(x)
        x = self.out(x)
        return x


model = CNNModel()

model.build(input_shape=(None, h, w, 12))

【讨论】:

这样就没有数据丢失的可能。但是,我认为范围应该从 2 而不是 3 开始,你错过了第一个 conv2d。是故意的吗? @M.Innat 那是因为我在现实中使用的是 ResNet50,它不适用于 2。我相信 layer1 = InputLayer, layer2 = Conv2d 和 layer3 有不同的输入形状? ValueError: Layer weight shape (7, 7, 12, 64) not compatible with provided weight shape (7, 7, 3, 64) 那么,这样,我们也失去了一些体重信息,对吧? 是的,第一层。但至少在进一步的训练中,它将具有相同的潜力。而在前面放置一个 3 通道层将第一层可以学习的信息减少 4

以上是关于用不同的通道替换预训练模型的输入层?的主要内容,如果未能解决你的问题,请参考以下文章

PyTorch:将预训练模型从 3 个 RGB 通道更改为 4 个通道后,出现“ValueError:无法优化非叶张量”

在预训练模型前添加 Conv 层会产生 ValueError

如何在 Keras 中的预训练 InceptionResNetV2 模型的不同层中找到激活的形状 - Tensorflow 2.0

pytorch如何给预训练模型添加新的层

pytorch中修改后的模型如何加载预训练模型

如果我们扩展或减少同一模型的层,我们仍然可以从 Pytorch 中的预训练模型进行训练吗?