如何在tf.keras的同一层内混合可训练和不可训练的权重

时间:2020-10-28 15:38:50

标签: 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, 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. 训练模型时,我有很多警告: “警告:tensorflow:变量['somme / Variable:0','somme / Variable:0']不存在渐变尽量减少损失。” keras如何将我自己的权重(self.w)与get_weights()返回的权重联系起来?

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

1 个答案:

答案 0 :(得分:0)

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

关于为什么第二个解决方案也不起作用的原因是,在两个tf.concat tf.Variablew1上调用w2之后,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)