如何在不使用Sequential()的情况下在Keras中堆叠图层?

时间:2018-06-26 10:48:07

标签: python tensorflow keras

如果我有一个keras层L,并且想在keras模型中堆叠该层的N个版本(具有不同的权重),那么最好的方法是什么?请注意,此处N很大,并且受超参数控制。如果N小,那么这不是问题(我们可以手动重复一行N次)。因此,我们假设N> 10。

如果该图层只有一个输入和一个输出,则可以执行以下操作:

m = Sequential()
for i in range(N):
    m.add(L)

但是,如果我的图层实际接受多个输入,则此操作将无效。例如,如果我的图层的形式为z = L(x,y),并且我希望我的模型可以这样做:

x_1 = L(x_0, y)
x_2 = L(x_1, y)
...
x_N = L(x_N-1, y)

然后,Sequential无法完成这项工作。我认为我可以对keras模型进行子类化,但是我不知道将N层放入类中的最干净方法是什么。我可以使用一个列表,例如:

class MyModel(Model):
    def __init__(self):
        super(MyModel, self).__init__()
        self.layers = []
        for i in range(N):
            self.layers.append(L)
    def call(self, inputs):
        x = inputs[0]
        y = inputs[1]
        for i in range(N):
            x = self.layers[i](x, y)
        return x

但这并不理想,因为keras无法识别这些层(似乎不认为层列表为“ checkpointables”)。例如,MyModel.variables将为空,而MyModel.Save()将不保存任何内容。

我也尝试使用功能性API定义模型,但在我的情况下也无法使用。实际上,如果我们这样做

def MyModel():
    input = Input(shape=...)
    output = SomeLayer(input)
    return Model(inputs=input, outputs=output)

如果SomeLayer本身是自定义模型,它将无法运行(它会引发NotImplementedError)。

有什么建议吗?

2 个答案:

答案 0 :(得分:3)

不确定我的问题是否正确,但我想您可以使用功能性API和concatenateadd层,如Keras应用程序中所示,例如{{3 }}或ResNet50建立“非顺序”网络。

更新

在我的一个项目中,我正在使用类似的东西。我有一个自定义层(在我的Keras版本中未实现,因此我只是手动将代码“反向移植”到笔记本中)。

class LeakyReLU(Layer):
    """Leaky version of a Rectified Linear Unit backported from newer Keras 
    version."""

    def __init__(self, alpha=0.3, **kwargs):
        super(LeakyReLU, self).__init__(**kwargs)
        self.supports_masking = True
        self.alpha = K.cast_to_floatx(alpha)

    def call(self, inputs):
        return tf.maximum(self.alpha * inputs, inputs)

    def get_config(self):
        config = {'alpha': float(self.alpha)}
        base_config = super(LeakyReLU, self).get_config()
        return dict(list(base_config.items()) + list(config.items()))

    def compute_output_shape(self, input_shape):
        return input_shape

然后,模型:

def create_model(input_shape, output_size, alpha=0.05, reg=0.001):
    inputs = Input(shape=input_shape)

    x = Conv2D(16, (3, 3), padding='valid', strides=(1, 1), 
               kernel_regularizer=l2(reg), kernel_constraint=maxnorm(3),
               activation=None)(inputs)
    x = BatchNormalization()(x)
    x = LeakyReLU(alpha=alpha)(x)
    x = MaxPooling2D(pool_size=(2, 2))(x)

    x = Conv2D(32, (3, 3), padding='valid', strides=(1, 1),
               kernel_regularizer=l2(reg), kernel_constraint=maxnorm(3),
               activation=None)(x)
    x = BatchNormalization()(x)
    x = LeakyReLU(alpha=alpha)(x)
    x = MaxPooling2D(pool_size=(2, 2))(x)

    x = Conv2D(64, (3, 3), padding='valid', strides=(1, 1),
               kernel_regularizer=l2(reg), kernel_constraint=maxnorm(3),
               activation=None)(x)
    x = BatchNormalization()(x)
    x = LeakyReLU(alpha=alpha)(x)
    x = MaxPooling2D(pool_size=(2, 2))(x)

    x = Conv2D(128, (3, 3), padding='valid', strides=(1, 1),
               kernel_regularizer=l2(reg), kernel_constraint=maxnorm(3),
               activation=None)(x)
    x = BatchNormalization()(x)
    x = LeakyReLU(alpha=alpha)(x)
    x = MaxPooling2D(pool_size=(2, 2))(x)

    x = Conv2D(256, (3, 3), padding='valid', strides=(1, 1),
               kernel_regularizer=l2(reg), kernel_constraint=maxnorm(3),
               activation=None)(x)
    x = BatchNormalization()(x)
    x = LeakyReLU(alpha=alpha)(x)
    x = MaxPooling2D(pool_size=(2, 2))(x)

    x = Flatten()(x)
    x = Dense(500, activation='relu', kernel_regularizer=l2(reg))(x)
    x = Dense(500, activation='relu', kernel_regularizer=l2(reg))(x)
    x = Dense(500, activation='relu', kernel_regularizer=l2(reg))(x)
    x = Dense(500, activation='relu', kernel_regularizer=l2(reg))(x)
    x = Dense(500, activation='relu', kernel_regularizer=l2(reg))(x)
    x = Dense(500, activation='relu', kernel_regularizer=l2(reg))(x)
    x = Dense(output_size, activation='linear', kernel_regularizer=l2(reg))(x)

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

    return model

最后,一个自定义指标:

def root_mean_squared_error(y_true, y_pred):
    return K.sqrt(K.mean(K.square(y_pred - y_true), axis=-1))

我使用以下代码片段创建和编译模型:

model = create_model(input_shape=X.shape[1:], output_size=y.shape[1])
model.compile(loss=root_mean_squared_error, optimizer='adamax')

和往常一样,我使用检查点回调来保存模型。要加载模型,您还需要将自定义图层类和指标传递到load_model函数中:

def load_custom_model(path):
    return load_model(path, custom_objects={
        'LeakyReLU': LeakyReLU,
        'root_mean_squared_error': root_mean_squared_error
    })

有帮助吗?

答案 1 :(得分:0)

如果我正确理解了您的问题,则可以在构建模型时简单地通过使用for循环来解决此问题。我不确定是否需要任何特殊的图层,所以我假设您仅在此处使用Dense:

def MyModel():
    input = Input(shape=...)
    x = input
    for i in range(N):
        x = Dense(number_of_nodes, name='dense %i' %i)(x)
        // Or some custom layers
    output = Dense(number_of_output)(x)

    return Model(inputs=input, outputs=output)