Tensorflow 如何在tf.keras的同一层中混合可训练和不可训练重量
我想创建一个层(使用tensorflow.keras),它包含可训练和不可训练的权重。我尝试通过子类化keras.layers.Layer来实现,如本例所示:Tensorflow 如何在tf.keras的同一层中混合可训练和不可训练重量,tensorflow,keras,Tensorflow,Keras,我想创建一个层(使用tensorflow.keras),它包含可训练和不可训练的权重。我尝试通过子类化keras.layers.Layer来实现,如本例所示: class MySum(keras.layers.Layer): def __init__(self, units=32, **kwargs): super(MySum, self).__init__(**kwargs) self.units = units def build(self
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)
不幸的是,这样做所有的重量是不可训练的。如果我首先添加不可训练权重,那么所有权重都是可训练的(似乎可训练标志是根据最后添加的权重设置的)。
我错过了什么
编辑: 我尝试使用Snoopy博士在构建函数中建议的不同名称:
# 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)
tf.Tensor(
[[1.]
[3.]], shape=(2, 1), dtype=float32)
[array([[1.],
[1.]], dtype=float32), array([[1.]], dtype=float32), array([[3.]], dtype=float32)]
但是,当我尝试像这样使用我的图层时:
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()函数获得:
# 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)
tf.Tensor(
[[1.]
[3.]], shape=(2, 1), dtype=float32)
[array([[1.],
[1.]], dtype=float32), array([[1.]], dtype=float32), array([[3.]], dtype=float32)]
注意:当我在不混合可训练权重和不可训练权重的情况下创建自定义层时,我没有这些问题。正如Snoopy博士所指出的,您的第一个解决方案使用相同的变量名称覆盖先前定义的权重 至于为什么您的第二个解决方案也不起作用,这是因为在对两个
tf.Variable
w1
和w2
调用tf.concat
后,e梯度消失了。这是Tensorflow上的一个已知错误,您可以在github上找到该问题:
一个最小的可重复的例子
让我们用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
可能的解决办法
一种解决方案是将变量分开。对于您的层,有许多单元=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)如果我在我的类MySum中添加了一个bias,并在MySum.call中使用它,那么我应该能够在类外看到它,在我的层上调用tf.keras函数get_weight()。但我只看到重量。2) 当我用自定义权重和激活函数创建自己的图层时,tf.keras如何知道如何计算梯度?@MepM我已经更新了我的答案,这种行为实际上是由于TensorFlow中的一个bug造成的。对于您的其他问题,请随时在网站上提出其他问题。如果可以的话,我会尽力回答。非常感谢你的帮助!