使用Pytorch从零开始构建WGAN

引言

在考虑生成对抗网络的文献时,Wasserstein GAN 因其与传统 GAN 相比的训练稳定性而成为关键概念之一。在本文中,我将介绍基于梯度惩罚的 WGAN 的概念。文章的结构安排如下:

  1. WGAN 背后的直觉;
  2. GAN 和 WGAN 的比较;
  3. 基于梯度惩罚的WGAN的数学背景;
  4. 使用 PyTorch 从头开始​​在;
  5. CelebA-Face 数据集上实现;
  6. WGAN 结果讨论。

WGAN 背后的直觉

GAN 最初由Ian J. Goodfellow 等人发明。在 GAN 中,有一个由生成器和判别器进行的双玩家最小最大游戏。早期 GAN 的主要问题是模式崩溃和梯度消失问题。为了克服这些问题,长期以来发明了许多技术。WGAN 是试图克服传统 GAN 的这些问题的方法之一。

GAN 与 WGAN

与传统的 GAN 相比,WGAN 有一些改进/变化。

  1. 评论家而非判别器;
  2. W-Loss 代替 BCE Loss;
  3. 使用梯度惩罚/权重剪裁进行权重正则化。

传统GAN的判别器被“Critic”取代。从实现的角度来看,这只不过是最后一层没有 Sigmoid 激活的判别器。

我们稍后将讨论 WGAN 损失函数和权重正则化。

数学背景

损失函数

这是基于梯度惩罚的 WGAN 的完整损失函数。

等式 1. 具有梯度惩罚的完整 WGAN 损失函数 — [3]
在这里插入图片描述
看起来很吓人吧?让我们分解一下这个方程。

第 1 部分:原始批评损失
在这里插入图片描述

该方程产生的值应由生成器正向最大化,同时由批评家负向最大化。请注意,这里的 x_CURL 是生成器 (G(z)) 生成的图像。

这里,D 在最后一层没有 Sigmoid 激活,因此 D(*) 可以是任何实数。这给出了地球移动器的真实分布和生成分布之间的距离的近似值 - [1]。我们在这里想做的是,

  1. 评论家的观点:通过最大化等式 2结果的负值/最小化正值,尽可能地将评论家对真实图像和生成图像的输出分布分开。这反映了评论家的目标,即为真实图像提供更高的分数,为更低的分数到生成的图像。
  2. 生成器的观点:尝试通过以相反的方向分离真实图像和生成图像的输出分布来抵消评论家的努力。这最终使式 2 的结果的正值最大化。这反映了生成器的目标是通过欺骗 Critic 来提高生成图像的 Critic 分数。
  • 在这里你可能已经注意到,Critic over Discriminator这个名字的出现是因为 Critic 不区分真假图像,只是给出一个无界的分数。

为了确保方程有效,我们需要确保 Critic 函数是 1-Lipschitz 连续的 — [1]。

1-Lipschitz连续性

函数 f(x) 是 1-L 连续的,梯度应始终小于或等于 1。

为了确保这种1-Lipschitz连续性,文献中主要提出了2种方法。

  1. Weight Clipping——这是 WGAN 论文 [2] 附带的初始方法;
  2. 梯度惩罚方法——这是在最初的论文之后作为改进提出的[3]。

在本文中,我们将重点关注基于梯度惩罚的 WGAN。

第二部分:梯度惩罚
在这里插入图片描述
这是 Gulrajani 等人提出的梯度惩罚。——[3]。这里我们通过减小 Critic 梯度的 L2 范数与 1 之间的平方距离来强制 Critic 的梯度为 1。注意,我们不能强制 Critic 的梯度为 0,因为这会导致梯度消失问题。

等等!x(^)是什么?

考虑到 1-Lipschitz 连续性的定义,所有 x 的梯度应≤1。但实际上,确保所有可能的图像都满足这种条件是很困难的。因此,我们使用 x(^) 表示使用真实图像和生成图像作为梯度惩罚的数据点的随机插值图像。这确保了 Critic 的梯度将通过查看训练期间遇到的一组公平的数据点/图像进行正则化。

Pytorch实现

在这里,我将介绍大家应该做的必要更改,以便将传统的 GAN 更改为 WGAN。

对于下面的实现,我将使用我在之前有关 DCGAN 的文章中详细解释的模型和训练原理。

数据集

Celeba-face 数据集用于训练。下载、预处理、制作数据加载器脚本如代码1所示。

import zipfile
import os
if not os.path.isfile('celeba.zip'):
  !mkdir data_faces && wget https://s3-us-west-1.amazonaws.com/udacity-dlnfd/datasets/celeba.zip 
  with zipfile.ZipFile("celeba.zip","r") as zip_ref:
    zip_ref.extractall("data_faces/")
    
from torch.utils.data import DataLoader

transform = transforms.Compose([
                               transforms.Resize((img_size,img_size)),
                               transforms.ToTensor(),
                                transforms.Normalize((0.5,0.5, 0.5),(0.5, 0.5, 0.5))])

dataset = datasets.ImageFolder('data_faces', transform=transform)
data_loader = DataLoader(dataset,batch_size=batch_size,shuffle=True)

生成器和评论家

Critic 与 Discriminator 相同,但不包含最后一层 Sigmoid 激活。

class Generator(nn.Module):
  def __init__(self,noise_channels,img_channels,hidden_G):
    super(Generator,self).__init__()
    self.G=nn.Sequential(
        conv_trans_block(noise_channels,hidden_G*16,kernal_size=4,stride=1,padding=0),
        conv_trans_block(hidden_G*16,hidden_G*8),
        conv_trans_block(hidden_G*8,hidden_G*4),
        conv_trans_block(hidden_G*4,hidden_G*2),
        nn.ConvTranspose2d(hidden_G*2,img_channels,kernel_size=4,stride=2,padding=1),
        nn.Tanh()
    )
  def forward(self,x):
    return self.G(x)

class Critic(nn.Module):
  def __init__(self,img_channels,hidden_D):
    super(Critic,self).__init__()
    self.D=nn.Sequential(
        conv_block(img_channels,hidden_G),
        conv_block(hidden_G,hidden_G*2),
        conv_block(hidden_G*2,hidden_G*4),
        conv_block(hidden_G*4,hidden_G*8),
        nn.Conv2d(hidden_G*8,1,kernel_size=4,stride=2,padding=0))
    
  def forward(self,x):
    return self.D(x)

Generator 和 Critic 的支持块如下面的代码 3 所示。

class conv_trans_block(nn.Module):
  def __init__(self,in_channels,out_channels,kernal_size=4,stride=2,padding=1):
    super(conv_trans_block,self).__init__()
    self.block=nn.Sequential(
        nn.ConvTranspose2d(in_channels,out_channels,kernal_size,stride,padding),
        nn.BatchNorm2d(out_channels),
        nn.ReLU())
  def forward(self,x):
    return self.block(x)

class conv_block(nn.Module):
  def __init__(self,in_channels,out_channels,kernal_size=4,stride=2,padding=1):
    super(conv_block,self).__init__()
    self.block=nn.Sequential(
        nn.Conv2d(in_channels,out_channels,kernal_size,stride,padding),
        nn.BatchNorm2d(out_channels),
        nn.LeakyReLU(0.2))
  def forward(self,x):
    return self.block(x)

损失函数

与任何其他典型的损失函数不同,损失函数可能有点棘手,因为它包含梯度。在这里,我们将使用梯度惩罚来实现 W-loss,稍后可以将其插入 WGAN 模型中。

def get_gen_loss(crit_fake_pred):
  gen_loss= -torch.mean(crit_fake_pred)
  return gen_loss

def get_crit_loss(crit_fake_pred, crit_real_pred, gradient_penalty, c_lambda):
  crit_loss= torch.mean(crit_fake_pred)- torch.mean(crit_real_pred)+ c_lambda* gradient_penalty
  return crit_loss

让我们分解一下代码 4 中所示的损失函数。

  1. 生成器损失 - 生成器损失不受梯度惩罚的影响。因此,它必须仅最大化 D(x_CURL)/ D(G(z)) 项,这意味着最小化 -D(G(z))。这是在第 2 行中实现的。
  2. 批评者损失 - 批评者损失包含等式 1 中所示损失的 2 个部分。在第 6 行中,前两项给出等式 2 中解释的原始批评者损失,而最后一项给出等式 3 中解释的梯度惩罚。

梯度惩罚可以按照下面的代码 5 来实现 - [1]。

def get_gradient(crit, real_imgs, fake_imgs, epsilon):

  mixed_imgs= real_imgs* epsilon + fake_imgs*(1- epsilon)
  mixed_scores= crit(mixed_imgs)

  gradient= torch.autograd.grad(outputs= mixed_scores,
                                inputs= mixed_imgs,
                                grad_outputs= torch.ones_like(mixed_scores),
                                create_graph=True,
                                retain_graph=True)[0]
  return gradient

def gradient_penalty(gradient):
  gradient= gradient.view(len(gradient), -1)
  gradient_norm= gradient.norm(2, dim=1)
  penalty = torch.nn.MSELoss()(gradient_norm, torch.ones_like(gradient_norm))
  return penalty

在代码 5 中,get_gradient()函数返回从x_hat (混合图像)开始到Critic 输出 (mixed_scores)结束的所有网络梯度。这将在gradient_penalty()函数中使用,它返回Critic梯度的1和L2范数之间的均方距离。

减少 Critic 的损失最终会减少这种梯度惩罚。这确保了 Critic 函数保留了 1-Lipschitz 连续性。

训练

训练将与上一篇文章中的几乎相同。但这里的损失与传统的 GAN 损失不同。我已经使用WANDB记录我的结果。如果您有兴趣记录结果,WANDB 是一个非常好的工具。

C=Critic(img_channels,hidden_C).to(device)
G=Generator(noise_channels,img_channels,hidden_G).to(device)

#C=C.apply(init_weights)
#G=G.apply(init_weights)

wandb.watch(G, log='all', log_freq=10)
wandb.watch(C, log='all', log_freq=10)

opt_C=torch.optim.Adam(C.parameters(),lr=lr, betas=(0.5,0.999))
opt_G=torch.optim.Adam(G.parameters(),lr=lr, betas=(0.5,0.999))

gen_repeats=1
crit_repeats=3

noise_for_generate=torch.randn(batch_size,noise_channels,1,1).to(device)

losses_C=[]
losses_G=[]

for epoch in range(1,epochs+1):
  loss_C_epoch=[]
  loss_G_epoch=[]

  for idx,(x,_) in enumerate(data_loader):
    C.train()
    G.train()

    x=x.to(device)
    x_len=x.shape[0]

    ### Train C

    loss_C_iter=0
    for _ in range(crit_repeats):
      opt_C.zero_grad()
      z=torch.randn(x_len,noise_channels,1,1).to(device)

      real_imgs=x
      fake_imgs=G(z).detach()

      real_C_out=C(real_imgs)
      fake_C_out=C(fake_imgs)

      epsilon= torch.rand(len(x),1,1,1, device= device, requires_grad=True)
      gradient= get_gradient(C, real_imgs, fake_imgs.detach(), epsilon)
      gp= gradient_penalty(gradient)
      loss_C= get_crit_loss(fake_C_out, real_C_out, gp, c_lambda=10)

      loss_C.backward()
      opt_C.step()

      loss_C_iter+=loss_C.item()/crit_repeats

    ### Train G
    loss_G_iter=0
    for _ in range(gen_repeats):
      opt_G.zero_grad()
      z=torch.randn(x_len,noise_channels,1,1).to(device)
      fake_C_out = C(G(z))
      loss_G= get_gen_loss(fake_C_out)
      loss_G.backward()
      opt_G.step()

      loss_G_iter+=loss_G.item()/gen_repeats

结果

这是经过 10 个 epoch 训练后获得的结果。与传统 GAN 一样,生成的图像随着时间的推移变得更加真实。WANDB 项目的所有结果都可以在这里找到。
在这里插入图片描述

结论

生成对抗网络一直是深度学习社区的热门话题。由于 GAN 传统训练方法的缺点,WGAN 随着时间的推移变得越来越流行。这主要是因为它对模式崩溃具有鲁棒性并且不存在梯度消失问题。在本文中,我们实现了一个能够生成人脸的简单 WGAN 模型。

请随意查看 GitHub 代码。如有任何意见、建议和意见,我们将不胜感激。

Reference

[1] GAN specialization on coursera

[2] Arjovsky, Martin et al. “Wasserstein GAN”

[3] Gulrajani, Ishaan et al. “Improved Training of Wasserstein GANs”

[4] Goodfellow, Ian et al. “Generative Adversarial Networks”

[5] Vincent Herrmann, “Wasserstein GAN and the Kantorovich-Rubinstein Duality”

[6] Karras, Tero et al. “A Style-Based Generator Architecture for Generative Adversarial Networks”

本文译自Udith Haputhanthri的博文。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/179162.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

财报解读:将低价作为“唯一性基础武器”的京东,效果在慢慢显现

近日,京东集团公布了2023年第三季度财报。这是自创始人刘强东去年11月强势回归京东一线、主导一系列战略调整和人事组织改革近一年后的一份“成绩单”。 财报显示,第三季度京东实现收入2477亿元,同比增长1.7%;归属于公司普通股股…

Linux上通过SSL/TLS和start tls连接到LDAP服务器

一,大致流程。 1.首先在Linux上搭建一个LDAP服务器 2.在LDAP服务器上安装CA证书,服务器证书,因为SSL/TLS,start tls都属于机密通信,需要客户端和服务器都存在一个相同的证书认证双方的身份。3.安装phpldapadmin工具&am…

HubSpot驱动业务增长:客户拓展的完美引擎!

随着数字化时代的来临,企业面临着前所未有的挑战,尤其在拓展客户方面,传统的方法已经难以适应新的市场环境。在这个背景下,数字化时代的客户拓展变得更为复杂,企业需要更智能、更综合的解决方案来脱颖而出。 HubSpot作…

Java之API(上)

前言: 这一次内容主要是围绕Java开发中的一些常用类,然后主要是去学习这些类里面的方法。 一、高级API: (1)介绍:API指的是应用程序编程接口,API可以让编程变得更加方便简单。Java也提供了大量API供程序开发者使用&…

结构体与指针_sizeof_static_extern_函数指针数组_函数指针_回调函数

一、结构体与指针 #include <stdint.h> #include <stdlib.h> #include <stdio.h> #define up_to_down(uuu) (downdemo_t *)(uuu->beg) #define __plc__ typedef struct updemo_s{uint8_t *head;uint8_t *beg;uint8_t *end; }updemo_t; typedef struct do…

python爬虫HMAC加密案例:某企业信息查询网站

声明&#xff1a; 该文章为学习使用&#xff0c;严禁用于商业用途和非法用途&#xff0c;违者后果自负&#xff0c;由此产生的一切后果均与作者无关 一、找出需要加密的参数 js运行 atob(‘aHR0cHM6Ly93d3cucWNjLmNvbS93ZWIvc2VhcmNoP2tleT0lRTQlQjglODclRTglQkUlQkUlRTklOUI…

基于PHP的动漫周边购物系统

有需要请加文章底部Q哦 可远程调试 基于PHP的动漫周边购物系统 一 介绍 此动漫周边购物系统系统基于原生PHP开发&#xff0c;数据库mysql&#xff0c;前端bootstrap。用户可注册登录&#xff0c;购物下单&#xff0c;评论等。管理员登录后台可对动漫周边商品&#xff0c;用户…

JVM中判断对象是否需要回收的方法

在堆里面存放着Java 世界中几乎所有的对象实例&#xff0c;垃圾收集器在对堆进行回收前&#xff0c;第一件事情就是要确定这些对象之中哪些还“ 存活 ” 着&#xff0c;哪些已经 “ 死去 ”。 引用计数算法 引用计数法是一种内存管理技术&#xff0c;它是通过对每个对象进行引用…

实现el-input-number数字框带单位

实现的效果展示&#xff0c;可以是前缀单位&#xff0c;也可以是后缀单位。实现的思路就是动态修改伪元素 ::before 和 ::after 的 content值 实现二次封装数字框的代码如下&#xff1a; <template><el-input-numberref"inputNumber"v-model"inputVal…

某60区块链安全之Call函数簇滥用实战二学习记录

区块链安全 文章目录 区块链安全Call函数簇滥用实战二实验目的实验环境实验原理实验内容实验步骤EXP利用 Call函数簇滥用实战二 实验目的 学会使用python3的web3模块 学会并区分以太坊call、staticcall、delegatecall三种函数调用的特点 找到合约漏洞进行分析并形成利用 实验…

linux CentOS7.6安装jenkins(小白版本)

前言 本人是一个前端开发者&#xff0c;由于有时候需要发版自己的东西&#xff0c;所以想搞一个Jenkins玩玩&#xff0c;看了网上好多教程&#xff0c;但是都不是针对小白的&#xff0c;比如linux怎么输入&#xff0c;怎么结束&#xff0c;自己也是搞了好久踩了好多坑 所以记录…

大数据预处理技术

文章目录 前言 大数据技术成为前沿专业 也是现在甚至未来的朝阳产业&#xff0c;大数据有分别是 数据预处理 数据存储 大数据处理和分析 数据可视化 部分组成 &#xff0c;大数据行业有数据则称王&#xff0c;大数据的核心是数据本身 怎么获取有价值的数据呢&#xff1f;本章讲…

七天.NET 8操作SQLite入门到实战 - 第三天SQLite快速入门

前言 今天我们花费一个小时快速了解SQLite数据类型、SQLite常用命令和语法。 七天.NET 8操作SQLite入门到实战详细教程 第一天 SQLite 简介第二天 在 Windows 上配置 SQLite环境 EasySQLite项目源码地址 GitHub地址&#xff1a;https://github.com/YSGStudyHards/EasySQLite&…

Cookie、Session、CBV加装饰器的三种方法

【0】cookie、session和Token的发展史 【1】Cookie的形式 存储形式&#xff1a;k&#xff1a;v键值对存储位置&#xff1a;客户端缺点&#xff1a;不安全&#xff0c;信息可能会泄露 【2】session的形式 标识符&#xff0c;表示我是当前用户加密出来的数据对敏感信息进行加密…

github访问失败

1. 问题场景 今天了解到notepad可以安装许多插件&#xff0c;但是自动下载插件时总是失败&#xff0c;这些插件的下载源都是github&#xff0c;将地址复制到浏览器也打不开&#xff0c;所以查了下github的访问问题&#xff0c;目前插件已正常下载。 2. 解决方法 gitee上搜索…

[项目管理-33/创业之路-87/管理者与领导者-127]:如何提升自己项目管理的能力和水平

目录 前言&#xff1a; 一、项目经理的角色定位 1.1 项目经理的职责 1.2 不同矩阵类型的项目&#xff0c;项目经理的职责 1.3 项目经理的角色定位 1.4 项目经理的发展路径 二、项目经理项目理论和知识结构 三、软件项目经理在计算机水平的提升 四、项目经理业务知识的…

基于JavaWeb+SSM+Vue家庭记账本微信小程序系统的设计和实现

基于JavaWebSSMVue家庭记账本微信小程序系统的设计和实现 源码获取入口前言主要技术系统设计功能截图Lun文目录订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码获取入口 前言 1.1选题背景 互联网是人类的基本需求&#xff0c;特别是在现代社会&#xff0c;个人…

elasticsearch 7安装

问题提前报 max virtual memory areas error max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144] 如果您的环境是Linux&#xff0c;注意要做以下操作&#xff0c;否则es可能会启动失败 1 用编辑工具打开文件/etc/sysctl.conf 2 …

Eclipse常用设置-乱码

在用Eclipse进行Java代码开发时&#xff0c;经常会遇到一些问题&#xff0c;记录下来&#xff0c;方便查看。 一、properties文件乱码 常用的配置文件properties里中文的乱码&#xff0c;不利于识别。 处理流程&#xff1a;Window -> Preferences -> General -> Ja…

孟德尔随机化 MR入门基础-简明教程-工具变量-暴露

孟德尔随机化&#xff08;MR&#xff09;入门介绍和分章分享&#xff08;暂时不解读&#xff09; 大家好&#xff0c;孟德尔随机化大火&#xff0c;但是什么是孟德尔随机化&#xff0c;具体怎么实操呢 这没有其他教程的繁冗&#xff0c;我这篇讲最基础的孟德尔随机化的核心步…
最新文章