Tensorflow 如何在tf.keras的同一层中混合可训练和不可训练重量

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

我想创建一个层(使用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, 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)]
  • [[1.],[1.]]数组来自哪里?(我只希望使用[[1.][3.]]数组)

  • 在训练我的模型时,我有很多警告:“警告:tensorflow:在最小化损失时,变量['somme/Variable:0','somme/Variable:0']不存在梯度。” keras如何将我自己的权重(self.w)与get_weights()返回的权重联系起来


  • 注意:当我在不混合可训练权重和不可训练权重的情况下创建自定义层时,我没有这些问题。

    正如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造成的。对于您的其他问题,请随时在网站上提出其他问题。如果可以的话,我会尽力回答。非常感谢你的帮助!