在 TensorFlow 中使用 stop_gradient 和 AdamOptimizer
Using stop_gradient with AdamOptimizer in TensorFlow
我正在尝试实现一个 training/finetuning 框架,在每次反向传播迭代中,一组特定的参数保持固定。我希望能够在迭代之间更改更新或固定参数集。 TensorFlow 方法 tf.stop_gradient
, which apparently forces gradients of some parameters to stay zero, is very useful for this purpose and it works perfectly fine with different optimizers if the set of updating or fixed parameters do not change from iterations to iterations. It can also handle varying set of updating or fixed parameters if it is used with stochastic gradient descent. My problem is that tf.stop_gradient
cannot handle such cases when being used with Adam optimizer. More specifically, it does keep the gradients of the fixed parameters at zero in the output of tf.compute_gradients
, but when applying the gradients (tf.apply_gradients
), value of the fixed parameters does change. I suppose this is because the optimiaztion step in Adam optimizer is not zero even if the gradient is zero (based on algorithm 1 in Kingma and Ba's paper)。有没有一种廉价的方法可以在每次 Adam 迭代中冻结一组可变参数,而不显式保存前一次迭代的固定参数值?
更多详情:
假设我有一个带有权重矩阵变量 W
和二进制掩码矩阵占位符 MW
的单层网络,它指定 W
的哪些元素应该在每次迭代中更新(中的值 1)。而不是用W
来写这一层的input/output关系,我修改如下
masked_W = MW*W + tf.stop_gradient(tf.abs(1-MW)*W)
屏蔽 W
的某些元素不具有非零梯度。然后我使用 masked_W
形成层的输出,因此网络的损失取决于这个屏蔽变量。关键是 MW
在每次迭代中都会发生变化。假设 W
是一个包含 4 个元素的向量,初始化为全零向量。这是发生的事情:
opt=tf.AdamOptimizer(1e-5)
sess.run(tf.global_variables_initializer())
grads_vars=opt.compute_gradients(loss, W)
# initial value of W=[0,0,0,0]
# first iteration:
MW_val = [0,1,1,0]
feed_dict={MW:MW_val, x: batch_of_data, y_:batch_of_labels}
sess.run(opt.apply_gradients(grads_vars), feed_dict=feed_dict))
# gradient of W=[0,xx,xx,0]
# new value of W=[0,a,b,0]
其中xx
是一些非零梯度值,a
和b
是更新W
元素的新值。在第二次迭代中,我们将分配给二进制掩码矩阵 MW
的值更改为 [1,0,0,1],因此我们希望 W[1]
和 W[2]
具有固定值并更新 W[0]
和 W[3]
的值。但这就是发生的事情:
# second iteration
MW_val = [1,0,0,1]
feed_dict={MW:MW_val, x: batch_of_data, y_:batch_of_labels}
sess.run(opt.apply_gradients(grads_vars), feed_dict=feed_dict))
# gradient of W=[xx,0,0,xx]
# new value of W=[c,aa,bb,d]
也就是说,虽然W[1]
和W[2]
的梯度为零,但它们得到了新的值(aa != a
和bb != b
)。将优化器从 Adam 更改为 SGD 时,固定参数的值与预期保持相同。
我找到了我的问题的解决方案并在这里分享它以防其他人发现它有用。在第一次迭代之后,那些在第一次迭代中更新的参数的矩已经不为零。因此,即使在第二次迭代中将它们的梯度设为零,它们也会因为它们的动量张量非零而被更新。为了防止更新,仅使用 tf.stop_gradient
是不够的,我们还必须去除它们的动量。在 Adam 优化器的情况下,这可以通过优化器的 get_slot
方法完成:opt.get_slot(par, 'm')
和 opt.get_slot(par,'v')
,其中前者和后者可以访问参数 par
,分别。在问题的示例中,我们必须添加以下行以在第二次迭代中冻结 W[1]
和 W[2]
:
# momentums of W in the first iteration
m_vals = opt.get_slot(W, 'm')
v_vals = opt.get_slot(W, 'v')
# mask them before running the second iteration
masked_m_vals[[1,2]]=0
masked_v_vals[[1,2]]=0
sess.run(opt.get_slot(W, 'm').assign(masked_m_vals))
sess.run(opt.get_slot(W, 'v').assign(masked_v_vals))
最好保存masked动量,在上面的例子中m_vals[[1,2]]
和v_vals[[1,2]]
,这样如果在第三次迭代中我们放宽W[1]
和[=的固定约束17=],我们可以在第一次迭代中将它们的动量恢复到原始值。
或者,当您想要更新不同的变量子集时,您可以将不同的变量子集传递给 apply_gradients。
我正在尝试实现一个 training/finetuning 框架,在每次反向传播迭代中,一组特定的参数保持固定。我希望能够在迭代之间更改更新或固定参数集。 TensorFlow 方法 tf.stop_gradient
, which apparently forces gradients of some parameters to stay zero, is very useful for this purpose and it works perfectly fine with different optimizers if the set of updating or fixed parameters do not change from iterations to iterations. It can also handle varying set of updating or fixed parameters if it is used with stochastic gradient descent. My problem is that tf.stop_gradient
cannot handle such cases when being used with Adam optimizer. More specifically, it does keep the gradients of the fixed parameters at zero in the output of tf.compute_gradients
, but when applying the gradients (tf.apply_gradients
), value of the fixed parameters does change. I suppose this is because the optimiaztion step in Adam optimizer is not zero even if the gradient is zero (based on algorithm 1 in Kingma and Ba's paper)。有没有一种廉价的方法可以在每次 Adam 迭代中冻结一组可变参数,而不显式保存前一次迭代的固定参数值?
更多详情:
假设我有一个带有权重矩阵变量 W
和二进制掩码矩阵占位符 MW
的单层网络,它指定 W
的哪些元素应该在每次迭代中更新(中的值 1)。而不是用W
来写这一层的input/output关系,我修改如下
masked_W = MW*W + tf.stop_gradient(tf.abs(1-MW)*W)
屏蔽 W
的某些元素不具有非零梯度。然后我使用 masked_W
形成层的输出,因此网络的损失取决于这个屏蔽变量。关键是 MW
在每次迭代中都会发生变化。假设 W
是一个包含 4 个元素的向量,初始化为全零向量。这是发生的事情:
opt=tf.AdamOptimizer(1e-5)
sess.run(tf.global_variables_initializer())
grads_vars=opt.compute_gradients(loss, W)
# initial value of W=[0,0,0,0]
# first iteration:
MW_val = [0,1,1,0]
feed_dict={MW:MW_val, x: batch_of_data, y_:batch_of_labels}
sess.run(opt.apply_gradients(grads_vars), feed_dict=feed_dict))
# gradient of W=[0,xx,xx,0]
# new value of W=[0,a,b,0]
其中xx
是一些非零梯度值,a
和b
是更新W
元素的新值。在第二次迭代中,我们将分配给二进制掩码矩阵 MW
的值更改为 [1,0,0,1],因此我们希望 W[1]
和 W[2]
具有固定值并更新 W[0]
和 W[3]
的值。但这就是发生的事情:
# second iteration
MW_val = [1,0,0,1]
feed_dict={MW:MW_val, x: batch_of_data, y_:batch_of_labels}
sess.run(opt.apply_gradients(grads_vars), feed_dict=feed_dict))
# gradient of W=[xx,0,0,xx]
# new value of W=[c,aa,bb,d]
也就是说,虽然W[1]
和W[2]
的梯度为零,但它们得到了新的值(aa != a
和bb != b
)。将优化器从 Adam 更改为 SGD 时,固定参数的值与预期保持相同。
我找到了我的问题的解决方案并在这里分享它以防其他人发现它有用。在第一次迭代之后,那些在第一次迭代中更新的参数的矩已经不为零。因此,即使在第二次迭代中将它们的梯度设为零,它们也会因为它们的动量张量非零而被更新。为了防止更新,仅使用 tf.stop_gradient
是不够的,我们还必须去除它们的动量。在 Adam 优化器的情况下,这可以通过优化器的 get_slot
方法完成:opt.get_slot(par, 'm')
和 opt.get_slot(par,'v')
,其中前者和后者可以访问参数 par
,分别。在问题的示例中,我们必须添加以下行以在第二次迭代中冻结 W[1]
和 W[2]
:
# momentums of W in the first iteration
m_vals = opt.get_slot(W, 'm')
v_vals = opt.get_slot(W, 'v')
# mask them before running the second iteration
masked_m_vals[[1,2]]=0
masked_v_vals[[1,2]]=0
sess.run(opt.get_slot(W, 'm').assign(masked_m_vals))
sess.run(opt.get_slot(W, 'v').assign(masked_v_vals))
最好保存masked动量,在上面的例子中m_vals[[1,2]]
和v_vals[[1,2]]
,这样如果在第三次迭代中我们放宽W[1]
和[=的固定约束17=],我们可以在第一次迭代中将它们的动量恢复到原始值。
或者,当您想要更新不同的变量子集时,您可以将不同的变量子集传递给 apply_gradients。