Tensorflow,Keras:如何创建仅在特定位置更新的可训练变量?

Posted

技术标签:

【中文标题】Tensorflow,Keras:如何创建仅在特定位置更新的可训练变量?【英文标题】:Tensorflow, Keras: How to create a trainable variable that only update in specific positions? 【发布时间】:2019-01-15 15:41:06 【问题描述】:

例如,y=Ax

其中A 是一个对角矩阵,其可训练权重 (w1, w2, w3) 在对角线上。

A = [w1 ... ...
    ...  w2 ...
    ... ... w3]

如何在 Tensorflow 或 Keras 中创建这样可训练的 A

如果我尝试A = tf.Variable(np.eye(3)),可训练权重的总数将是 3*3=9,而不是 3。因为我想更新 (w1,w2,w3) 这 3 个权重.

一个技巧可能是使用A = tf.Variable([1, 1, 1]) * np.eye(3),以便将3个可训练的权重映射到A的对角线上。

我的问题是:

    这个技巧对我有用吗?梯度会被正确计算吗?

    如果A的情况比较复杂怎么办?例如。如果我想创建:

w1, w2, ..., w6 是要更新的权重。

【问题讨论】:

您可以使用 layer.trainable = False 或 True 在 Keras 中创建可训练层。但是我想你会在梯度计算中发现一个问题,因为它需要矩阵运算,你可以找到一些可变的元素和其他的常数。 【参考方案1】:

您有两种不同的工具来解决这个问题。

    您可以创建所需的变量并将它们重新排列成所需的形式。 您可以创建比您需要的更多的变量,然后丢弃一些以达到所需的形式。

这两种方法都不是排他性的,您可以混合使用类型 #1 和 #2 的连续步骤。

例如,对于您的第一个示例(对角矩阵),我们可以使用方法 #1。

w = tf.Variable(tf.zeros(n))
A = tf.diag(w) # creates a diagonal matrix with elements of w

对于您的第二个更复杂的示例,我们可以使用方法 #2。

A = tf.Variable(tf.zeros((n, n)))
A = tf.matrix_band_part(A, 1, 1) # keep only the central band of width 3
A = tf.matrix_set_diag(A, tf.ones(n)) # set diagonal to 1

【讨论】:

【参考方案2】:

对于更复杂的情况,需要将A 分成几个部分,其中只有一些部分是可训练的,而其他部分可以具有任意值,最简单的做法是构建各个部分,然后将它们连接在一起。

例如,我需要任意大小的权重矩阵 A,(对于 4x4 大小)看起来像这样(2x2 的 4 个不同部分):

#  [[0.,   0.,   -0.2,    0.],
#   [0.,   0.,   0.,      -0.2],
#   [0.35, 0.,   train,   train],
#   [0.,   0.35, train,   train]]

制作代码:

n_neurons = 3
zero_quarter = tf.zeros((n_neurons, n_neurons))  # upper left quarter are zeros
neg_diag = tf.diag(tf.ones(n_neurons) * -0.2)  # upper right is negative diag
pos_diag = tf.diag(tf.ones(n_neurons) * 0.35)  # lower left is positive diag
# lower right quarter is trainable randomly initialized vars
train_quarter = tf.get_variable(name='TrainableWeights', shape=[n_neurons, n_neurons])

weights_row0 = tf.concat([zero_quarter, neg_diag], axis=1)
weights_row1 = tf.concat([pos_diag, train_quarter], axis=1)

weights = tf.concat([weights_row0, weights_row1], axis=0)

sess = tf.Session()
sess.run(tf.global_variables_initializer())
print(sess.run(weights))

结果是:

[[ 0.          0.          0.         -0.2         0.          0.        ]
 [ 0.          0.          0.          0.         -0.2         0.        ]
 [ 0.          0.          0.          0.          0.         -0.2       ]
 [ 0.35        0.          0.         -0.61401606  0.39812732  0.72078323]
 [ 0.          0.35        0.         -0.34560132  0.40494204  0.36660933]
 [ 0.          0.          0.35        0.34820676  0.5112138  -0.97605824]]

只有右下角的 3x3 部分是可训练的。

【讨论】:

【参考方案3】:

创建向量或矩阵变量都可以正常工作

对于问题 1。

别担心,梯度会正确计算的

关于问题 2。

如果它变得更复杂,就像您提到的那样,您仍然可以创建一个向量变量,然后从该变量构建矩阵。

或者,您可以创建一个矩阵变量,然后使用tf.scatter_update 而不是tf.assign 只更新其中的一部分

【讨论】:

以上是关于Tensorflow,Keras:如何创建仅在特定位置更新的可训练变量?的主要内容,如果未能解决你的问题,请参考以下文章

如何解决 tensorflow.keras 中的值错误?

如何在 Keras / Tensorflow 中将(无,)批量维度重新引入张量?

如何将保存的模型转换或加载到 TensorFlow 或 Keras?

如何确保 Keras 使用 GPU 和 tensorflow 后端?

Tensorflow v2 创建网络模型且保存参数至本地(非keras)

如何向Keras的层喂入数据