Ответ 1
Насколько мне известно, это невозможно сделать с помощью общего "уровня API" использования Keras. Однако, если вы копаете немного глубже, есть некоторые (уродливые) способы распределения весов.
Прежде всего, веса слоев Conv2D
создаются внутри функции build()
, вызывая add_weight()
:
self.kernel = self.add_weight(shape=kernel_shape,
initializer=self.kernel_initializer,
name='kernel',
regularizer=self.kernel_regularizer,
constraint=self.kernel_constraint)
Для вашего предоставленного использования (т.е. по умолчанию trainable
/constraint
/regularizer
/initializer
), add_weight()
ничего не делает, но добавляет весовые переменные в _trainable_weights
:
weight = K.variable(initializer(shape), dtype=dtype, name=name)
...
self._trainable_weights.append(weight)
Наконец, поскольку build()
вызывается только внутри __call__()
, если слой не был создан, общие веса между слоями могут быть созданы:
- Вызовите
conv1.build()
, чтобы инициализировать переменныеconv1.kernel
иconv1.bias
для совместного использования. - Вызовите
conv2.build()
для инициализации слоя. - Замените
conv2.kernel
иconv2.bias
наconv1.kernel
иconv1.bias
. - Удалите
conv2.kernel
иconv2.bias
изconv2._trainable_weights
. - Добавить
conv1.kernel
иconv1.bias
вconv2._trainable_weights
. - Завершить определение модели. Здесь
conv2.__call__()
будем называть; однако, посколькуconv2
уже построен, веса не будут повторно инициализированы.
Может оказаться полезным следующий фрагмент кода:
def create_shared_weights(conv1, conv2, input_shape):
with K.name_scope(conv1.name):
conv1.build(input_shape)
with K.name_scope(conv2.name):
conv2.build(input_shape)
conv2.kernel = conv1.kernel
conv2.bias = conv1.bias
conv2._trainable_weights = []
conv2._trainable_weights.append(conv2.kernel)
conv2._trainable_weights.append(conv2.bias)
# check if weights are successfully shared
input_img = Input(shape=(299, 299, 3))
conv1 = Conv2D(64, 3, padding='same')
conv2 = Conv2D(64, 3, padding='valid')
create_shared_weights(conv1, conv2, input_img._keras_shape)
print(conv2.weights == conv1.weights) # True
# check if weights are equal after model fitting
left = conv1(input_img)
right = conv2(input_img)
left = GlobalAveragePooling2D()(left)
right = GlobalAveragePooling2D()(right)
merged = concatenate([left, right])
output = Dense(1)(merged)
model = Model(input_img, output)
model.compile(loss='binary_crossentropy', optimizer='adam')
X = np.random.rand(5, 299, 299, 3)
Y = np.random.randint(2, size=5)
model.fit(X, Y)
print([np.all(w1 == w2) for w1, w2 in zip(conv1.get_weights(), conv2.get_weights())]) # [True, True]
Одним из недостатков этого хакерского распределения веса является то, что весы не останутся разделенными после сохранения/загрузки модели. Это не повлияет на предсказание, но может быть проблематично, если вы хотите загрузить обученную модель для дальнейшей тонкой настройки.