为什么在此示例中需要对变量调用分离?
Why detach needs to be called on variable in this example?
我正在看这个例子 - https://github.com/pytorch/examples/blob/master/dcgan/main.py 我有一个基本问题。
fake = netG(noise)
label = Variable(label.fill_(fake_label))
output = netD(fake.detach()) # detach to avoid training G on these labels
errD_fake = criterion(output, label)
errD_fake.backward()
D_G_z1 = output.data.mean()
errD = errD_real + errD_fake
optimizerD.step()
我理解为什么我们在变量 fake
上调用 detach()
,这样就不会为生成器参数计算梯度。我的问题是,因为 optimizerD.step()
将仅更新与 Discriminator 关联的参数,所以这重要吗?
OptimizerD 定义为:
optimizerD = optim.Adam(netD.parameters(), lr=opt.lr, betas=(opt.beta1, 0.999))
此外,下一步我们将更新生成器的参数,在此之前我们将调用 netG.zero_grad()
最终删除所有先前计算的梯度。此外,当我们更新 G 网络的参数时,我们会这样做 - output = netD(fake)
。在这里,我们没有使用分离。为什么?
那么,为什么在上面的代码中需要分离变量(第 3 行)?
因为假变量现在是生成器图的一部分 [1],但您不希望这样。因此,在将其放入鉴别器之前,您必须从他那里 "detach" 获取它。
原始答案(错误/不完整)
你是对的,optimizerD
只更新 netD
而 netG
上的渐变在 netG.zero_grad()
被调用之前没有被使用,所以分离是没有必要的,它只是节省时间,因为您不需要为生成器计算梯度。
你基本上也在自己回答你的另一个问题,你没有在第二个块中分离 fake
因为你特别想计算 netG
上的梯度以便能够更新它的参数.
注意在the second block real_label
中是如何作为fake
的对应标签的,所以如果判别器发现假输入是真实的,最后的损失很小,反之亦然,即正是您想要的发电机。不确定这是否让您感到困惑,但与在假输入上训练鉴别器相比,这确实是唯一的区别。
编辑
请看FatPanda的评论!我的原始答案实际上是不正确的。当调用 .backward()
时,Pytorch 会破坏(部分)计算图。如果不在 errD_fake.backward()
之前分离,稍后的 errG.backward()
调用将无法反向传播到生成器中,因为所需的图不再可用(除非您指定 retain_graph=True
)。我对 Soumith 犯了同样的错误感到宽慰 :D
让我告诉你。 detach的作用是冻结梯度下降。无论是判别网络还是生成网络,我们都更新了 logD(G(z))。对于判别网络,冻结G不影响整体的梯度更新(即认为内层函数是常数,不影响外层函数求梯度),但反过来,如果冻结D,则有没有办法完成梯度更新。因此,我们在训练生成器时没有使用冻结D的梯度。所以,对于生成器,我们确实计算了D的梯度,但是没有更新D的权重(只写了optimizer_g.step),所以生成器训练的时候不会改变判别器。你可能会问,这就是为什么,当你训练判别器的时候,你需要添加detach。这不是多余的动作吗?
因为我们冻结了梯度,所以我们可以加快训练速度,所以我们可以用在该用的地方。这不是一项额外的任务。那么我们在训练生成器的时候,因为logD(G(z)),没有办法冻结D的梯度,所以这里就不写detach了
投票最高的答案是 INCORRECT/INCOMPLETE!
检查这个:https://github.com/pytorch/examples/issues/116,然后看看@plopd 的回答:
This is not true. Detaching fake
from the graph is necessary to avoid forward-passing the noise through G when we actually update the generator. If we do not detach, then, although fake
is not needed for gradient update of D, it will still be added to the computational graph and as a consequence of backward
pass which clears all the variables in the graph (retain_graph=False
by default), fake
won't be available when G is updated.
这个post也说明了很多:https://zhuanlan.zhihu.com/p/43843694(中文)
那是因为如果你不在 output = netD(fake.detach()).view(-1)
中使用 fake.detach()
那么 fake 只是整个计算图中的一些中间变量,它跟踪 netG 和 netD 中的梯度。当您调用 netD.backward()
时,图表将被释放。这意味着计算图中不再有关于 netG() 的梯度信息。然后当你稍后使用 errG.backward() 时,它会导致类似
的错误
Trying to backward through the graph a second time
如果不用fake.detach(),可以用netD.backward(retain_graph=True)
我正在看这个例子 - https://github.com/pytorch/examples/blob/master/dcgan/main.py 我有一个基本问题。
fake = netG(noise)
label = Variable(label.fill_(fake_label))
output = netD(fake.detach()) # detach to avoid training G on these labels
errD_fake = criterion(output, label)
errD_fake.backward()
D_G_z1 = output.data.mean()
errD = errD_real + errD_fake
optimizerD.step()
我理解为什么我们在变量 fake
上调用 detach()
,这样就不会为生成器参数计算梯度。我的问题是,因为 optimizerD.step()
将仅更新与 Discriminator 关联的参数,所以这重要吗?
OptimizerD 定义为:
optimizerD = optim.Adam(netD.parameters(), lr=opt.lr, betas=(opt.beta1, 0.999))
此外,下一步我们将更新生成器的参数,在此之前我们将调用 netG.zero_grad()
最终删除所有先前计算的梯度。此外,当我们更新 G 网络的参数时,我们会这样做 - output = netD(fake)
。在这里,我们没有使用分离。为什么?
那么,为什么在上面的代码中需要分离变量(第 3 行)?
因为假变量现在是生成器图的一部分 [1],但您不希望这样。因此,在将其放入鉴别器之前,您必须从他那里 "detach" 获取它。
原始答案(错误/不完整)
你是对的,optimizerD
只更新 netD
而 netG
上的渐变在 netG.zero_grad()
被调用之前没有被使用,所以分离是没有必要的,它只是节省时间,因为您不需要为生成器计算梯度。
你基本上也在自己回答你的另一个问题,你没有在第二个块中分离 fake
因为你特别想计算 netG
上的梯度以便能够更新它的参数.
注意在the second block real_label
中是如何作为fake
的对应标签的,所以如果判别器发现假输入是真实的,最后的损失很小,反之亦然,即正是您想要的发电机。不确定这是否让您感到困惑,但与在假输入上训练鉴别器相比,这确实是唯一的区别。
编辑
请看FatPanda的评论!我的原始答案实际上是不正确的。当调用 .backward()
时,Pytorch 会破坏(部分)计算图。如果不在 errD_fake.backward()
之前分离,稍后的 errG.backward()
调用将无法反向传播到生成器中,因为所需的图不再可用(除非您指定 retain_graph=True
)。我对 Soumith 犯了同样的错误感到宽慰 :D
让我告诉你。 detach的作用是冻结梯度下降。无论是判别网络还是生成网络,我们都更新了 logD(G(z))。对于判别网络,冻结G不影响整体的梯度更新(即认为内层函数是常数,不影响外层函数求梯度),但反过来,如果冻结D,则有没有办法完成梯度更新。因此,我们在训练生成器时没有使用冻结D的梯度。所以,对于生成器,我们确实计算了D的梯度,但是没有更新D的权重(只写了optimizer_g.step),所以生成器训练的时候不会改变判别器。你可能会问,这就是为什么,当你训练判别器的时候,你需要添加detach。这不是多余的动作吗? 因为我们冻结了梯度,所以我们可以加快训练速度,所以我们可以用在该用的地方。这不是一项额外的任务。那么我们在训练生成器的时候,因为logD(G(z)),没有办法冻结D的梯度,所以这里就不写detach了
投票最高的答案是 INCORRECT/INCOMPLETE!
检查这个:https://github.com/pytorch/examples/issues/116,然后看看@plopd 的回答:
This is not true. Detaching
fake
from the graph is necessary to avoid forward-passing the noise through G when we actually update the generator. If we do not detach, then, althoughfake
is not needed for gradient update of D, it will still be added to the computational graph and as a consequence ofbackward
pass which clears all the variables in the graph (retain_graph=False
by default),fake
won't be available when G is updated.
这个post也说明了很多:https://zhuanlan.zhihu.com/p/43843694(中文)
那是因为如果你不在 output = netD(fake.detach()).view(-1)
中使用 fake.detach()
那么 fake 只是整个计算图中的一些中间变量,它跟踪 netG 和 netD 中的梯度。当您调用 netD.backward()
时,图表将被释放。这意味着计算图中不再有关于 netG() 的梯度信息。然后当你稍后使用 errG.backward() 时,它会导致类似
Trying to backward through the graph a second time
如果不用fake.detach(),可以用netD.backward(retain_graph=True)