【问题标题】:How to use stop_gradient in Tensorflow如何在 Tensorflow 中使用 stop_gradient
【发布时间】:2015-11-16 03:10:01
【问题描述】:

我想知道如何在 tensorflow 中使用stop_gradient,文档对我来说不是很清楚。

我目前正在使用stop_gradient 来生成损失函数 w.r.t 的梯度。 CBOW word2vec 模型中的词嵌入。我只想获得价值,而不是进行反向传播(因为我正在生成对抗性示例)。

目前,我正在使用代码:

lossGrad = gradients.gradients(loss, embed)[0]
real_grad = lossGrad.eval(feed_dict)

但是当我运行它时,它无论如何都会进行反向传播!我做错了什么,同样重要的是,我该如何解决这个问题?

澄清:为了澄清“反向传播”,我的意思是“计算值和更新模型参数”。

更新

如果我在第一个训练步骤之后运行上面的两行代码,我在 100 个训练步骤之后得到的损失与我不运行这两行代码时不同。我可能从根本上误解了 Tensorflow。

我尝试在图形声明的开头和每个训练步骤之前都使用set_random_seed 进行设置。多次运行之间的总损失是一致的,但在包括/排除这两条线之间却不一致。因此,如果不是 RNG 导致了差异,也不是训练步骤之间模型参数的意外更新,您知道什么会导致这种行为吗?

解决方案

Welp,这有点晚了,但这是我解决它的方法。我只想优化一些但不是全部变量。我认为防止优化某些变量的方法是使用stop_grad - 但我从来没有找到一种方法来实现这一点。也许有一种方法,但对我有用的是调整我的optimizer 以仅优化变量列表。所以而不是:

opt = tf.train.GradientDescentOptimizer(learning_rate=eta)
train_op = opt.minimize(loss)

我用过:

opt = tf.train.GradientDescentOptimizer(learning_rate=eta)
train_op = opt.minimize(loss, var_list=[variables to optimize over])

这阻止了opt 更新不在var_list 中的变量。希望它也对你有用!

【问题讨论】:

  • loss(因此是lossGrad)是否依赖于任何输入阅读器,或者是否所有示例都使用feed_dict 传递? (feed_dict 是否与 lossGrad.eval(...) 和训练步骤相同?)你的图中的随机诱导操作是什么?
  • 不,所有的例子都是通过feed_dict传入的。 feed_dict 与训练步骤相同。随机诱导操作是变量embeddingsnce_weights 的初始化。 embed 取决于 embeddings,损失取决于 embeddingsnce_weights。它可以在教程中的 word2vec 示例中重现。
  • 我注意到 word2vec 示例使用了一个自定义阅读器操作 (word2vec.skipgram),它是有状态的。您确定它没有执行多次(尽管输入了示例和标签),即在训练步骤中执行一次,在获取梯度时执行一次?另一种可能性:如果难以追踪副作用,您可以修改对session.run(train_op) 的调用以在同一步骤中计算梯度。
  • 我的错 - 我的意思是 word2vec_basic。 IIRC,这个不使用word2vec.skipgram。修改对session.run(train_op) 的调用(耶!)用于渐变w.r.t。美联储的输入,但如果我想使用新的输入。有没有办法解决这个问题?
  • “反向传播”是指“计算值和更新模型参数”。 ==> backprop 只计算梯度。优化器用于更新参数。

标签: tensorflow


【解决方案1】:

tf.stop_gradient 提供了一种在反向传播期间不计算某些变量的梯度的方法。

例如,在下面的代码中,我们有三个变量,w1w2w3 和输入 x。损失是square((x1.dot(w1) - x.dot(w2 * w3)))。我们希望将此损失最小化为w1,但希望保持w2w3 不变。为此,我们只需输入tf.stop_gradient(tf.matmul(x, w2*w3))

在下图中,我将w1w2w3 从它们的初始值绘制为训练迭代的函数。可以看出w2w3保持不变,而w1一直变化直到等于w2 * w3

显示 w1 只学习但不学习 w2w3 的图像:

import tensorflow as tf
import numpy as np

w1 = tf.get_variable("w1", shape=[5, 1], initializer=tf.truncated_normal_initializer())
w2 = tf.get_variable("w2", shape=[5, 1], initializer=tf.truncated_normal_initializer())
w3 = tf.get_variable("w3", shape=[5, 1], initializer=tf.truncated_normal_initializer())
x = tf.placeholder(tf.float32, shape=[None, 5], name="x")


a1 = tf.matmul(x, w1)
a2 = tf.matmul(x, w2*w3)
a2 = tf.stop_gradient(a2)
loss = tf.reduce_mean(tf.square(a1 - a2))
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.1)
gradients = optimizer.compute_gradients(loss)
train_op = optimizer.apply_gradients(gradients)

【讨论】:

  • optimizer.compute_gradients(loss) 中你必须传入 [w1] (损失所依赖的张量列表),否则你会得到 Error: TypeError: Fetch argument None has invalid type
【解决方案2】:

tf. gradients(loss, embed) 计算张量 loss 相对于张量 embed 的偏导数。 TensorFlow 通过反向传播计算这个偏导数,因此评估tf. gradients(...) 的结果执行反向传播是预期的行为。但是,评估该张量不会执行任何变量更新,因为表达式不包含任何 assignment operations

tf.stop_gradient() 是一个操作,它在正向方向上充当恒等函数,但阻止累积梯度在反向方向上流过该算子。它不会完全阻止反向传播,而是阻止单个张量对为表达式计算的梯度做出贡献。 documentation for the operation 有更多关于操作的详细信息,以及何时使用它。

【讨论】:

  • 那么这是否意味着tf.gradients(loss, embed)[0].eval() 不会改变网络中的任何权重?我怎么能用任意输入计算这个值,而不首先在网络上运行它们并在过程中进行反向传播?感谢您的快速回复,顺便说一句:)
  • 没错——它只是计算并返回该梯度的值,而不运行任何更新权重的操作。如果您想计算任意输入的梯度,只需将feed_dict 提供给eval()。 (为了清楚起见:我假设“反向传播”是指“计算梯度更新模型参数。)
  • 感谢您迄今为止的帮助。我添加了一个更新,以根据您的回答澄清问题。
  • 感谢tf.stop_gradient(op)。它真的很有用。我宁愿将它的段落移到答案的顶部,因为这是人们会寻找的。​​span>
  • 链接应该已经修复了!
猜你喜欢
  • 1970-01-01
  • 2019-01-02
  • 2017-03-26
  • 2019-02-20
  • 2017-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-02
相关资源
最近更新 更多