【发布时间】:2025-09-01 10:20:02
【问题描述】:
我正在尝试在 Keras 中创建一个自定义的 Dense 层,以在自动编码器中绑定权重。我已经尝试在卷积层here 中执行此操作的示例,但似乎某些步骤不适用于 Dense 层(另外,代码来自两年多前)。
通过绑定权重,我希望解码层使用编码层的转置权重矩阵。 this article(第 5 页)也采用了这种方法。以下是文章的相关引述:
这里,我们选择编码和解码激活函数都是 sigmoid 函数,并且只考虑 绑定权重情况,其中 W ′ = WT (其中 WT 是个 W 的转置)作为大多数现有的深度学习方法 做。
在上面的引用中,W是编码层中的权重矩阵,W'(等于W的转置)是解码层的权重矩阵。
dense 层我没有改变太多。我在构造函数中添加了一个tied_to 参数,它允许您传递要绑定到的层。唯一的其他变化是build 函数,sn-p 如下:
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.input_spec = InputSpec(min_ndim=2, axes={-1: input_dim})
self.built = True
下面是__init__ 方法,这里唯一的变化是添加了tied_to 参数。
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):
if 'input_shape' not in kwargs and 'input_dim' in kwargs:
kwargs['input_shape'] = (kwargs.pop('input_dim'),)
super(Dense, self).__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
self.tied_to = tied_to
call 函数未编辑,但在下面供参考。
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
在上面,我添加了一个条件来检查是否设置了tied_to 参数,如果是,则将层的内核设置为tied_to 层的内核的转置。
下面是用于实例化模型的代码。它是使用 Keras 的顺序 API 完成的,DenseTied 是我的自定义层。
# encoder
#
encoded1 = Dense(2, activation="sigmoid")
decoded1 = DenseTied(4, activation="sigmoid", tied_to=encoded1)
# autoencoder
#
autoencoder = Sequential()
autoencoder.add(encoded1)
autoencoder.add(decoded1)
训练模型后,下面是模型摘要和权重。
autoencoder.summary()
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_7 (Dense) (None, 2) 10
_________________________________________________________________
dense_tied_7 (DenseTied) (None, 4) 12
=================================================================
Total params: 22
Trainable params: 14
Non-trainable params: 8
________________________________________________________________
autoencoder.layers[0].get_weights()[0]
array([[-2.122982 , 0.43029135],
[-2.1772149 , 0.16689162],
[-1.0465667 , 0.9828905 ],
[-0.6830663 , 0.0512633 ]], dtype=float32)
autoencoder.layers[-1].get_weights()[1]
array([[-0.6521988 , -0.7131109 , 0.14814234, 0.26533198],
[ 0.04387903, -0.22077179, 0.517225 , -0.21583867]],
dtype=float32)
如您所见,autoencoder.get_weights() 报告的权重似乎没有绑定。
所以在展示了我的方法之后,我的问题是,这是在 Dense Keras 层中绑定权重的有效方法吗?我能够运行代码,并且它目前正在训练中。损失函数似乎也在合理地减少。我担心的是,这只会在构建模型时使它们相等,但实际上不会将它们绑定。我希望后端 transpose 函数通过引擎盖下的引用将它们联系起来,但我确信我错过了一些东西。
【问题讨论】:
-
“tie”是指有两个具有完全相同权重的 Dense 层吗?如果是这种情况,那么为什么不使用单个 Dense 层并将其应用到模型的不同部分?
-
抱歉,我已经更新了这个问题,以说明我所说的“捆绑”权重是什么意思。不幸的是,它并不像使用同一层那么简单,因为必须转置权重矩阵。
-
我无法测试它,但我非常有信心您的方法是正确的(尽管我不确定
self._trainable_weights.append(self.kernel)是否是绝对必要的,因为权重self.tied_to.kernel在理论上已经可以训练)。我建议您在训练后检查重量,并确保它们相同。您还可以使用 Tensorboard 可视化计算图。 -
@JamesMchugh 我认为你根本不应该使用
self._trainable_weights.append(self.kernel),因为从自定义密集层的角度来看,这些权重是不可训练的。要么完全删除该行,要么改用self._non_trainable_weights.append(self.kernel),以便您可以独立地从自定义密集层访问权重(即使用get_weights()方法)。 -
对于任何感兴趣的人来说,问题是通过使用
k.variable(k.transpose(self.kernel)),我打破了平局。我不得不改用k.transpose(self.kernel)。但是,这在尝试使用autoencoder.load_weights(file)时确实会导致一些问题,因为self.kernel是张量并且没有assign方法。
标签: python keras autoencoder