【问题标题】:Modify layer parameters in Keras在 Keras 中修改图层参数
【发布时间】:2019-03-19 12:36:28
【问题描述】:

我对更新 Keras 中现有的图层参数感兴趣(不是删除图层并插入新图层,而只是修改现有参数)。

我将举一个我正在编写的函数的示例:

def add_filters(self, model):
    conv_indices = [i for i, layer in enumerate(model.layers) if 'convolution' in layer.get_config()['name']]
    random_conv_index = random.randint(0, len(conv_indices)-1)
    factor = 2
    conv_layer = model.layers[random_conv_index]
    conv_layer.filters = conv_layer.filters * factor
    print('new conv layer filters after transform is:', conv_layer.filters)
    print('just to make sure, its:', model.layers[random_conv_index].filters)
    return model

所以这里基本上发生的事情是我从我的网络中获取一个随机卷积层(我所有的卷积层的名称中都有“卷积”)并尝试将过滤器加倍。据我所知,在任何情况下,这都不应该导致输入/输出大小兼容性的任何“编译问题”。

问题是,我的模型根本没有改变。我最后添加的 2 个打印输出打印了正确的数字(是之前过滤器数量的两倍)。但是当我编译模型并打印model.summary()时,还是看到了之前的过滤量。

顺便说一句,我并不局限于 Keras。例如,如果有人知道如何使用 PyTorch 实现这一目标,我也会购买它:D

【问题讨论】:

    标签: python neural-network keras deep-learning conv-neural-network


    【解决方案1】:

    好吧,如果您想在现有模型的基础上创建新模型的架构,尽管进行一些修改,您可以使用to_jsonmodel_from_json() 函数。这是一个例子:

    model = Sequential()
    model.add(Conv2D(10, (3,3), input_shape=(100,100,3)))
    model.add(Conv2D(40, (3,3)))
    
    model.summary()
    

    模型总结:

    Layer (type)                 Output Shape              Param #   
    =================================================================
    conv2d_12 (Conv2D)           (None, 98, 98, 10)        280       
    _________________________________________________________________
    conv2d_13 (Conv2D)           (None, 96, 96, 40)        3640      
    =================================================================
    Total params: 3,920
    Trainable params: 3,920
    Non-trainable params: 0
    _________________________________________________________________
    

    现在我们修改第一层的过滤器数量,并基于修改后的架构创建一个新模型:

    from keras.models import model_from_json
    
    model.layers[0].filters *= 2
    new_model = model_from_json(model.to_json())
    new_model.summary()
    

    新模型总结:

    Layer (type)                 Output Shape              Param #   
    =================================================================
    conv2d_12 (Conv2D)           (None, 98, 98, 20)        560       
    _________________________________________________________________
    conv2d_13 (Conv2D)           (None, 96, 96, 40)        7240      
    =================================================================
    Total params: 7,800
    Trainable params: 7,800
    Non-trainable params: 0
    _________________________________________________________________
    

    也可以不修改模型实例直接修改model.to_json()的输出。


    您可以轻松使用get_weights() 方法获取卷积层的当前权重。它将返回两个 numpy 数组的列表。第一个对应于过滤器权重,第二个对应于偏置参数。然后你可以使用set_weights() 方法来设置新的权重:

    conv_layer = model.layers[random_conv_index]
    weights = conv_layer.get_weights()
    weights[0] *= factor  # multiply filter weights by `factor`
    conv_layer.set_weights(weights)
    

    附带说明一下,您在代码中使用的卷积层的filters 属性对应于该层中过滤器的数量,而不是它们的权重。

    【讨论】:

    • 感谢您的回答,但您没有明白我的意思。我正在尝试增加图层中过滤器的数量 - 而不是改变权重。例如 - 将 [28,28,64] 卷积层转换为 [28,28,128] 卷积层。在我的示例中,第一个轴是高度,第二个是宽度,第三个是深度(不包括样本数作为第一个轴)。
    • @erap129 哦!对于那个很抱歉。至于在定义模型后更改过滤器的数量,我猜你不能这样做,因为它会影响所有后续层、它们的输入形状和它们的参数(即权重)。你需要做这样的事情的具体场景是什么?即为什么你不能从一开始就创建一个新模型或指定正确的过滤器数量?
    • 我正在尝试创建一种算法来变形卷积网络。我想在不重建整个架构的情况下从一种架构转到另一种架构。无论如何,看到卷积进入“全深度” - 我不明白为什么形状不匹配会成为问题。当然,以前的权重将变得无用,但我只会重新训练整个网络。另一种选择是保留一个包含网络定义的“元”数据结构,并在此基础上对网络进行变形——但我正在寻找一个更简洁的选择。
    • @erap129 问题是,当您将过滤器的数量从 32 更改为 64 时,该层的输出形状会发生变化,即从 (?,?,32)(?,?,64)。结果,下一层的输入形状发生了变化,因此会影响其权重,例如如果它是一个卷积层,则该层中的每个过滤器之前的形状为 (3,3,32)(假设内核大小为 3),但现在它们的形状应该为 (3,3,64),这可能会在模型中进一步向下传播。
    • 是的,我知道,我想改变形状 :) 无论如何,我做了一个 hacky 解决方案,将模型“描述”保存为字符串列表。每次我想向图层添加更多过滤器时,我只需获取存储的描述,找到我想要添加过滤器的图层并更新其描述。然后我根据更新的描述制作了一个全新的模型。这很混乱,我希望我能找到更好的方法,我的猜测是 TF/pytorch 可以胜任这项工作,但我对这些框架的经验较少。
    【解决方案2】:

    另一种解决方法是重新设置图层的属性。例如,如果有人想改变卷积层的内核初始化器,下面是一个小例子:

    img_input = tf.keras.Input(shape=(256,256,1)) 
    
    x = tf.keras.layers.Conv2D(64, (7, 7), padding='same', use_bias=False, kernel_initializer=None,name='conv')(img_input) 
    
    model = tf.keras.Model(inputs=[img_input], outputs=[x], name='resnext') 
    for layer in model.layers:
            print(layer.get_config())
    

    输出:

    {'batch_input_shape': (None, 256, 256, 1), 'dtype': 'float32', 'sparse': False, 'name': 'input_1'}
    {'name': 'conv2d', 'trainable': True, 'dtype': 'float32', 'filters': 64, 'kernel_size': (7, 7), 'strides': (1, 1), 'padding': 'same', 'data_format': 'channels_last', 'dilation_rate': (1, 1), 'activation': 'linear', 'use_bias': False, 'kernel_initializer': None, 'bias_initializer': {'class_name': 'Zeros', 'config': {'dtype': 'float32'}}, 'kernel_regularizer': None, 'bias_regularizer': None, 'activity_regularizer': None, 'kernel_constraint': None, 'bias_constraint': None}
     
    

    设置后:

    init1 = tf.keras.initializers.TruncatedNormal() 
        for layer in model.layers:
              if hasattr(layer, 'kernel_initializer'):
                    setattr(layer, 'kernel_initializer', init1)
            
        for layer in model.layers:
              print(layer.get_config())
    

    输出:

    {'batch_input_shape': (None, 256, 256, 1), 'dtype': 'float32', 'sparse': False, 'name': 'input_1'}
    {'name': 'conv2d', 'trainable': True, 'dtype': 'float32', 'filters': 64, 'kernel_size': (7, 7), 'strides': (1, 1), 'padding': 'same', 'data_format': 'channels_last', 'dilation_rate': (1, 1), 'activation': 'linear', 'use_bias': False, 'kernel_initializer': {'class_name': 'TruncatedNormal', 'config': {'mean': 0.0, 'stddev': 0.05, 'seed': None, 'dtype': 'float32'}}, 'bias_initializer': {'class_name': 'Zeros', 'config': {'dtype': 'float32'}}, 'kernel_regularizer': None, 'bias_regularizer': None, 'activity_regularizer': None, 'kernel_constraint': None, 'bias_constraint': None}
    

    内核初始化器已设置

    【讨论】:

    • 这将导致......无法设置属性“recurrent_dropout”,可能是因为它与对象的现有只读@property 冲突。请选择其他名称。
    猜你喜欢
    • 1970-01-01
    • 2019-01-01
    • 1970-01-01
    • 2018-04-30
    • 1970-01-01
    • 1970-01-01
    • 2019-09-15
    • 2019-12-28
    • 1970-01-01
    相关资源
    最近更新 更多