我正在阅读这个例子 - 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()
我理解为什么我们在变量detach()
上调用fake
,因此不会为Generator参数计算渐变。我的问题是,是否重要,因为optimizerD.step()
只会更新与Discriminator相关的参数?
OptimizerD定义为:
optimizerD = optim.Adam(netD.parameters(), lr=opt.lr, betas=(opt.beta1, 0.999))
此外,在下一步我们将更新Generator的参数时,在此之前我们将调用netG.zero_grad()
,最终删除所有先前计算的梯度。此外,当我们更新G网络的参数时,我们会这样做 - output = netD(fake)
。在这里,我们没有使用分离。为什么呢?
那么,为什么在上面的代码中需要分离变量(第3行)?
答案 0 :(得分:7)
你是对,optimizerD
只更新netD
,netG
上的渐变在调用netG.zero_grad()
之前未使用,因此不需要分离,它只是节省时间,因为你不计算发电机的梯度。
你自己基本上也在回答你的另一个问题,你不会在第二个区块中分离fake
,因为你特别想在netG
计算渐变以便能够更新它的参数。
注意the second block real_label
中的hashing如何用作fake
的相应标签,因此如果鉴别器发现假输入是真实的,那么最终损失很小,反之亦然,这正是你想要发电机。不确定这是否让你感到困惑,但与在假输入上训练鉴别器相比,它确实是唯一的区别。
答案 1 :(得分:0)
因为假变量现在是生成器图[1]的一部分,但你不想要它。所以你必须"分离"在你把它放入鉴别器之前,它就是他的。
答案 2 :(得分:0)
投票得最高的答案是不正确/不完整!
检查以下内容:https://github.com/pytorch/examples/issues/116,并查看@plopd的答案:
这不是事实。为了避免在我们实际更新生成器时将噪声从G向前传递,必须从图中分离
fake
。如果我们不分离,则尽管D的梯度更新不需要fake
,但仍将其添加到计算图中,并且作为backward
传递的结果,它清除了D中的所有变量。图(默认为retain_graph=False
),fake
在更新G时将不可用。
这篇文章也阐明了很多内容:https://zhuanlan.zhihu.com/p/43843694(中文)。
答案 3 :(得分:-1)
让我告诉你。分离的作用是冻结梯度下降。无论是用于区分网络还是生成网络,我们都将更新logD(G(z))。对于判别网络,冻结G不会影响整体梯度更新(即内部函数被视为常数,这不会影响外部函数以找到梯度),但是相反,如果D被冻结,则存在无法完成渐变更新。因此,在训练发生器时,我们没有使用冻结D的梯度。因此,对于生成器,我们确实计算了D的斜率,但是没有更新D的权重(仅写了optimizer_g.step),因此在训练生成器时不会更改鉴别器。您可能会问,这就是为什么在训练鉴别器时需要添加分离器的原因。这不是额外的举动吗? 因为我们冻结了渐变,所以我们可以加快训练速度,因此可以在可以使用的地方使用它。这不是额外的任务。然后,当我们训练生成器时,由于logD(G(z)),无法冻结D的梯度,因此我们在这里不再写分离。