如何在Keras模型中替换(或插入)中间层?

ZFT*_*rbo 16 keras

我有一个训练有素的Keras模型,我想要:

1)用相同但无偏差的Con2D层替换。

2)在首次激活之前添加BatchNormalization层

我怎样才能做到这一点?

def keras_simple_model():
    from keras.models import Model
    from keras.layers import Input, Dense,  GlobalAveragePooling2D
    from keras.layers import Conv2D, MaxPooling2D, Activation

    inputs1 = Input((28, 28, 1))
    x = Conv2D(4, (3, 3), activation=None, padding='same', name='conv1')(inputs1)
    x = Activation('relu')(x)
    x = Conv2D(4, (3, 3), activation=None, padding='same', name='conv2')(x)
    x = Activation('relu')(x)
    x = MaxPooling2D((2, 2), strides=(2, 2), name='pool1')(x)

    x = Conv2D(8, (3, 3), activation=None, padding='same', name='conv3')(x)
    x = Activation('relu')(x)
    x = Conv2D(8, (3, 3), activation=None, padding='same', name='conv4')(x)
    x = Activation('relu')(x)
    x = MaxPooling2D((2, 2), strides=(2, 2), name='pool2')(x)

    x = GlobalAveragePooling2D()(x)
    x = Dense(10, activation=None)(x)
    x = Activation('softmax')(x)

    model = Model(inputs=inputs1, outputs=x)
    return model


if __name__ == '__main__':
    model = keras_simple_model()
    print(model.summary())
Run Code Online (Sandbox Code Playgroud)

ale*_*xhg 14

以下功能允许您在名称与正则表达式匹配的原始模型中的每一层之前之后替换新层,包括非顺序模型,例如DenseNet或ResNet。

import re
from keras.models import Model

def insert_layer_nonseq(model, layer_regex, insert_layer_factory,
                        insert_layer_name=None, position='after'):

    # Auxiliary dictionary to describe the network graph
    network_dict = {'input_layers_of': {}, 'new_output_tensor_of': {}}

    # Set the input layers of each layer
    for layer in model.layers:
        for node in layer.outbound_nodes:
            layer_name = node.outbound_layer.name
            if layer_name not in network_dict['input_layers_of']:
                network_dict['input_layers_of'].update(
                        {layer_name: [layer.name]})
            else:
                network_dict['input_layers_of'][layer_name].append(layer.name)

    # Set the output tensor of the input layer
    network_dict['new_output_tensor_of'].update(
            {model.layers[0].name: model.input})

    # Iterate over all layers after the input
    for layer in model.layers[1:]:

        # Determine input tensors
        layer_input = [network_dict['new_output_tensor_of'][layer_aux] 
                for layer_aux in network_dict['input_layers_of'][layer.name]]
        if len(layer_input) == 1:
            layer_input = layer_input[0]

        # Insert layer if name matches the regular expression
        if re.match(layer_regex, layer.name):
            if position == 'replace':
                x = layer_input
            elif position == 'after':
                x = layer(layer_input)
            elif position == 'before':
                pass
            else:
                raise ValueError('position must be: before, after or replace')

            new_layer = insert_layer_factory()
            if insert_layer_name:
                new_layer.name = insert_layer_name
            else:
                new_layer.name = '{}_{}'.format(layer.name, 
                                                new_layer.name)
            x = new_layer(x)
            print('Layer {} inserted after layer {}'.format(new_layer.name,
                                                            layer.name))
            if position == 'before':
                x = layer(x)
        else:
            x = layer(layer_input)

        # Set new output tensor (the original one, or the one of the inserted
        # layer)
        network_dict['new_output_tensor_of'].update({layer.name: x})

    return Model(inputs=model.inputs, outputs=x)

Run Code Online (Sandbox Code Playgroud)

与单纯的顺序模型的简单情况相比,不同之处在于,在遍历各层以查找关键层之前,您首先要分析图形并将每层的输入层存储在辅助字典中。然后,当您遍历各层时,还将存储每层的新输出张量,该张量用于在构建新模型时确定各层的输入层。

以下是一个用例,其中在ResNet50的每个激活层之后插入一个Dropout层:

from keras.applications.resnet50 import ResNet50

model = ResNet50()
def dropout_layer_factory():
    return Dropout(rate=0.2, name='dropout')
model = insert_layer_nonseq(model, '.*activation.*, dropout_layer_factory)
model.summary()
Run Code Online (Sandbox Code Playgroud)

  • 谢谢你!这对我有用。但是,当尝试在同一模型上调用两次“insert_layer_nonseq(...)”函数时,我注意到一个问题:调用“x = layer(”时,“outbound_layers”和“inbound_layers”被附加到 - 而不是被替换。我的修复方法是将 `network_dict['input_layers_of'][layer_name].append(layer.name)` 放置在 if 条件 `if (layer.name not in network_dict['input_layers_of'][layer_name]):` 中 (2认同)
  • 我必须将“outbound_nodes”更改为“_outbound_nodes” (2认同)
  • 每次插入新层时,我们都会更改内部 Keras 图连接,即创建新的入站和出站节点,如果我们想要进行进一步的网络“手术,这可能会成为问题的根源。复杂的解决方案:手动编写一个函数删除旧节点(我这样做了,但这并不简单)。简单的解决方案:保存模型并再次加载。这样,不必要的节点就不再是图形的一部分。 (2认同)

ZFT*_*rbo 12

您可以使用以下功能:

def replace_intermediate_layer_in_keras(model, layer_id, new_layer):
    from keras.models import Model

    layers = [l for l in model.layers]

    x = layers[0].output
    for i in range(1, len(layers)):
        if i == layer_id:
            x = new_layer(x)
        else:
            x = layers[i](x)

    new_model = Model(input=layers[0].input, output=x)
    return new_model

def insert_intermediate_layer_in_keras(model, layer_id, new_layer):
    from keras.models import Model

    layers = [l for l in model.layers]

    x = layers[0].output
    for i in range(1, len(layers)):
        if i == layer_id:
            x = new_layer(x)
        x = layers[i](x)

    new_model = Model(input=layers[0].input, output=x)
    return new_model
Run Code Online (Sandbox Code Playgroud)

范例

if __name__ == '__main__':
    from keras.layers import Conv2D, BatchNormalization
    model = keras_simple_model()
    print(model.summary())
    model = replace_intermediate_layer_in_keras(model, 3, Conv2D(4, (3, 3), activation=None, padding='same', name='conv2_repl', use_bias=False))
    print(model.summary())
    model = insert_intermediate_layer_in_keras(model, 4, BatchNormalization())
    print(model.summary())
Run Code Online (Sandbox Code Playgroud)

由于图层形状等原因,替换上有一些限制。

  • 这对我不起作用,它似乎在连接和合并层方面存在问题:ValueError:应该在输入列表上调用合并层。 (3认同)