Pytorch GAN中D和G的训练详细解释

简单来说detach就是截断反向传播的梯度流

    def detach(self):
        """Returns a new Variable, detached from the current graph.

        Result will never require gradient. If the input is volatile, the output
        will be volatile too.

        .. note::

          Returned Variable uses the same data tensor, as the original one, and
          in-place modifications on either of them will be seen, and may trigger
          errors in correctness checks.
        """
        result = NoGrad()(self)  # this is needed, because it merges version counters
        result._grad_fn = None
        return result
 
 

    可以看到Returns a new Variable, detached from the current graph。将某个node变成不需要梯度的Varibale。因此当反向传播经过这个node时,梯度就不会从这个node往前面传播。

    从GAN的代码中看detach()

    GAN的G的更新,主要是GAN loss。就是G生成的fake图让D来判别,得到的损失,计算梯度进行反传。这个梯度只能影响G,不能影响D!可以看到,由于torch是非自动求导的,每一层的梯度的计算必须用net:backward才能计算gradInput和网络中的参数的梯度。

    先看Torch版本的代码

    local fGx = function(x)
        netD:apply(function(m) if torch.type(m):find('Convolution') then m.bias:zero() end end)
        netG:apply(function(m) if torch.type(m):find('Convolution') then m.bias:zero() end end)
    
        gradParametersG:zero()
    
        -- GAN loss
        local df_dg = torch.zeros(fake_B:size())
        if opt.use_GAN==1 then
           local output = netD.output -- netD:forward{input_A,input_B} was already executed in fDx, so save computation
           local label = torch.FloatTensor(output:size()):fill(real_label) -- fake labels are real for generator cost
    
           errG = criterion:forward(output, label)
           local df_do = criterion:backward(output, label)
           df_dg = netD:updateGradInput(fake_AB, df_do):narrow(2,fake_AB:size(2)-output_nc+1, output_nc)
        else
            errG = 0
        end
    
        -- unary loss
        -- 得到 df_do_AE(已省略)   
        netG:backward(real_A, df_dg + df_do_AE:mul(opt.lambda))
    
        return errG, gradParametersG
    end
     
     

      在下面代码中,是先得到fake图进入D的loss,然后这个loss的梯度df_do进行反传,首先要这个梯度经过D。此时不能改变D的参数的梯度,所以这里用updateGradInput,不能用backward。这是因为backward是调用2个函数updateGradInputaccGradParameters。后者是计算loss对于网络中参数的梯度,这些梯度是不断累加的!除非手动gradParametersG:zero()置零。

             errG = criterion:forward(output, label)
             local df_do = criterion:backward(output, label)
             df_dg = netD:updateGradInput(fake_AB, df_do):narrow(2,fake_AB:size(2)-output_nc+

      然后得到的df_dg才是要更新G的GAN损失的梯度,当然G的另一个损失是L1损失(unary loss)这个没啥好说了。

      pytorch的GAN实现

      由于Pytorch是自动反向传播,

          def backward_D(self):
              # Fake
              # stop backprop to the generator by detaching fake_B
              fake_AB = self.fake_B
              # fake_AB = self.fake_AB_pool.query(torch.cat((self.real_A, self.fake_B), 1))
              self.pred_fake = self.netD.forward(fake_AB.detach())
              self.loss_D_fake = self.criterionGAN(self.pred_fake, False)
      
              # Real
              real_AB = self.real_B # GroundTruth
              # real_AB = torch.cat((self.real_A, self.real_B), 1)
              self.pred_real = self.netD.forward(real_AB)
              self.loss_D_real = self.criterionGAN(self.pred_real, True)
      
              # Combined loss
              self.loss_D = (self.loss_D_fake + self.loss_D_real) * 0.5
      
              self.loss_D.backward()
      
          def backward_G(self):
              # First, G(A) should fake the discriminator
              fake_AB = self.fake_B
              pred_fake = self.netD.forward(fake_AB)
              self.loss_G_GAN = self.criterionGAN(pred_fake, True)
      
              # Second, G(A) = B
              self.loss_G_L1 = self.criterionL1(self.fake_B, self.real_B) * self.opt.lambda_A
      
              self.loss_G = self.loss_G_GAN + self.loss_G_L1
      
              self.loss_G.backward()
      
      
          def forward(self):
              self.real_A = Variable(self.input_A)
              self.fake_B = self.netG.forward(self.real_A)
              self.real_B = Variable(self.input_B)
      
          # 先调用 forward, 再 D backward, 更新D之后; 再G backward, 再更新G
          def optimize_parameters(self):
              self.forward()
      
              self.optimizer_D.zero_grad()
              self.backward_D()
              self.optimizer_D.step()
      
              self.optimizer_G.zero_grad()
              self.backward_G()
              self.optimizer_G.step()
       
       

        解释backward_D:

        对于D,我们值需要,如果输入是真实图,那么产生loss,输入真实图,也产生loss。
        这两个梯度进行更新D。如果是真实图(real_B),由于real_B是初始结点,所以没什么可担心的。但是对于生成图fake_B,由于 fake_B是由 netG.forward(real_A)产生的。我们只希望 该loss更新D不要影响到 G. 因此这里需要“截断反传的梯度流”,用 fake_AB = fake_B, fake_AB.detach()从而让梯度不要通过 fake_AB反传到netG中!

        解释backward_G:

        由于在调用 backward_G已经调用了zero_grad,所以没什么好担心的。
        更新G时,来自D的GAN损失是, netD.forward(fake_AB),得到 pred_fake,然后得到损失,反传播即可。
        注意,这里反向传播时,会先将梯度传到 fake_AB结点,然而我们知道 fake_AB即 fake_B结点,而fake_B正是由netG(real_A)产生的,所以还会顺着继续往前传播,从而得到G的对应的梯度。

        对比 Torch代码

        df_dg = netD:updateGradInput(fake_AB, df_do):narrow(2,fake_AB:size(2)-output_nc+1, output_nc)
        netG:backward(real_A, df_dg + df_do_AE:mul(opt.lambda))
         
         

          Torch中,没有计算netD的参数的梯度,而是直接用 updateGradInput。在pytorch中,我们也是希望GAN loss只能更新G。但是pytorch是自动求导的,所以我们没法手动像Torch一样只调用updateGradInput

                  self.loss_G_GAN = self.criterionGAN(pred_fake, True)
          
                  # Second, G(A) = B
                  self.loss_G_L1 = self.criterionL1(self.fake_B, self.real_B) * self.opt.lambda_A
          
                  self.loss_G = self.loss_G_GAN + self.loss_G_L1
                  self.loss_G.backward()
           
           

            在这里,虽然pytorch中会自动计算所有的结点的梯度,但是我们执行loss_G.backward()后,按照Torch的理解是,这里直接调用backward。即不仅调用了updateGradInput(我们只需要这个),还额外的计算了accGradParameters(这个是没用的),但是看到,在optimize_parameters中,只是进行 optimizer_G.step()所以只会更新G的参数。所以没有更新D(虽然此时D中有dummy gradient)。等下一回合,又调用 optimizer_D.zero_grad(), 因此会把刚才残留的D的梯度清空。所以仍旧是符合的。

            自动求导反向书写的简洁

            得出结论,书写自动求导的代码完全还是很简洁的。只需要进行loss计算。loss可以直接相加,然后loss.backward()即可。loss的定义比如:

            self.optimizer_G = torch.optim.Adam(self.netG.parameters(),
                        lr=opt.lr, betas=(opt.beta1, 0.999))
             
             

              Adam是继承自Optimizer类。该类的step函数会将构建loss的所有的Variable的参数进行更新。

                  def step(self, closure=None):
                      """Performs a single optimization step.
              
                      Arguments:
                          closure (callable, optional): A closure that reevaluates the model
                              and returns the loss.
                      """
                      loss = None
                      if closure is not None:
                          loss = closure()
              
                      for group in self.param_groups:
                          for p in group['params']: 
                           #如果这个参数有没有grad(这个Variable的requries_grad为False)
                           #则直接跳过。
                              if p.grad is None:
                                  continue
                              grad = p.grad.data
                              state = self.state[p]
              
                              # 对p.data进行更新!就是对参数进行更新!
              
                              # State initialization
                              if len(state) == 0:
                                  state['step'] = 0
                                  # Exponential moving average of gradient values
                                  state['exp_avg'] = grad.new().resize_as_(grad).zero_()
                                  # Exponential moving average of squared gradient values
                                  state['exp_avg_sq'] = grad.new().resize_as_(grad).zero_()
              
                              exp_avg, exp_avg_sq = state['exp_avg'], state['exp_avg_sq']
                              beta1, beta2 = group['betas']
              
                              state['step'] += 1
              
                              if group['weight_decay'] != 0:
                                  grad = grad.add(group['weight_decay'], p.data)
              
                              # Decay the first and second moment running average coefficient
                              exp_avg.mul_(beta1).add_(1 - beta1, grad)
                              exp_avg_sq.mul_(beta2).addcmul_(1 - beta2, grad, grad)
              
                              denom = exp_avg_sq.sqrt().add_(group['eps'])
              
                              bias_correction1 = 1 - beta1 ** state['step']
                              bias_correction2 = 1 - beta2 ** state['step']
                              step_size = group['lr'] * math.sqrt(bias_correction2) / bias_correction1
              
                              p.data.addcdiv_(-step_size, exp_avg, denom)
               
               

                转载 https://blog.csdn.net/Hungryof/article/details/78035332

                • 6
                  点赞
                • 10
                  收藏
                  觉得还不错? 一键收藏
                • 1
                  评论
                1.项目代码功能经验证ok,确保稳定可靠运行。欢迎下载使用!在使用过程,如有问题或建议,请及时私信沟通。 2.主要针对各个计算机相关专业,包括计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网等领域的在校学生、专业教师或企业员工使用。 3.项目具有丰富的拓展空间,不仅可作为入门进阶,也可直接作为毕设、课程设计、大作业、初期项目立项演示等用途。 4.当然也鼓励大家基于此进行二次开发。 5.期待你能在项目找到乐趣和灵感,也欢迎你的分享和反馈! 【资源说明】 基于mindspore框架和GAN实现的漫画脸生成python源码+项目说明+模型.zip 简介 图像风格迁移领域近期有一篇论文U-GAT-IT提出了一种归一化方法——AdaLIN,能够自动调节Instance Norm和Layer Norm的比重,再结合attention机制能够实现精美的人像日漫风格转换。 主要包含两个生成器Generator和四个辨别器Discriminator,也是本工作的主要完成训练框架转换的部分。本工作主要根据开源的pytorch代码,将其的风格迁移模型U-GAT-IT的模型定义、训练和测试脚本改成了mindspore框架(具体包括**训练脚本**`./models/UGATIT_sadalin_hourglass.py`和**模型定义文件**`./models/ms/networks.py`),而数据预处理的部分保留原先的pytorch框架。 ## 生成过程 由于实验数据较为匮乏,为了降低训练难度,首先将数据处理成固定的模式。数据处理部分用的是pytorch的模型和框架。主要包含以下几个步骤: - 检测人脸及关键点。 - 根据关键点旋转校正人脸。 - 将关键点边界框按固定的比例扩张并裁剪出人脸区域。 - 使用人像分割模型将背景置白,得到统一的图像模式。 ![image-20220625223031715](./images/image-20220625223031715.png) 最后将去除背景的正脸,输入U-GAT-IT模型进行漫画风格迁移,得到最终的漫画脸。 硬件环境与依赖库 相关的系统与硬件配置如下: - Linux x86_64 - Ubuntu 18.04 - NVIDIA Tesla T4 (CUDA 11.4) 项目所需的主要依赖库如下: - mindspore 1.7.0 - python 3.7.5 - pytorch 1.7 - tensorflow-gpu 1.14 - face-alignment - dlib 训练 1. 随机初始化权重并重新训练: ```shell python train.py --dataset photo2cartoon ``` 2. 加载预训练参数进行训练: ```shell python train.py --dataset photo2cartoon --pretrained_model pretrained_models ``` 3. 训练过程会输出iteration和训练的时间、生成器的损失g_loss和辨别器的损失d_loss: 测试 1. 将一张测试照片(亚洲年轻女性)转换为卡通风格: ```shell python test.py --photo_path ./images/photo_test.jpg --save_path ./images/cartoon_result.png ``` 2. 测试输出如下,表示漫画脸生成成功,生成结果存放在`--save_path`设置的路径下。
                GAN (Generative Adversarial Network) 是一种深度学习型,用于生成模拟数据,如图像、音频、文本等。PyTorch 是一个广泛使用的深度学习框架,可以用来实现 GAN。 在 PyTorch 实现 GAN,你需要定义一个生成器网络和一个判别器网络。生成器网络接收一些随机噪声作为输入,并生成与真实数据类似的数据样本。判别器网络则尝试区分生成器产生的假数据和真实数据。 训练 GAN 的过程,生成器和判别器相互博弈。生成器的目标是生成尽可能逼真的数据以欺骗判别器,而判别器的目标是准确地区分真实数据和生成的数据。通过交替地训练生成器和判别器,GAN 可以逐渐提升生成器产生的数据质量。 在 PyTorch ,你可以使用 nn.Module 类来定义生成器和判别器网络,使用 nn.BCELoss 作为损失函数来度量判别器的输出与真实标签之间的差异。你还可以使用优化器如 Adam 来更新网络的参数。 以下是一个简单的 PyTorch GAN 示例代码: ```python import torch import torch.nn as nn import torch.optim as optim # 定义生成器网络 class Generator(nn.Module): def __init__(self): super(Generator, self).__init__() # 定义网络结构... def forward(self, x): # 前向传播过程... # 定义判别器网络 class Discriminator(nn.Module): def __init__(self): super(Discriminator, self).__init__() # 定义网络结构... def forward(self, x): # 前向传播过程... # 初始化生成器和判别器 generator = Generator() discriminator = Discriminator() # 定义损失函数和优化器 criterion = nn.BCELoss() optimizer_g = optim.Adam(generator.parameters(), lr=0.001) optimizer_d = optim.Adam(discriminator.parameters(), lr=0.001) # 训练 GAN for epoch in range(num_epochs): # 更新判别器 optimizer_d.zero_grad() # 计算真实数据的判别器损失 real_images = ... real_labels = torch.ones(batch_size, 1) output_real = discriminator(real_images) loss_real = criterion(output_real, real_labels) # 计算生成数据的判别器损失 fake_images = generator(torch.randn(batch_size, latent_dim)) fake_labels = torch.zeros(batch_size, 1) output_fake = discriminator(fake_images.detach()) loss_fake = criterion(output_fake, fake_labels) # 总的判别器损失 loss_d = loss_real + loss_fake loss_d.backward() optimizer_d.step() # 更新生成器 optimizer_g.zero_grad() # 生成器生成数据并输入判别器 fake_images = generator(torch.randn(batch_size, latent_dim)) output_fake = discriminator(fake_images) # 生成器的损失(让判别器将生成数据判别为真实数据) loss_g = criterion(output_fake, real_labels) loss_g.backward() optimizer_g.step() ``` 这只是一个简单的示例代码,实际上你可能需要根据具体的问题和数据集进行更复杂的网络设计和训练策略。希望这可以帮助你入门 PyTorch GAN 实现!

                “相关推荐”对你有帮助么?

                • 非常没帮助
                • 没帮助
                • 一般
                • 有帮助
                • 非常有帮助
                提交
                评论 1
                添加红包

                请填写红包祝福语或标题

                红包个数最小为10个

                红包金额最低5元

                当前余额3.43前往充值 >
                需支付:10.00
                成就一亿技术人!
                领取后你会自动成为博主和红包主的粉丝 规则
                hope_wisdom
                发出的红包
                实付
                使用余额支付
                点击重新获取
                扫码支付
                钱包余额 0

                抵扣说明:

                1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
                2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

                余额充值