В этом другом вопросе было показано, что можно повторно использовать плотный слой на разных входных слоях, чтобы обеспечить разделение веса. Теперь мне интересно, как распространить этот принцип на целый блок слоев; моя попытка заключается в следующем:
from keras.layers import Input, Dense, BatchNormalization, PReLU
from keras.initializers import Constant
from keras import backend as K
def embedding_block(dim):
dense = Dense(dim, activation=None, kernel_initializer='glorot_normal')
activ = PReLU(alpha_initializer=Constant(value=0.25))(dense)
bnorm = BatchNormalization()(activ)
return bnorm
def embedding_stack():
return embedding_block(32)(embedding_block(16)(embedding_block(8)))
common_embedding = embedding_stack()
Здесь я создаю «встраиваемые блоки» с одним плотным слоем переменной размерности, которые я пытаюсь объединить в «встраиваемый стек», состоящий из блоков с увеличивающимся размером. Затем я хотел бы применить это «общее вложение» к нескольким входным слоям (все они имеют одинаковую форму), чтобы веса были общими.
Приведенный выше код не работает с
<ipython-input-33-835f06ed7bbb> in embedding_block(dim)
1 def embedding_block(dim):
2 dense = Dense(dim, activation=None, kernel_initializer='glorot_normal')
----> 3 activ = PReLU(alpha_initializer=Constant(value=0.25))(dense)
4 bnorm = BatchNormalization()(activ)
5 return bnorm
/localenv/lib/python3.8/site-packages/tensorflow/python/keras/engine/base_layer.py in __call__(self, *args, **kwargs)
980 with ops.name_scope_v2(name_scope):
981 if not self.built:
--> 982 self._maybe_build(inputs)
983
984 with ops.enable_auto_cast_variables(self._compute_dtype_object):
/localenv/lib/python3.8/site-packages/tensorflow/python/keras/engine/base_layer.py in _maybe_build(self, inputs)
2641 # operations.
2642 with tf_utils.maybe_init_scope(self):
-> 2643 self.build(input_shapes) # pylint:disable=not-callable
2644 # We must set also ensure that the layer is marked as built, and the build
2645 # shape is stored since user defined build functions may not be calling
/localenv/lib/python3.8/site-packages/tensorflow/python/keras/utils/tf_utils.py in wrapper(instance, input_shape)
321 if input_shape is not None:
322 input_shape = convert_shapes(input_shape, to_tuples=True)
--> 323 output_shape = fn(instance, input_shape)
324 # Return shapes from `fn` as TensorShapes.
325 if output_shape is not None:
/localenv/lib/python3.8/site-packages/tensorflow/python/keras/layers/advanced_activations.py in build(self, input_shape)
138 @tf_utils.shape_type_conversion
139 def build(self, input_shape):
--> 140 param_shape = list(input_shape[1:])
141 if self.shared_axes is not None:
142 for i in self.shared_axes:
TypeError: 'NoneType' object is not subscriptable
Каков правильный способ сделать это? Спасибо!
Вы хотите построить общий блок и использовать его на разных этапах вашей модели, вызывая его с разными параметрами (например, kernel_size
, stride
, padding
и т. д.)?
Привет @SebastianR. ах, я надеялся сделать это без переопределения чего-либо... :/
Привет @M.Innat, функция embedding_stack() здесь только для удобства - я хочу определить один такой стек для модели и передать ему разные входные слои, чтобы веса были общими. Затем я могу собрать вывод из внедрения каждого ввода, объединить их и что-то с этим сделать. Но сам стек встраивания обучается на всех входных данных вместе с одним набором весов. Имеет ли это смысл?
Я думаю, что не понимаю его полностью. Тем не менее, не могли бы вы добавить диаграмму того, что вы хотите? Это было бы намного понятнее. И вы можете проверить этот блог, я написал его несколько дней назад. Пожалуйста, посмотрите на строительный блок небольшой начальной модели, это то, что вам нужно?
@M.Innat хорошо, вот пример: я хочу, чтобы общий блок BatchNorm(PReLU(Dense(8)(input))) применялся к двум головкам ввода одинакового размера=4. Затем я могу объединить два выходных тензора с размерностью 8 каждый и передать их остальной части сети (например, слою Dense(16)). Это легко сделать, если я определяю две такие последовательности BatchNorm(PReLU(Dense(8)(input))), по одной для каждого входа (в основном это просто объединение двух отдельных моделей). Что я хочу сделать, так это распределить веса между этими двумя ветвями. Это яснее?
Вам необходимо отделить создание экземпляров слоев от создания модели.
Вот простой метод с использованием цикла for:
from tensorflow.keras import layers, initializers, Model
def embedding_block(dim):
dense = layers.Dense(dim, activation=None, kernel_initializer='glorot_normal')
activ = layers.PReLU(alpha_initializer=initializers.Constant(value=0.25))
bnorm = layers.BatchNormalization()
return [dense, activ, bnorm]
stack = embedding_block(8) + embedding_block(16) + embedding_block(32)
inp1 = layers.Input((5,))
inp2 = layers.Input((5,))
x,y = inp1,inp2
for layer in stack:
x = layer(x)
y = layer(y)
concat_layer = layers.Concatenate()([x,y])
pred = layers.Dense(1, activation = "sigmoid")(concat_layer)
model = Model(inputs = [inp1, inp2], outputs=pred)
Сначала мы создаем каждый слой, а затем итерируем их, используя функциональный API для создания модели.
Вы можете проанализировать сеть в netron, чтобы убедиться, что веса действительно разделены:
Спасибо! именно то, что мне было нужно :) а про нетрон я и не знал, он реально крут!
Вы можете расширить
tf.keras.Layer
и определить «подслои» внутри. Затем определите переменную общих весов в вашем расширенном слое и замените веса ваших «подслоев».