用不同的通道替换预训练模型的输入层?
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