Keras —转移学习—更改输入张量形状


15

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

我有两个图像数据集,其中一个具有形状(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)片段都是从输入->输出逐段构造图层,而只是在前面抛出不同的输入会破坏它。

我真的不知道...

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


你解决了吗?
tktktk0711

Answers:


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())


@ r-zip我收到一个错误: Traceback (most recent call last): File "predict_video11.py", line 67, in <module> new_layer.set_weights(layer.get_weights()) File "/usr/local/lib/python2.7/dist-packages/keras/engine/base_layer.py", line 1057, in set_weights 'provided weight shape ' + str(w.shape)) ValueError: Layer weight shape (3, 3, 33, 64) not compatible with provided weight shape (3, 3, 9, 64) 那是Input层,所以使用[2:]
mLstudent33 '19

1

VGGnet的输出尺寸的输出宽度和高度是输入宽度和高度的固定部分,因为更改这些尺寸的唯一层是池化层。输出中的通道数固定为最后一个卷积层中的滤波器数。扁平层将对其重塑形状,使其具有一维的形状:

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

其中x是小数<1。

要点是,“密集”层的输入形状取决于整个模型的输入宽度和高度。输入到密集层的形状无法更改,因为这将意味着在神经网络中添加或删除节点。

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

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

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

model.layers[0] = input_layer在TF 2.1中对我不起作用。没有错误,但实际上并未替换该层。看起来复制权重可能更可靠(请参阅其他答案)。
z0r

0

这是另一种解决方案,不特定于VGG模型。

注意,密层的权重无法复制(因此将被重新初始化)。这是有道理的,因为权重的形状在新旧模型中都不同。

import keras
import numpy as np

def get_model():
    old_input_shape = (20, 20, 3)
    model = keras.models.Sequential()
    model.add(keras.layers.Conv2D(9, (3, 3), padding="same", input_shape=old_input_shape))
    model.add(keras.layers.MaxPooling2D((2, 2)))
    model.add(keras.layers.Flatten())
    model.add(keras.layers.Dense(1, activation="sigmoid"))
    model.compile(loss='binary_crossentropy', optimizer=keras.optimizers.Adam(lr=0.0001), metrics=['acc'], )
    model.summary()
    return model

def change_model(model, new_input_shape=(None, 40, 40, 3)):
    # replace input shape of first layer
    model._layers[1].batch_input_shape = new_input_shape

    # feel free to modify additional parameters of other layers, for example...
    model._layers[2].pool_size = (8, 8)
    model._layers[2].strides = (8, 8)

    # rebuild model architecture by exporting and importing via json
    new_model = keras.models.model_from_json(model.to_json())
    new_model.summary()

    # copy weights from old model to new one
    for layer in new_model.layers:
        try:
            layer.set_weights(model.get_layer(name=layer.name).get_weights())
        except:
            print("Could not transfer weights for layer {}".format(layer.name))

    # test new model on a random input image
    X = np.random.rand(10, 40, 40, 3)
    y_pred = new_model.predict(X)
    print(y_pred)

    return new_model

if __name__ == '__main__':
    model = get_model()
    new_model = change_model(model)

0

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

对于TF中的Keras:pip install tfkerassurgeonhttps://github.com/Raukk/tf-keras-surgeon)。对于独立的Keras:pip install kerassurgeonhttps://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)

0

@gebbissimo答案在TF2中对我有用,只是我在下面的单个函数中分享的一些小改动:

def change_input_size(model,h,w,ch=3):
   model._layers[0]._batch_input_shape = (None,h,w,ch)
   new_model = keras.models.model_from_json(model.to_json())
   new_model.summary()
   for layer,new_layer in zip(model.layers,new_model.layers):
      new_layer.set_weights(layer.get_weights())
   return new_model

0

这就是我如何在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())
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.