我可以将张量的一部分设置为不可训练吗?

Could I set a part of a tensor untrainable?

设置一个不可训练的张量很容易,trainable=False。但是我可以只设置张量的一部分不可训练吗?

假设我有一个 2*2 张量,我只希望一个元素不可训练,而其他三个元素可训练。

像这样(我希望1,1元素始终为零,其他三个元素由优化器更新)

untrainable trainable 
trainable   trainable

谢谢。

简答:你不能。

更长的答案:您可以通过在计算梯度后将梯度的一部分设置为零来模拟该效果,这样变量的一部分就永远不会更新。

这是一个例子:

import tensorflow as tf
tf.random.set_seed(0)
model = tf.keras.Sequential([tf.keras.layers.Dense(2, activation="sigmoid", input_shape=(2,), name="first"), tf.keras.layers.Dense(1,activation="sigmoid")])
X = tf.random.normal((1000,2))
y = tf.reduce_sum(X, axis=1)
ds = tf.data.Dataset.from_tensor_slices((X,y))

在该示例中,第一层的权重 W 如下:

>>> model.get_layer("first").trainable_weights[0]
<tf.Variable 'first/kernel:0' shape=(2, 2) dtype=float32, numpy=
array([[ 0.13573623, -0.68269   ],
       [ 0.8938798 ,  0.6792033 ]], dtype=float32)>

然后我们编写自定义循环,只更新该权重的第一行 W :

loss = tf.losses.MSE
opt = tf.optimizers.SDG(1.) # high learning rate to see the change
for xx,yy in ds.take(1):
    with tf.GradientTape() as tape:
        l = loss(model(xx),yy)
    g = tape.gradient(l,model.get_layer("first").trainable_weights[0])
    gradient_slice = g[:1] # first row
    new_grad = tf.concat([gradient_slice, tf.zeros((1,2), dtype=tf.float32),], axis=0) # replacing the rest with zeros
    opt.apply_gradients(zip([new_grad], [model.get_layer("first").trainable_weights[0]]))

然后,在 运行 那个循环之后,我们可以再次检查权重:

model.get_layer("first").trainable_weights[0]
<tf.Variable 'first/kernel:0' shape=(2, 2) dtype=float32, numpy=
array([[-0.08515069, -0.51738167],
       [ 0.8938798 ,  0.6792033 ]], dtype=float32)>

而且只更改了第一行。