首页 > 解决方案 > 为什么使用 Keras API 将最大池替换为平均池会失败?

问题描述

我正在尝试使用 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) (None, None, None, 3) 0
_________________________________________________________________ block1_conv1 (Conv2D) (None, None, None, 64)1792
_________________________________________________________________ block1_conv2(Conv2D)(无,无,无,64)36928
_________________________________________________________________ block1_pool_av(平均池(无,无,无,64)0
_________________________________________________________________ block2_conv1 (Conv2D) (无,无,无,128) 73856
_________________________________________________________________ block2_conv2 (Conv2D) (无,无,无,128) 147584
_________________________________________________________________ block2_pool_av (AveragePooli (无,无,无,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) (None, None, None, 3) 0
_________________________________________________________________ block1_conv1 (Conv2D) (None, None, None, 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 *name of an old operation*_1,并且当我调用vgg_av.get_layer("block3_conv1")它时仍然从vgg. 但如果我在 中打印图层名称vgg_av,则名称与 中相同vgg。为什么只有在我尝试获取图层子集时它才会失败?我想完全重建计算图,但也许我缺少一些 Keras API,或者我在概念上缺少一些东西。

标签: pythontensorflowkeras

解决方案


原因是每当您重用一个层(您在使用平均池创建新分支时重用层)时,您都会在图中创建一个新节点。

原始模型仍然存在,并且所有层都使用索引 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 }


推荐阅读