Вопрос или проблема
Я создаю пользовательский уровень Keras FConv2D() и добавляю вес в его функции build() с помощью метода add_weight(), как предложено в официальном руководстве Keras по созданию пользовательских уровней.
def build(self, input_shape):
shape = tf.TensorShape(input_shape).as_list()
h = shape[1]
w = shape[2]
in_channels = shape[3]
self.kernel = self.add_weight(
shape=(h,w,in_channels,self.num_outputs),
initializer="random_normal",
trainable=True,
)
super(FConv2D, self).build(input_shape)
Но когда я печатаю сводку однослойной модели, содержащей только этот уровень, количество параметров в этом уровне оказывается равным 0.
Model: "model_4"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_5 (InputLayer) [(None, 7, 7, 2)] 0
_________________________________________________________________
f_conv2d_4 (FConv2D) (None, 7, 7, 64) 0
=================================================================
Total params: 0
Trainable params: 0
Non-trainable params: 0
Я пробовал тот же метод в руководстве по пользовательским уровням на официальном сайте, но их метод add_weight, похоже, работает правильно –
class SimpleDense(tf.keras.layers.Layer):
def __init__(self, units=32):
super(SimpleDense, self).__init__()
self.units = units
def build(self, input_shape): # Создает состояние уровня (веса)
self.w = self.add_weight(shape=(input_shape[-1], self.units),
initializer="random_normal",
trainable=True)
self.b = self.add_weight(shape=(self.units,),
initializer="random_normal",
trainable=True)
def call(self, inputs): # Определяет вычисление от входов к выходам
return tf.matmul(inputs, self.w) + self.b
input = tf.keras.layers.Input(shape = (1000,1))
output = SimpleDense(100)(input)
model = tf.keras.Model(inputs = [input], outputs = [output])
model.summary()
Model: "model_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_2 (InputLayer) [(None, 1000, 1)] 0
_________________________________________________________________
simple_dense_3 (SimpleDense) (None, 1000, 100) 200
=================================================================
Total params: 200
Trainable params: 200
Non-trainable params: 0
Может ли кто-нибудь сказать, почему веса, добавленные в пользовательском уровне, не отображаются в параметрах модели?
Я столкнулся с такой же проблемой, причиной которой может быть то, что вы использовали def __call__
в своем пользовательском уровне. Как это сделано в официальном образце, попробуйте использовать def call
вместо этого.
Столкнулся с такой же проблемой. Как упомянуто в документации по созданию пользовательских уровней, метод build
используется для ленивой инициализации весов и вызывается только во время первого вызова метода call
. Инициализация весов в методе __init__()
исправила проблему.
Ответ или решение
Проблема, с которой вы столкнулись, связана с тем, как Keras обрабатывает добавление весов в пользовательских слоях. При использовании метода add_weight()
в методе build()
вес будет инициализирован только при первом вызове метода call()
. Таким образом, если метод call()
не вызывается, Keras не знает о существовании весов, и, соответственно, они не отображаются в сводке модели.
Для решения вашей проблемы, убедитесь, что:
-
Вы вызываете метод
call()
— При создании модели обязательно используйте ваш пользовательский слой в вычислениях, чтобы Keras вызвал методcall()
и инициализировал параметры. Например:input = tf.keras.layers.Input(shape=(7, 7, 2)) # Учтите, что входная форма правильная output = FConv2D(num_outputs=64)(input) # Здесь вызывается метод call, что инициализирует веса model = tf.keras.Model(inputs=[input], outputs=[output]) model.summary()
-
Почему ваш слой может не иметь весов: Если вы используете метод
__call__()
вместоcall()
, это также может привести к проблемам. Keras ожидает, что ваш пользовательский слой будет иметь методcall()
. Убедитесь, что вы используете именноcall()
для определения вычислений, а не__call__()
. -
Правильная инициализация весов: Метод
build()
служит для ленивой инициализации весов. Таким образом, если вы вызываете свой слой 1 раз, и он не создает весов из-за пропуска вызоваcall()
, вы не получите параметров. Убедитесь, что ваши функции определения параметров правильно связаны с методом, который создаёт их.
В вашем случае проверьте, правильно ли вы определили call()
и используется ли он в модели. Если же build()
не вызывается, это также может означать, что вы не используете слой должным образом.
Вот пример правильной реализации вашего пользовательского слоя:
import tensorflow as tf
class FConv2D(tf.keras.layers.Layer):
def __init__(self, num_outputs=64):
super(FConv2D, self).__init__()
self.num_outputs = num_outputs
def build(self, input_shape):
h = input_shape[1]
w = input_shape[2]
in_channels = input_shape[3]
self.kernel = self.add_weight(
shape=(h, w, in_channels, self.num_outputs),
initializer="random_normal",
trainable=True,
)
super(FConv2D, self).build(input_shape)
def call(self, inputs):
# Здесь необходимо указать логику использования весов
return tf.nn.conv2d(inputs, self.kernel, strides=[1, 1, 1, 1], padding='SAME') # Пример операции
# Создание модели
input = tf.keras.layers.Input(shape=(7, 7, 2))
output = FConv2D(num_outputs=64)(input)
model = tf.keras.Model(inputs=[input], outputs=[output])
model.summary()
Если вы выполните указанные шаги, инициализация весов должна заработать, и они будут правильно отображаться в сводке модели.