如何从Keras模型中删除前N层



我想从预训练的Keras模型中删除第一个N层。例如,EfficientNetB0,其前3层仅负责预处理:

import tensorflow as tf
efinet = tf.keras.applications.EfficientNetB0(weights=None, include_top=True)
print(efinet.layers[:3])
# [<tensorflow.python.keras.engine.input_layer.InputLayer at 0x7fa9a870e4d0>,
# <tensorflow.python.keras.layers.preprocessing.image_preprocessing.Rescaling at 0x7fa9a61343d0>,
# <tensorflow.python.keras.layers.preprocessing.normalization.Normalization at 0x7fa9a60d21d0>]

正如M.Innat所提到的,第一层是Input Layer,应该保留或重新连接。我想删除这些层,但像这样简单的方法会抛出错误:

cut_input_model = return tf.keras.Model(
inputs=[efinet.layers[3].input], 
outputs=efinet.outputs
)

这将导致:

ValueError: Graph disconnected: cannot obtain value for tensor KerasTensor(...)

建议的方法是什么?

得到Graph disconnected错误的原因是没有指定Input层。但这不是主要问题。有时从keras模型中移除中间层对于SequentialFunctionalAPI来说并不简单。

对于顺序,它相对来说应该很容易,而在功能模型中,您需要关心多输入块(例如multiplyadd等(。例如:如果你想删除序列模型中的一些中间层,你可以很容易地调整这个解决方案。但对于函数模型(efficientnet(,由于多输入内部块,您不能这样做,您将遇到以下错误:ValueError: A merged layer should be called on a list of inputs。因此,这需要更多的工作AFAIK,这里有一个可能的方法来克服它


这里我将为您的案例展示一个简单的解决方法,但它可能不通用,在某些情况下也不安全。基于这种方法;使用CCD_ 12方法。为什么使用它不安全!。好的,我们先加载模型。

func_model = tf.keras.applications.EfficientNetB0()
for i, l in enumerate(func_model.layers):
print(l.name, l.output_shape)
if i == 8: break
input_19 [(None, 224, 224, 3)]
rescaling_13 (None, 224, 224, 3)
normalization_13 (None, 224, 224, 3)
stem_conv_pad (None, 225, 225, 3)
stem_conv (None, 112, 112, 32)
stem_bn (None, 112, 112, 32)
stem_activation (None, 112, 112, 32)
block1a_dwconv (None, 112, 112, 32)
block1a_bn (None, 112, 112, 32)

接下来,使用.pop方法:

func_model._layers.pop(1) # remove rescaling
func_model._layers.pop(1) # remove normalization
for i, l in enumerate(func_model.layers):
print(l.name, l.output_shape)
if i == 8: break
input_22 [(None, 224, 224, 3)]
stem_conv_pad (None, 225, 225, 3)
stem_conv (None, 112, 112, 32)
stem_bn (None, 112, 112, 32)
stem_activation (None, 112, 112, 32)
block1a_dwconv (None, 112, 112, 32)
block1a_bn (None, 112, 112, 32)
block1a_activation (None, 112, 112, 32)
block1a_se_squeeze (None, 32)

对于我@M.Innat的解决方案导致了一个断开的图,因为仅仅弹出层是不够的,需要在输入层和第一个卷积层之间建立连接(您可以使用Netron检查问题(。

对我来说,唯一合适的解决方案是手动编辑模型的配置。

这是一个完整的脚本,删除了Efficientnet-B1的预处理部分。已使用TF2进行测试。

import tensorflow as tf
def split(model, start, end):
confs = model.get_config()
kept_layers = set()
for i, l in enumerate(confs['layers']):
if i == 0:
confs['layers'][0]['config']['batch_input_shape'] = model.layers[start].input_shape
if i != start:
#confs['layers'][0]['name'] += str(random.randint(0, 100000000)) # rename the input layer to avoid conflicts on merge
confs['layers'][0]['config']['name'] = confs['layers'][0]['name']
elif i < start or i > end:
continue
kept_layers.add(l['name'])
# filter layers
layers = [l for l in confs['layers'] if l['name'] in kept_layers]
layers[1]['inbound_nodes'][0][0][0] = layers[0]['name']
# set conf
confs['layers'] = layers
confs['input_layers'][0][0] = layers[0]['name']
confs['output_layers'][0][0] = layers[-1]['name']
# create new model
submodel = tf.keras.Model.from_config(confs)
for l in submodel.layers:
orig_l = model.get_layer(l.name)
if orig_l is not None:
l.set_weights(orig_l.get_weights())
return submodel

model = tf.keras.applications.efficientnet.EfficientNetB1()
# first layer = 3, last layer = 341
new_model = split(model, 3, 341)
new_model.summary()
new_model.save("efficientnet_b1.h5")

剧本就是基于这个伟大的答案。

我一直在尝试对keras tensorflow VGGFace模型做同样的事情。经过大量的实验,我发现这种方法是有效的。在这种情况下,除了最后一层之外,所有的模型都被使用了,最后一层被自定义嵌入层取代:

vgg_model = VGGFace(include_top=True, input_shape=(224, 224, 3)) # full VGG16 model
inputs = Input(shape=(224, 224, 3))
x = inputs
# Assemble all layers except for the last layer
for layer in vgg_model.layers[1:-2]:
x = vgg_model.get_layer(layer.name)(x)

# Now add a new last layer that provides the 128 embeddings output
x = Dense(128, activation='softmax', use_bias=False, name='fc8x')(x)
# Create the custom model
custom_vgg_model = Model(inputs, x, name='custom_vggface')

与layers[x]或pop((不同,get_layer获取实际的层,从而将它们组装到一个新的输出层集中。然后可以从中创建新模型。"for"语句以1而不是0开头,因为输入层已由"inputs"定义。

这种方法适用于顺序模型。尚不清楚它是否适用于更复杂的模型。

相关内容

  • 没有找到相关文章

最新更新