Keras 替换输入层

Posted

技术标签:

【中文标题】Keras 替换输入层【英文标题】:Keras replacing input layer 【发布时间】:2018-09-07 20:55:10 【问题描述】:

我拥有的代码(我无法更改)使用带有my_input_tensor 作为 input_tensor 的 Resnet。

model1 = keras.applications.resnet50.ResNet50(input_tensor=my_input_tensor, weights='imagenet')

研究source code,ResNet50 函数使用my_input_tensor 创建一个新的keras 输入层,然后创建模型的其余部分。这是我想用我自己的模型复制的行为。我从 h5 文件加载我的模型。

model2 = keras.models.load_model('my_model.h5')

由于这个模型已经有一个输入层,我想用my_input_tensor定义的新输入层替换它。

如何替换输入层?

【问题讨论】:

你试过功能性的api吗 我没有。我查了文档。也许model2(my_input_tensor) 可以以某种方式使用? 如果模型不是连续的(我可以看到它的 resnet50),解决方案如下,如果是,你可以使用model.add() 【参考方案1】:

当您使用以下方式保存模型时:

old_model.save('my_model.h5')

它将保存以下内容:

    模型的架构,允许创建模型。 模型的权重。 模型的训练配置(损失、优化器)。 优化器的状态,允许训练从您之前离开的地方恢复。

那么,当你加载模型时:

res50_model = load_model('my_model.h5')

您应该得到相同的模型,您可以使用以下方法进行验证:

res50_model.summary()
res50_model.get_weights()

现在你可以,弹出输入层并添加你自己的使用:

res50_model.layers.pop(0)
res50_model.summary()

添加新的输入层:

newInput = Input(batch_shape=(0,299,299,3))    # let us say this new InputLayer
newOutputs = res50_model(newInput)
newModel = Model(newInput, newOutputs)

newModel.summary()
res50_model.summary()

【讨论】:

由于我输入了我想使用的张量,所以我结束了使用。 new_input_layer = keras.layers.Input(tensor=my_input_tensor) 并遵循您的建议。有用!谢谢。 model.layers.pop(0) 调用之后model.input 仍然显示原始输入层似乎很奇怪。我正在尝试使用 VGG16 模型发布的解决方案。 这将不起作用,如果我们创建一个 resnet18,然后我们删除第一层(pop)然后我们创建一个具有另一个输入的模型,然后是 resnet,发生的情况是它创建了一个模型一个输入层和第二个“模型”层,它与另一个断开连接。另外,这也否定了与其他模型连接/合并的可能性(这主要是我抱怨的原因)。 model.layers.pop(0) 不会改变任何东西。 github.com/tensorflow/tensorflow/issues/22479【参考方案2】:

Layers.pop(0) 或类似的东西不起作用。

您可以尝试两种选择:

1.

您可以创建具有所需层的新模型。

一个相对简单的方法是 i) 提取模型 json 配置,ii) 适当地更改它,iii) 从中创建一个新模型,然后 iv) 复制权重。我只会展示基本的想法。

i) 提取配置

model_config = model.get_config()

ii) 更改配置

input_layer_name = model_config['layers'][0]['name']
model_config['layers'][0] = 
                      'name': 'new_input',
                      'class_name': 'InputLayer',
                      'config': 
                          'batch_input_shape': (None, 300, 300),
                          'dtype': 'float32',
                          'sparse': False,
                          'name': 'new_input'
                      ,
                      'inbound_nodes': []
                  
model_config['layers'][1]['inbound_nodes'] = [[['new_input', 0, 0, ]]]
model_config['input_layers'] = [['new_input', 0, 0]]

ii) 创建一个新模型

new_model = model.__class__.from_config(model_config, custom_objects=)  # change custom objects if necessary

ii) 复制权重

# iterate over all the layers that we want to get weights from
weights = [layer.get_weights() for layer in model.layers[1:]]
for layer, weight in zip(new_model.layers[1:], weights):
    layer.set_weights(weight)

2.

您可以尝试像 kerassurgeon 这样的库(我正在链接到一个适用于 tensorflow keras 版本的 fork)。请注意,插入和删除操作仅适用于某些条件,例如兼容尺寸。

from kerassurgeon.operations import delete_layer, insert_layer

model = delete_layer(model, layer_1)
# insert new_layer_1 before layer_2 in a model
model = insert_layer(model, layer_2, new_layer_3)

【讨论】:

救了我,很有用。【参考方案3】:

不幸的是,@MilindDeore 的解决方案对我不起作用。虽然我可以打印新模型的摘要,但我在预测时收到“矩阵大小不兼容”错误。我想这是有道理的,因为密集层的新输入形状与旧密集层权重的形状不匹配。

因此,这是另一种解决方案。对我来说,关键是使用“_layers”而不是“layers”。后者似乎只返回一个副本。

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)

【讨论】:

如果您使用的是 2.X 中的 tf.keras,请使用带有下划线开头的 layer._batch_input_size【参考方案4】:

不幸的是,kerassurgeon 不支持我的模型,因为我有冻结层。我不得不对@MilindDeore 的解决方案进行一些小改动 - 将 model.layers.pop(0) 替换为 model._layers.pop(0) 并且它对我有用。请注意,我在 TF 2.0 中使用 tf.keras。

【讨论】:

【参考方案5】:

对于 Tensorflow 2 中的 tf.keras,使用 tfsurgeon 不起作用,因为我有自定义层。

所做的工作改变了layer._batch_input_size(注意下划线)

Hacky,但完成了工作。

【讨论】:

【参考方案6】:

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

对于 TF 中的 Keras:pip install tfkerassurgeon (https://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 = keras.models.load_model('my_model.h5')
my_input_tensor = Input(input_shape=(260, 260, 3))

# 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], my_input_tensor)

【讨论】:

更新此答案:“tfkerassurgeon”不再维护,不适用于当前的 TensorFlow 版本 (2.5.0)。另一方面,“kerassurgeon”确实通过 TensorFlow(即 tensorflow.keras)支持 Keras,而不仅仅是独立的 Keras。 随时编辑答案@SMD01

以上是关于Keras 替换输入层的主要内容,如果未能解决你的问题,请参考以下文章

Keras 序列模型输入层

Keras 输入层和 TensorFlow 占位符之间的区别

如何将 keras LSTM 层的输出输入到输入层?

Keras:指定始终保留某些特征的输入丢失层

是否可以根据 Keras 中的输入实现层之间的切换?

Python Keras:一个层输出与输入完全相同的东西