如何在不破坏结构的情况下重命名 Keras 模型的层?
How to rename the layers of a Keras model without corrupting the structure?
对于 some library functionality 我正在尝试重命名给定模型的层(包括输入层)。
以下最小示例显示了我 运行 使用当前方法(使用 TensorFlow 2.3)时遇到的错误:
from tensorflow.keras.models import load_model
model = load_model("model.h5")
for layer in model.layers:
layer._name = layer.name + "_renamed"
model.to_json()
ValueError: The target structure is of type `<class 'tensorflow.python.framework.ops.Tensor'>`
Tensor("input_1:0", shape=(None, 4), dtype=float32)
However the input structure is a sequence (<class 'list'>) of length 0.
model.h5
文件可能是这样创建的,例如:
from tensorflow.keras.layers import Input, Dense
from tensorflow.keras.models import Model
inputs = Input(shape=(4,))
x = Dense(5, activation='relu', name='a')(inputs)
x = Dense(3, activation='softmax', name='b')(x)
model = Model(inputs=inputs, outputs=x)
model.compile(loss='categorical_crossentropy', optimizer='nadam')
model.save("model.h5")
知道如何解决这个问题吗?
问题:Keras通过遍历layer._inbound_nodes
和comparing againstmodel._network_nodes
对网络进行序列化;设置 layer._name
时,后者会保留 原始 名称。
解决方案:相应地重命名_network_nodes
。工作函数在底部,示例如下:
from tensorflow.keras.models import load_model
from tensorflow.keras.layers import Input, Dense
from tensorflow.keras.models import Model
ipt = Input((16,))
out = Dense(16)(ipt)
model = Model(ipt, out)
model.compile('sgd', 'mse')
rename(model, model.layers[1], 'new_name')
model.save('model.h5')
loaded = load_model('model.h5')
注意:layer.name
是一个 @property
without a .setter
, meaning it's not meant to be set (as evident). Further, layer.__setattr__
被覆盖,并执行除了设置属性之外的步骤 - 可能是必要的,但不能确切地确定它可能有什么其他影响。我已经包含了一个绕过这些的替代方案。最多将此视为临时解决方案;我建议在 Github 上开一个 Issue,因为 API-side 更改到期。
函数:
并非万无一失 - _get_node_suffix
的命名逻辑需要改进(例如 dense_1
可能会与 dense_11
混淆)。
def rename(model, layer, new_name):
def _get_node_suffix(name):
for old_name in old_nodes:
if old_name.startswith(name):
return old_name[len(name):]
old_name = layer.name
old_nodes = list(model._network_nodes)
new_nodes = []
for l in model.layers:
if l.name == old_name:
l._name = new_name
# vars(l).__setitem__('_name', new) # bypasses .__setattr__
new_nodes.append(new_name + _get_node_suffix(old_name))
else:
new_nodes.append(l.name + _get_node_suffix(l.name))
model._network_nodes = set(new_nodes)
对于 some library functionality 我正在尝试重命名给定模型的层(包括输入层)。
以下最小示例显示了我 运行 使用当前方法(使用 TensorFlow 2.3)时遇到的错误:
from tensorflow.keras.models import load_model
model = load_model("model.h5")
for layer in model.layers:
layer._name = layer.name + "_renamed"
model.to_json()
ValueError: The target structure is of type `<class 'tensorflow.python.framework.ops.Tensor'>`
Tensor("input_1:0", shape=(None, 4), dtype=float32)
However the input structure is a sequence (<class 'list'>) of length 0.
model.h5
文件可能是这样创建的,例如:
from tensorflow.keras.layers import Input, Dense
from tensorflow.keras.models import Model
inputs = Input(shape=(4,))
x = Dense(5, activation='relu', name='a')(inputs)
x = Dense(3, activation='softmax', name='b')(x)
model = Model(inputs=inputs, outputs=x)
model.compile(loss='categorical_crossentropy', optimizer='nadam')
model.save("model.h5")
知道如何解决这个问题吗?
问题:Keras通过遍历layer._inbound_nodes
和comparing againstmodel._network_nodes
对网络进行序列化;设置 layer._name
时,后者会保留 原始 名称。
解决方案:相应地重命名_network_nodes
。工作函数在底部,示例如下:
from tensorflow.keras.models import load_model
from tensorflow.keras.layers import Input, Dense
from tensorflow.keras.models import Model
ipt = Input((16,))
out = Dense(16)(ipt)
model = Model(ipt, out)
model.compile('sgd', 'mse')
rename(model, model.layers[1], 'new_name')
model.save('model.h5')
loaded = load_model('model.h5')
注意:layer.name
是一个 @property
without a .setter
, meaning it's not meant to be set (as evident). Further, layer.__setattr__
被覆盖,并执行除了设置属性之外的步骤 - 可能是必要的,但不能确切地确定它可能有什么其他影响。我已经包含了一个绕过这些的替代方案。最多将此视为临时解决方案;我建议在 Github 上开一个 Issue,因为 API-side 更改到期。
函数:
并非万无一失 - _get_node_suffix
的命名逻辑需要改进(例如 dense_1
可能会与 dense_11
混淆)。
def rename(model, layer, new_name):
def _get_node_suffix(name):
for old_name in old_nodes:
if old_name.startswith(name):
return old_name[len(name):]
old_name = layer.name
old_nodes = list(model._network_nodes)
new_nodes = []
for l in model.layers:
if l.name == old_name:
l._name = new_name
# vars(l).__setitem__('_name', new) # bypasses .__setattr__
new_nodes.append(new_name + _get_node_suffix(old_name))
else:
new_nodes.append(l.name + _get_node_suffix(l.name))
model._network_nodes = set(new_nodes)