Keras——迁移学习——改变输入张量形状

数据挖掘 喀拉斯
2021-09-13 23:23:59

这篇文章似乎表明我想要完成的事情是不可能的。但是,我不相信这一点-鉴于我已经完成的工作,我不明白为什么我想做的事情无法实现...

我有两个图像数据集,其中一个具有形状图像(480、720、3),而另一个具有形状图像(540、960、3)。

我使用以下代码初始化了一个模型:

input = Input(shape=(480, 720, 3), name='image_input')

initial_model = VGG16(weights='imagenet', include_top=False)

for layer in initial_model.layers:
    layer.trainable = False

x = Flatten()(initial_model(input))
x = Dense(1000, activation='relu')(x)
x = BatchNormalization()(x)
x = Dropout(0.5)(x)
x = Dense(1000, activation='relu')(x)
x = BatchNormalization()(x)
x = Dropout(0.5)(x)
x = Dense(14, activation='linear')(x)

model = Model(inputs=input, outputs=x)
model.compile(loss='mse', optimizer='adam', metrics=['mae'])

现在我已经在前一个数据集上训练了这个模型,我想弹出输入张量层,并在模型前面加上一个形状与后一个数据集的图像尺寸相匹配的新输入张量。

model = load_model('path/to/my/trained/model.h5')
old_input = model.pop(0)
new_input = Input(shape=(540, 960, 3), name='image_input')
x = model(new_input)
m = Model(inputs=new_input, outputs=x)
m.save('transfer_model.h5')

这会产生这个错误:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/aicg2/.local/lib/python2.7/site-packages/keras/engine/topology.py", line 2506, in save
    save_model(self, filepath, overwrite, include_optimizer)
  File "/home/aicg2/.local/lib/python2.7/site-packages/keras/models.py", line 106, in save_model
    'config': model.get_config()
  File "/home/aicg2/.local/lib/python2.7/site-packages/keras/engine/topology.py", line 2322, in get_config
    layer_config = layer.get_config()
  File "/home/aicg2/.local/lib/python2.7/site-packages/keras/engine/topology.py", line 2370, in get_config
    new_node_index = node_conversion_map[node_key]
KeyError: u'image_input_ib-0'

在我链接的帖子中,maz 指出存在尺寸不匹配,阻止更改模型的输入层 - 如果是这种情况,那么我如何在前面放置一个 (480, 720, 3) 输入层期望 (224, 224, 3) 图像的 VGG16 模型?

我认为一个更可能的问题是,我以前的模型的输出预期与我根据 fchollet 在这篇文章中所说的内容给出的不同。我在语法上很困惑,但我相信整个x = Layer()(x)部分是从输入->输出逐层构建的,只是在前面抛出一个不同的输入就会破坏它。

虽然我真的不知道...

有人可以启发我如何完成我正在尝试做的事情,或者,如果不可能,请向我解释为什么不这样做?

4个回答

您可以通过使用新的输入形状创建一个新的 VGG16 模型实例new_shape并复制所有层权重来做到这一点。代码大致

new_model = VGG16(weights=None, input_shape=new_shape, include_top=False)
for new_layer, layer in zip(new_model.layers[1:], model.layers[1:]):
    new_layer.set_weights(layer.get_weights())

这应该很容易使用kerassurgeon. 首先你需要安装库;根据您是通过 TensorFlow(使用 tf 2.0 及更高版本)还是将 Keras 作为单独的库使用 Keras,需要以不同的方式安装它。

对于 TF 中的 Keras:pip install tfkerassurgeonhttps://github.com/Raukk/tf-keras-surgeon)。对于独立的 Keras pip install kerassurgeon:(https://github.com/BenWhetton/keras-surgeon

要替换输入(以 TF 2.0 为例;当前未经测试的代码):

from tensorflow import keras  # or import keras for standalone version
from tensorflow.keras.layers import Input

model = load_model('path/to/my/trained/model.h5')
new_input = Input(shape=(540, 960, 3), name='image_input')

# or kerassurgeon for standalone Keras
from tfkerassurgeon import delete_layer, insert_layer

model = delete_layer(model.layers[0])
# inserts before layer 0
model = insert_layer(model.layers[0], new_input)

这就是我在 Keras 模型中更改输入大小的方式。我有两个 CNN 模型,一个有输入大小[None,None,3]而另一个有输入大小[512,512,3]. 两种型号的重量相同。通过使用set_weights(model.get_weights()),模型 1 的权重可以转移到模型 2。

inputs = Input((None, None, 3))
.....
model = Model(inputs=[inputs], outputs=[outputs])
model.compile(optimizer='adam', loss='mean_squared_error')
model.load_weights('my_model_name.h5')

inputs2 = Input((512, 512, 3))
....
model2 = Model(inputs=[inputs2], outputs=[outputs])
model2.compile(optimizer='adam', loss='mean_squared_error')
model2.set_weights(model.get_weights())

VGGnet 输出维度的输出宽度和高度是输入宽度和高度的固定部分,因为改变这些维度的唯一层是池化层。输出中的通道数固定为最后一个卷积层中的滤波器数。展平层将对其进行整形以获得具有以下形状的一维:

((input_width * x) * (input_height * x) * channels)

其中 x 是一些小于 1 的小数。

要点是密集层的输入形状取决于整个模型输入的宽度和高度。密集层的形状输入不能改变,因为这意味着从神经网络中添加或删除节点。

避免这种情况的一种方法是使用全局池化层而不是扁平层(通常是 GlobalAveragePooling2D),这将找到每个通道的平均值,从而导致密集层(channels,)的输入形状不依赖于输入形状整个模型。

完成此操作后,网络中的任何层都不会依赖于输入的宽度和高度,因此可以使用类似的方式更改输入层

input_layer = InputLayer(input_shape=(480, 720, 3), name="input_1")
model.layers[0] = input_layer