为什么使用Keras API用平均池替换最大池失败?

时间:2019-02-04 00:00:51

标签: python tensorflow keras

我正在尝试使用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或从概念上讲缺少某些东西。

1 个答案:

答案 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 }