我正在尝试使用Keras API用平均池化层替换经过预训练的网络中的最大池化层。不知怎么对我不起作用。如果您能帮助我弄清楚如何实现它,我将不胜感激。
以下是我当前的解决方案:
def replace_max_by_average_pooling(model):
input_layer, *other_layers = model.layers
assert isinstance(input_layer, keras.layers.InputLayer)
x = input_layer.output
for layer in other_layers:
if isinstance(layer, keras.layers.MaxPooling2D):
layer = keras.layers.AveragePooling2D(
pool_size=layer.pool_size,
strides=layer.strides,
padding=layer.padding,
data_format=layer.data_format,
name=f"{layer.name}_av",
)
x = layer(x)
return keras.models.Model(inputs=input_layer.input, outputs=x)
当我尝试在VGG网络上使用此功能时:
vgg = keras.applications.vgg19.VGG19(include_top=False, weights="imagenet")
vgg_av = replace_max_by_average_pooling(vgg)
如果我打印摘要,它看起来不错:
_________________________________________________________________图层(类型)输出形状参数#
================================================== ============== input_1(InputLayer)(无,无,无,3)0
_________________________________________________________________ block1_conv1(Conv2D)(无,无,无,64)1792
_________________________________________________________________ block1_conv2(Conv2D)(无,无,无,64)36928
_________________________________________________________________ block1_pool_av(平均Pooli (无,无,无,64)0
_________________________________________________________________ block2_conv1(Conv2D)(无,无,无,128)73856
_________________________________________________________________ block2_conv2(Conv2D)(无,无,无,128)147584
_________________________________________________________________ block2_pool_av(平均Pooli (无,无,无,128)0
_________________________________________________________________ block3_conv1(Conv2D)(无,无,无,256)295168
...
但是,如果我尝试基于vgg_av
的几层来构建新模型:
layer = vgg_av.get_layer("block3_conv1")
keras.models.Model(inputs=vgg_av.layers[0].input, outputs=layer.output).summary()
某种程度上,平均池化层再次被最大池化层取代:
_________________________________________________________________图层(类型)输出形状参数#
================================================== ============== input_1(InputLayer)(无,无,无,3)0
_________________________________________________________________ block1_conv1(Conv2D)(无,无,无,64)1792
_________________________________________________________________ block1_conv2(Conv2D)(无,无,无,64)36928
_________________________________________________________________ block1_pool(MaxPooling2D)(无,无,无,64)0
_________________________________________________________________ block2_conv1(Conv2D)(无,无,无,128)73856
_________________________________________________________________ block2_conv2(Conv2D)(无,无,无,128)147584
_________________________________________________________________ block2_pool(MaxPooling2D)(无,无,无,128)0
_________________________________________________________________ block3_conv1(Conv2D)(无,无,无,256)295168
================================================== ===============总参数:555,328可训练参数:555,328非可训练参数: 0
我做错什么了吗?为什么在哪里?
我的猜测是,在此行x = layer(x)
上将新操作添加到计算图中,使得新操作的名称为*name of an old operation*_1
,并且当我调用vgg_av.get_layer("block3_conv1")
时,它仍会提取子图来自vgg
。但是,如果我在vgg_av
中打印图层名称,则这些名称与vgg
中的名称相同。为什么只有在尝试获取图层的子集时它才会失败?我本以为完全重建计算图,但也许我缺少某些Keras API或从概念上讲缺少某些东西。
答案 0 :(得分:1)
原因是,每当您重复使用图层时(在使用avg池创建新分支时重新使用图层),都会在图形中创建一个新节点。
原始模型仍然存在,并且对所有层使用索引为0的节点,而新模型使用索引为1的节点。
图层应具有方法get_output_at(index)
或类似的方法,您要将要从中获取输出的节点传递给该方法。从过去的经验中,我猜想layer.output
会带来一个错误,因为您有多个节点(但是令人惊讶的是代码接受了这一点-我猜想,keras的版本会有所不同)。
因此,您应该使用以下方法实现目标:
layer = vgg_av.get_layer("block3_conv1")
output = layer.get_output_at(1)
keras.models.Model(inputs=vgg_av.layers[0].input, outputs=output).summary()
在调用最后一层之后,最好在.outputs
内计算新模型最后一层replace_max_by_average_pooling
的数量,以防您有更多类似的模型来自相同的原始模型(意味着更多的节点)。
Keras中的保存和加载模型提供了一个系统(最初用于自定义层和自定义函数),您可以在其中定义哪些keras用于不知道的类名和函数名。
加载模型是“使用保存的参数再次创建模型”。因此,如果使用此系统“替换”现有名称,则它应该在模型重建期间替换图层。
custom_objects = {'MaxPooling2D': AveragePooling2D}
vgg.save_model(filename)
vgg_ag = keras.models.load_model(filename, custom_objects = custom_objects)
如果这不起作用,则可以创建一个自定义函数,该函数使用以下给定参数返回平均值池:
def createAvgFromMax(**params):
#study the params, choose what to keep and discard
return AveragePooling2D(....)
还有custom_objects = { 'MaxPooling2D': createAvgFromMax }