使用 Model() 在 Keras 中使用绑定权重的自动编码器

Autoencoder with tied weights in Keras using Model()

我正在尝试设置一个具有固定权重的自动编码器。我正在使用 Python 3.6.10、Tensorflow 1.15.0 和 Keras 2.2.4-tf。

有一个非常好的解决方案 使用 Sequential() 构建模型。

random.seed(1)

class DenseTied(keras.layers.Layer):
    def __init__(self, units,
                 activation=None,
                 use_bias=True,
                 kernel_initializer='glorot_uniform',
                 bias_initializer='zeros',
                 kernel_regularizer=None,
                 bias_regularizer=None,
                 activity_regularizer=None,
                 kernel_constraint=None,
                 bias_constraint=None,
                 tied_to=None,
                 **kwargs):
        self.tied_to = tied_to
        if 'input_shape' not in kwargs and 'input_dim' in kwargs:
            kwargs['input_shape'] = (kwargs.pop('input_dim'),)
        super().__init__(**kwargs)
        self.units = units
        self.activation = activations.get(activation)
        self.use_bias = use_bias
        self.kernel_initializer = initializers.get(kernel_initializer)
        self.bias_initializer = initializers.get(bias_initializer)
        self.kernel_regularizer = regularizers.get(kernel_regularizer)
        self.bias_regularizer = regularizers.get(bias_regularizer)
        self.activity_regularizer = regularizers.get(activity_regularizer)
        self.kernel_constraint = constraints.get(kernel_constraint)
        self.bias_constraint = constraints.get(bias_constraint)
        self.input_spec = InputSpec(min_ndim=2)
        self.supports_masking = True

    def build(self, input_shape):
        assert len(input_shape) >= 2
        input_dim = input_shape[-1]

        if self.tied_to is not None:
            self.kernel = K.transpose(self.tied_to.kernel)
            self._non_trainable_weights.append(self.kernel)
        else:
            self.kernel = self.add_weight(shape=(input_dim, self.units),
                                          initializer=self.kernel_initializer,
                                          name='kernel',
                                          regularizer=self.kernel_regularizer,
                                          constraint=self.kernel_constraint)
        if self.use_bias:
            self.bias = self.add_weight(shape=(self.units,),
                                        initializer=self.bias_initializer,
                                        name='bias',
                                        regularizer=self.bias_regularizer,
                                        constraint=self.bias_constraint)
        else:
            self.bias = None

        self.built = True

    
    def compute_output_shape(self, input_shape):
        assert input_shape and len(input_shape) >= 2
        assert input_shape[-1] == self.units
        output_shape = list(input_shape)
        output_shape[-1] = self.units
        return tuple(output_shape)

    def call(self, inputs):
        output = K.dot(inputs, self.kernel)
        if self.use_bias:
            output = K.bias_add(output, self.bias, data_format='channels_last')
        if self.activation is not None:
            output = self.activation(output)
        return output

定义模型如下:

# generate data
x = np.random.rand(100, 4) 

# model architecture
original_dim = 4
latent_dim = 2

input_layer = Input(shape=(original_dim,))
encoded1 = Dense(latent_dim, activation="sigmoid", use_bias=True)  
decoded1 = DenseTied(original_dim, activation="sigmoid", tied_to=encoded1, use_bias=False)

autoencoder = Sequential()
autoencoder.add(input_layer)
autoencoder.add(encoded1)
autoencoder.add(decoded1)

autoencoder.compile(optimizer="adam", loss="binary_crossentropy")
print(autoencoder.summary())

autoencoder.fit(x, x, epochs=3)

print(autoencoder.layers[0].get_weights()[0])
print(autoencoder.layers[-1].get_weights()[-1])

这非常适用于可以使用 Sequential() class 定义的简单模型。不幸的是,我的模型要复杂得多,所以我需要使用 Model() 而不是 Sequential()。我需要像这样定义我的模型并保留绑定权重:

# generate data
x = np.random.rand(100, 4) 
original_dim = 4
latent_dim = 2

input_layer = Input(shape=(original_dim,))
encoded1 = Dense(latent_dim, activation="sigmoid", use_bias=True)(input_layer)
decoded1 = DenseTied(original_dim, activation="sigmoid", tied_to=encoded1, use_bias=False)(encoded1)

autoencoder2 = Model(input_layer, decoded1)
autoencoder2.compile(optimizer="adam", loss="binary_crossentropy")

print(autoencoder2.summary())

autoencoder2.fit(x, x, epochs=3)

print(autoencoder2.layers[0].get_weights()[0])
print(autoencoder2.layers[-1].get_weights()[-1])

上面的代码在 autoencoder2.compile() 步骤中断。

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-56-0851be462afd> in <module>
      6 input_layer = Input(shape=(original_dim,))
      7 encoded1 = Dense(latent_dim, activation="sigmoid", use_bias=True)(input_layer)
----> 8 decoded1 = DenseTied(original_dim, activation="sigmoid", tied_to=encoded1, use_bias=False)(encoded1)
      9 
     10 autoencoder2 = Model(input_layer, decoded1)

/miniconda3/envs/nnet_entropy/lib/python3.6/site-packages/tensorflow_core/python/keras/engine/base_layer.py in __call__(self, inputs, *args, **kwargs)
    822           # Build layer if applicable (if the `build` method has been
    823           # overridden).
--> 824           self._maybe_build(inputs)
    825           cast_inputs = self._maybe_cast_inputs(inputs)
    826 

/miniconda3/envs/nnet_entropy/lib/python3.6/site-packages/tensorflow_core/python/keras/engine/base_layer.py in _maybe_build(self, inputs)
   2144         # operations.
   2145         with tf_utils.maybe_init_scope(self):
-> 2146           self.build(input_shapes)
   2147       # We must set self.built since user defined build functions are not
   2148       # constrained to set self.built.

<ipython-input-15-925349097567> in build(self, input_shape)
     36 
     37         if self.tied_to is not None:
---> 38             self.kernel = K.transpose(self.tied_to.kernel)
     39             self._non_trainable_weights.append(self.kernel)
     40         else:

AttributeError: 'Tensor' object has no attribute 'kernel'

我试过在 Model() 下定义我的模型的一部分,并将其传递给在 Sequential() 下定义的模型的其余部分(正如所做的那样 here),但它似乎没有是个好方法。有什么办法让它发挥作用吗?如果有任何建议,我将不胜感激。

您没有正确使用自定义层,您需要将层实例传递给 DenseTied tied_to 参数,而不是通过将输入传递给该层获得的张量,如下所示:

input_layer = Input(shape=(original_dim,))
encoded1_layer = Dense(latent_dim, activation="sigmoid", use_bias=True)
encoded1 = encoded1_layer(input_layer)
decoded1 = DenseTied(original_dim, activation="sigmoid", tied_to=encoded1_layer, use_bias=False)(encoded1)