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 替换输入层的主要内容,如果未能解决你的问题,请参考以下文章