#tensorflow #keras
#tensorflow #keras
Вопрос:
Я хотел бы создать слой (с помощью tensorflow.keras), который содержит как обучаемые, так и необучаемые веса. Я попытался сделать это, создав подкласс keras.слои.Слой, как в этом примере:
class MySum(keras.layers.Layer):
def __init__(self, units=32, **kwargs):
super(MySum, self).__init__(**kwargs)
self.units = units
def build(self, input_shape):
n_input = input_shape[-1] # nb of input elements
n_output = self.units # nb of layer neurons
n_input_div_2 = input_shape[-1] // 2
# 1. add the trainable weights
self.w = self.add_weight(shape=(n_input_div_2, self.units),
initializer=tf.ones_initializer(),
trainable=True)
# 2. add the non trainable weights
self.w = self.add_weight(shape=(input_shape[-1]-n_input_div_2, self.units),
initializer=tf.keras.initializers.Constant(value=3),
trainable=False)
def call(self, inputs):
return tf.matmul(inputs, self.w)
К сожалению, при этом все веса не поддаются обучению. Если я добавлю сначала необучаемые веса, тогда все веса будут обучаемыми (кажется, что флаг обучаемости устанавливается в соответствии с последними добавленными весами).
Чего мне здесь не хватает?
РЕДАКТИРОВАТЬ: я пытался использовать разные имена, как предложил доктор Снупи в функции сборки:
# 1. add the trainable weights
w1 = self.add_weight(shape=(n_input_div_2, self.units),
initializer=tf.ones_initializer(),
trainable=True)
# 2. add the non trainable weights
w2 = self.add_weight(shape=(input_shape[-1]-n_input_div_2, self.units),
initializer=tf.keras.initializers.Constant(value=3),
trainable=False)
self.w = tf.concat([w1, w2], 0)
Но, когда я пытаюсь использовать свой слой таким образом:
custom = customLayer.MySum(1, name='somme')
my_input = keras.Input(shape=(2,), name="input")
my_output = custom(my_input)
print(custom.get_weights())
Я получаю с помощью функции get_weights():
tf.Tensor(
[[1.]
[3.]], shape=(2, 1), dtype=float32)
[array([[1.],
[1.]], dtype=float32), array([[1.]], dtype=float32), array([[3.]], dtype=float32)]
-
Где [[1.],[1.]] откуда взялся массив? (Я хотел бы иметь только [[1.][3.]] массив)
-
У меня много предупреждений при обучении моей модели: «ПРЕДУПРЕЖДЕНИЕ: тензорный поток: градиенты не существуют для переменных [‘somme / Variable:0’, ‘somme / Variable: 0’] при минимизации потерь».Как keras связывает мои собственные веса (self.w) с весами, возвращаемыми get_weights()?
Примечание: когда я создаю настраиваемые слои без смешивания обучаемых и необучаемых весов, у меня нет этих проблем.
Комментарии:
1. Вы используете одну и ту же переменную из обоих весов, это перезаписывает ранее определенные веса, просто используйте разные имена переменных
Ответ №1:
Как указал доктор Снупи, ваше первое решение перезаписывает ранее определенный вес, используя то же имя переменной.
Что касается того, почему ваше второе решение тоже не работает, это потому, что после вызова tf.concat
ваших двух tf.Variable
w1
и w2
, градиент e исчезает. Это известная ошибка в Tensorflow, вы можете найти проблему на github здесь: градиенты не существуют для переменных после tf.concat() . #37726
Минимальный воспроизводимый пример
Давайте проведем некоторый эксперимент, используя tf.GradientTape
для вычисления градиента :
w1 = tf.Variable([1.0])
w2 = tf.Variable([3.0])
w = tf.expand_dims(tf.concat([w1,w2],0),-1)
X = tf.random.normal((1,2))
y = tf.reduce_sum(X,1)
with tf.GradientTape(persistent=True) as tape:
r = tf.matmul(w,X)
loss = tf.metrics.mse(y, w)
print(tape.gradient(loss, r))
Результаты None
.
Возможное исправление
Одним из решений является сохранение переменной разделенной. Для вашего слоя с числом units=1
, есть эта тривиальная замена tf.matmul
:
w1 = tf.Variable([1.0])
w2 = tf.Variable([3.0], trainable=False)
X = tf.random.normal((1,2))
y = tf.reduce_sum(X,1)
with tf.GradientTape(persistent=True) as tape:
r = X[:,0]*w1 X[:,1]*w2
loss = tf.metrics.mse(y,r)
print(tape.gradient(loss, r))
Выходы : tf.Tensor([-3.1425157], shape=(1,), dtype=float32)
Комментарии:
1. Спасибо, это действительно полезно! Кажется, это решает мою проблему с весами. Но есть еще несколько вещей, которые я не понимаю: 1) если я добавлю смещение в свой класс MySum и использую его в MySum.call, тогда я смогу увидеть его за пределами моего класса, вызывающего функцию tf.keras get_weight() на моем слое. Но я вижу только веса. 2) Откуда tf.keras знает, как вычислить градиент, когда я создаю свой собственный слой с настраиваемыми весами и функцией активации?
2. @MepM Я обновил свой ответ, поведение было на самом деле из-за ошибки в TensorFlow. По другим вашим вопросам, не стесняйтесь задавать другие вопросы на веб-сайте. Я постараюсь ответить, если смогу.