如何在Keras中将密集层转换为等效的卷积层?

时间:2016-12-15 09:47:05

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

我想使用Keras做类似于完全卷积网络的论文(https://people.eecs.berkeley.edu/~jonlong/long_shelhamer_fcn.pdf)。我有一个网络,最终使特征映射变平并通过几个密集层运行它们。我想将这样的网络中的权重加载到一个密集层被等效卷积替换的地方。

可以使用Keras附带的VGG16网络作为示例,其中最后一个MaxPooling2D()的7x7x512输出被展平,然后进入密集(4096)层。在这种情况下,Dense(4096)将被7x7x4096卷积替换。

我的真实网络略有不同,有一个GlobalAveragePooling2D()层而不是MaxPooling2D()和Flatten()。 GlobalAveragePooling2D()的输出是2D张量,并且不需要另外将其展平,因此包括第一个的所有密集层将被1x1卷积替换。

我已经看到了这个问题:Python keras how to transform a dense layer into a convolutional layer如果不相同则看起来非常相似。问题是我无法获得建议的解决方案,因为(a)我使用TensorFlow作为后端,因此权重重新排列/过滤"旋转"是不对的,(b)我无法弄清楚如何加载权重。使用model.load_weights(by_name=True)将旧权重文件加载到新网络中并不起作用,因为名称不匹配(即使它们的尺寸不同)。

使用TensorFlow时重新排列应该是什么?

如何加载重量?我是否创建了每个模型中的一个,在两者上调用model.load_weights()来加载相同的权重,然后复制一些需要重新排列的额外权重?

2 个答案:

答案 0 :(得分:5)

基于hars的答案,我创建了这个函数来将任意cnn转换为fcn:

from keras.models import Sequential
from keras.layers.convolutional import Convolution2D
from keras.engine import InputLayer
import keras

def to_fully_conv(model):

    new_model = Sequential()

    input_layer = InputLayer(input_shape=(None, None, 3), name="input_new")

    new_model.add(input_layer)

    for layer in model.layers:

        if "Flatten" in str(layer):
            flattened_ipt = True
            f_dim = layer.input_shape

        elif "Dense" in str(layer):

            input_shape = layer.input_shape
            output_dim =  layer.get_weights()[1].shape[0]
            W,b = layer.get_weights()

            if flattened_ipt:
                shape = (f_dim[1],f_dim[2],f_dim[3],output_dim)
                new_W = W.reshape(shape)
                new_layer = Convolution2D(output_dim,
                                          (f_dim[1],f_dim[2]),
                                          strides=(1,1),
                                          activation=layer.activation,
                                          padding='valid',
                                          weights=[new_W,b])
                flattened_ipt = False

            else:
                shape = (1,1,input_shape[1],output_dim)
                new_W = W.reshape(shape)
                new_layer = Convolution2D(output_dim,
                                          (1,1),
                                          strides=(1,1),
                                          activation=layer.activation,
                                          padding='valid',
                                          weights=[new_W,b])


        else:
            new_layer = layer

        new_model.add(new_layer)

    return new_model

你可以测试这样的功能:

model = keras.applications.vgg16.VGG16()
new_model = to_fully_conv(model)

答案 1 :(得分:2)

一个。无需进行复杂的旋转。只是重塑正在运作

湾使用get_weights()和init new layer

遍历model.layers,使用set_weights创建具有config的相同图层并加载权重,如下所示。

以下伪代码对我有用。 (Keras 2.0)

伪代码:

# find input dimensions of Flatten layer
f_dim =  flatten_layer.input_shape

# Creating new Conv layer and putting dense layers weights 
m_layer = model.get_layer(layer.name)
input_shape = m_layer.input_shape
output_dim =  m_layer.get_weights()[1].shape[0]
W,b = layer.get_weights()
if first dense layer :
    shape = (f_dim[1],f_dim[2],f_dim[3],output_dim)
    new_W = W.reshape(shape)
    new_layer = Convolution2D(output_dim,(f_dim[1],f_dim[2]),strides=(1,1),activation='relu',padding='valid',weights=[new_W,b])

else: (not first dense layer)
    shape = (1,1,input_shape[1],output_dim)
    new_W = W.reshape(shape)
    new_layer = Convolution2D(output_dim,(1,1),strides=(1,1),activation='relu',padding='valid',weights=[new_W,b])