【问题标题】:How to mix trainable and non trainable weights inside a same layer in tf.keras如何在 tf.keras 的同一层内混合可训练和不可训练的权重
【发布时间】:2021-02-11 00:29:24
【问题描述】:

我想创建一个包含可训练和不可训练权重的层(使用 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)

但是,当我尝试像这样使用我的图层时:

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.]] 数组从何而来? (我希望只有 [[1.][3.]] 数组)

  2. 在训练我的模型时,我有很多警告:"WARNING:tensorflow:Gradients do not exist for variables ['somme/Variable:0', 'somme/Variable:0'] when minimum the损失。” keras 如何将我自己的权重 (self.w) 与 get_weights() 返回的权重联系起来?

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

【问题讨论】:

  • 你在两个权重中使用相同的变量,这会覆盖之前定义的权重,只是使用不同的变量名

标签: tensorflow keras


【解决方案1】:

正如 Snoopy 博士所指出的,您的第一个解决方案使用相同的变量名称覆盖了之前定义的权重。

至于为什么您的第二个解决方案也不起作用,这是因为在您的两个tf.Variable w1w2 上调用tf.concat 后,e 渐变消失了。这是 Tensorflow 上的一个已知错误,您可以在 github 上找到问题:Gradients do not exist for variables after 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)如果我在我的类 MySum 中添加一个偏差并在 MySum.call 中使用它,那么我应该能够在我的类之外看到它调用 tf.keras 函数 get_weight () 在我的图层上。但我只看到权重。 2) 当我使用自定义权重和激活函数创建自己的层时,tf.keras 如何知道如何计算梯度?
  • @MepM 我已经更新了我的答案,这种行为实际上是由于 TensorFlow 中的一个错误。对于您的其他问题,请随时在网站上提出其他问题。如果可以,我会尽力回答。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-09-30
  • 2017-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多