Я пытаюсь создать пользовательский плотный слой в Керасе, чтобы связать веса в автоэнкодере.Я попытался следовать примеру для выполнения этого в сверточных слоях здесь , но казалось, что некоторые из шагов неприменимы для плотного слоя (также, код более двух лет назад).
Связывая веса, я хочу, чтобы слой декодирования использовал транспонированную матрицу весов уровня кодирования.Этот подход также принят в этой статье (стр. 5).Ниже приведена соответствующая цитата из статьи:
Здесь мы выбираем функцию активации кодирования и декодирования как сигмовидную функцию и рассматриваем только случай связанных весов, в котором W ′ = W T (где W T - транспонирование W), как это делают большинство существующих методов глубокого обучения.
В приведенной выше цитате W матрица весов в слое кодирования и W ' (равная транспонированию W ) - это матрица весов в слое декодирования.
Я тоже не изменилсямного в плотном слое.Я добавил параметр tied_to
в конструктор, который позволяет вам передать слой, к которому вы хотите привязать его.Единственное другое изменение касалось функции build
, фрагмент которой приведен ниже:
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
.
Ниже приведен код, используемый для создания экземпляра модели.Это делается с помощью последовательного API Keras, а 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
связывает их через ссылки под капотом, но я уверен, что что-то упустил.