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

How to convert a dense layer to an equivalent convolutional layer in Keras?

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

以Keras自带的VGG16网络为例,将最后一个MaxPooling2D()的7x7x512输出拉平后进入Dense(4096)层。在这种情况下,Dense(4096) 将替换为 7x7x4096 卷积。

我的真实网络略有不同,有一个 GlobalAveragePooling2D() 层而不是 MaxPooling2D() 和 Flatten()。 GlobalAveragePooling2D() 的输出是一个二维张量,不需要额外地展平它,所以包括第一层在内的所有密集层都将被替换为 1x1 卷积。

我看过这个问题: 如果不完全相同,它看起来非常相似。问题是我无法使建议的解决方案起作用,因为 (a) 我使用 TensorFlow 作为后端,所以权重 rearrangement/filter "rotation" 不正确,并且 (b) 我无法弄清楚如何加载权重。使用 model.load_weights(by_name=True) 将旧的权重文件加载到新网络中是行不通的,因为名称不匹配(即使它们的尺寸不同)。

在使用TensorFlow的时候应该怎么重排?

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

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

b。使用 get_weights() 并初始化新层

遍历 model.layers,使用 set_weights 或如下所示创建具有配置和负载权重的相同层。

以下一段伪代码对我有用。 (凯拉斯 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])
            

根据 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)