GAN系列学习之 cGAN 、lsGAN和DCGAN

cGAN

论文地址:cGAN
cGAN 即条件GAN,条件GAN其实理解起来非常的简单,其实就是对原来的G 和D 多加了一个label 的输入,这样就从无监督,变为了有监督。直接上公式,cGAN中,把公式由1)变为了2):
在这里插入图片描述
在这里插入图片描述
这里扩充,也不仅仅是class label ,可以是多种多样的信息,从这个角度看,后面GAN的许许多多变种都借鉴了cGAN。核心图如下:

在这里插入图片描述
其实没太多可讲的,下面来看下实现。

cGAN实现

在上篇GAN的代码实现的基础上,做一丢丢修改即可。
G 需要加上一个embedding层,embedding层可以理解为没有bia的线性层,权重也是可学习的。 BTW,embedding层一开始输入的都是one-hot的向量,这样就相当于一种查表,如1-10 one-hot后, 每个数字查出来的就是embedding层的每一行,维度为label_embed_dim的向量。

class Generator(nn.Module):
    def __init__(self, z_dim) -> None:
        super().__init__()
        self.mlp = nn.Sequential(
            nn.Linear(z_dim+label_embed_dim, 256),
            nn.BatchNorm1d(256),
            nn.GELU(),
            nn.Linear(256, 512),
            nn.BatchNorm1d(512),
            nn.GELU(),
            nn.Linear(512, 28*28),
            nn.Sigmoid()            
        )
        self.embedding = nn.Embedding(10, label_embed_dim)
    
    def forward(self, z, labels):
        # z (b, z_dim)
        label_embedding = self.embedding(labels)
        z = torch.cat((z, label_embedding), axis=-1)
        out_put = self.mlp(z)
        img = out_put.reshape(z.shape[0], *image_size)
        return img

D的添加是类似的,也是搞一个self.embedding 出来,过该层得到的label_embedding简单就是concatenate 起来使用。
使用环节,generator, discriminator 做相应修改即可。

lsGAN

文章地址: lsGAN
lsGAN 即Least Squares Generative Adversarial Networks 是2016年的一篇文章。 lsGAN 可以比原始的gan 生成更为清晰的,高分辨率的图片,而且稳定性更强,这里其实仅仅是更改了loss。原始的GAN中,用的是cross entropy,它容易带来梯度消失问题,尽管它能够做出正确的选择(分类),但是还是达不到正确的值(意指生成方向正确,但是清晰度不行)。 原因可以用下图解释,传统GAN在越过决策边界之后,梯度接近消失,很难在对的决策边界内进行学习(x=1是真值),b图确有良好的特性:
在这里插入图片描述
上图可以用一下的代码画出:

logits = torch.linspace(-10, 10, 200)
bce_fn = nn.BCELoss()
msc_fn = nn.MSELoss()
loss1 = []
loss2 = []
for logit in logits:
    loss1.append(bce_fn(torch.sigmoid(logit), torch.ones_like(logit)))
    loss2.append(msc_fn(logit, torch.ones_like(logit)))
plt.plot(logits, loss1, label="BCELoss")
plt.plot(logits, loss2, label="MCELoss")
plt.xlim(-10,10)
plt.ylim(0,16)
plt.legend()
plt.show()

图片为:
在这里插入图片描述

。ls GAN核心公式为:
在这里插入图片描述

推导

lsGAN的优化公式,可以写成:
在这里插入图片描述
第二行G的多了一项,但是无关紧要,因为它是一个常数,不影响最优值。
仿照GAN,同样地,固定G下,最优的D的参数,服从:
在这里插入图片描述
则(4)式中的G可以带入5式化简,得:
在这里插入图片描述
这里面变换不是特别复杂,(6)式子的最后,正好当b-c=1,b-a=2时候,优化的目标正是优化一个皮尔逊平方散度:
在这里插入图片描述
这是说,此时,pg优化的目标还是pd,学习起来完全没有问题。
常用取值,文章中给出了两种:
a=-1, b=1, c=0, 这个是按照 b-c=1,b-a=2 设定出来的。
另外一种是 c=b=-1, a=0设置,这种设置的考虑是,很多情况下,输出的值是0-1之间的数,且希望G生成的尽可能真,也就是D对真正的x,和G生成的x’ , 也就是G(z)打一样的分。实践中,这种收敛效果和上面的非常相似。

lsGAN实现

代码里面的改动,只需要 criterion = nn.MSELoss() 一行即可。

DC GAN

论文链接: DC GAN

DC GAN 是GAN系列里重要的奠基之作,解读的博客简直不要太多。上面的两篇,算是小细节的修改,而DCGAN几乎奠定了GAN的标准架构(当然GAN是开山鼻祖),文章的主要内容有以下几点:

  • CNN在有监督学习中效果非常不错,因此想结合CNN来做GAN。
  • 因为用了CNN,所以可以可视化一些filter,确实学到一些特定的东西。
  • 原来的GAN是比较不稳定的,这里面作者也做了不少尝试和设计,总结了在GAN里面使用CNN的有用的经验。
  • 生成器具有向量计算属性,这是说可以通过向量的操作,对应操作一些语义属性,这点也非常有用。

文章说 DC GAN 的结构指导经验是:
模型结构:

• 所有的pooling层使用strided卷积(判别器)和反卷积(生成器)进行替换
• 使用Batch Normalization
• 移除全连接的隐层,让网络可以更深
• 在生成器上,除了输出层使用Tanh外,其它所有层的激活函数都使用ReLU
• 判别器所有层的激活函数都使用LeakyReLU

在这里插入图片描述
上图的是G的设计,100维度的z,线性变换后,reshape 441024 反卷积得到 88512 -> 1616256 -> 3232128 -> 64643。
D的设计就是卷积,以及提到的一些细节。
一些训练的细节:

  • 没有对图片进行预处理, 除了将G的输出变换到[-1,1]。
  • 训练使用mini-batch SGD, batch size = 128。
  • 所有的参数都采用0均值,标准差为0.02的初始化方式。
  • leaky relu。leaky relu 的 α 的取值为0.2。
  • optimizers。Adam optimizer, 发现默认的学习率为0.001,太高调整为了0.0002。Adam中的momentum term β1 =0.9太高了,会使得训练过程震荡,不稳定,将其调整为0.5发现可以使训练过程更加稳定。

DC GAN 一些可视化结果

  • 连续隐变量下的变化
    就是对z进行插值,观察图片的变化,基本上都像bedroom。第六行,窗户逐渐出现,最后一行,TV逐渐消失变成窗户,还是挺有意思的。
    在这里插入图片描述

  • 特征图的可视化
    这个就不放图了,就是看到特征图在一些filter作用下,学到了窗户,床等一些结构。

  • 忘记画特定物体实验

作者做了一个小实验,从选出150个样本中,手动标注了52个窗口的bounding box,在倒数第二个卷积层,用LR拟合学习是否特征中有窗户,学习时候,bounding box外部的为正值,外部的为负值。这样就得到了一个对一个feature map 判断哪些像素是窗户的LR模型。生成时候,我们就可以选择移除,或者不移除feature map上有窗户的位置的特征,继续往下生成图像,得到下面的对比图,第一排是有窗户的,第二排是处理后生成的图片,网络删去了窗户,或者替换为了其他物体,这说明学到了窗户的特征。(ps 这个有点 patchGAN 的味道了 )
在这里插入图片描述

  • VECTOR ARITHMETIC ON FACE SAMPLES
    这块的意思,可以用vector(“King”)-vector(“Man”)+vector(“Woman”)的结果和向量Queen接近。向量的操作能带来语义上的改变,也能反应到图像上。文章指出,对单个样本进行操作的结果不是很稳定,而如果使用三个样本的平均值,结果就会好很多。
    在这里插入图片描述

DC GAN 的实现

DC GAN 的实现,改造其实蛮简单的,可以参考这篇博客,GAN优化方法-DCGAN案例
自己参考着实现了一下,但是这边就不贴自己的代码了,文章已经写得太长了,到此为止啦。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
生成对抗网络(GAN)是一种深度学习模型,通常用于生成具有特定属性的图像、音频和文本等内容。GAN使用两个神经网络:生成器和判别器。生成器从噪声中生成假数据,判别器则尝试区分真实数据和假数据。两个网络相互博弈,直到生成器可以生成无法被判别器区分的真实数据为止。 在GAN中引入注意力机制,可以帮助生成器更好地关注图像中的重要部分,从而生成更准确的图像。一种常见的注意力机制是self-attention,它可以帮助生成器更好地捕捉图像中的全局信息。下面是使用self-attention的GAN代码: ``` import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets from torchvision import transforms from torch.utils.data import DataLoader import torch.nn.functional as F class SelfAttention(nn.Module): def __init__(self, in_dim): super(SelfAttention, self).__init__() self.query_conv = nn.Conv2d(in_channels=in_dim, out_channels=in_dim//8, kernel_size=1) self.key_conv = nn.Conv2d(in_channels=in_dim, out_channels=in_dim//8, kernel_size=1) self.value_conv = nn.Conv2d(in_channels=in_dim, out_channels=in_dim, kernel_size=1) self.gamma = nn.Parameter(torch.zeros(1)) self.softmax = nn.Softmax(dim=-1) def forward(self, x): m_batchsize, C, width, height = x.size() proj_query = self.query_conv(x).view(m_batchsize, -1, width*height).permute(0, 2, 1) proj_key = self.key_conv(x).view(m_batchsize, -1, width*height) energy = torch.bmm(proj_query, proj_key) attention = self.softmax(energy) proj_value = self.value_conv(x).view(m_batchsize, -1, width*height) out = torch.bmm(proj_value, attention.permute(0, 2, 1)) out = out.view(m_batchsize, C, width, height) out = self.gamma*out + x return out class Generator(nn.Module): def __init__(self, latent_dim=100): super(Generator, self).__init__() self.latent_dim = latent_dim self.fc = nn.Sequential( nn.Linear(latent_dim, 256), nn.LeakyReLU(0.2, inplace=True), nn.Linear(256, 512), nn.LeakyReLU(0.2, inplace=True), nn.Linear(512, 1024), nn.LeakyReLU(0.2, inplace=True), ) self.conv = nn.Sequential( nn.ConvTranspose2d(1024, 512, 4, stride=2, padding=1), nn.BatchNorm2d(512), nn.LeakyReLU(0.2, inplace=True), nn.ConvTranspose2d(512, 256, 4, stride=2, padding=1), nn.BatchNorm2d(256), nn.LeakyReLU(0.2, inplace=True), SelfAttention(256), nn.ConvTranspose2d(256, 128, 4, stride=2, padding=1), nn.BatchNorm2d(128), nn.LeakyReLU(0.2, inplace=True), nn.ConvTranspose2d(128, 1, 4, stride=2, padding=1), nn.Tanh(), ) def forward(self, z): x = self.fc(z) x = x.view(-1, 1024, 1, 1) x = self.conv(x) return x class Discriminator(nn.Module): def __init__(self): super(Discriminator, self).__init__() self.conv = nn.Sequential( nn.Conv2d(1, 64, 4, stride=2, padding=1), nn.LeakyReLU(0.2, inplace=True), nn.Conv2d(64, 128, 4, stride=2, padding=1), nn.BatchNorm2d(128), nn.LeakyReLU(0.2, inplace=True), nn.Conv2d(128, 256, 4, stride=2, padding=1), nn.BatchNorm2d(256), nn.LeakyReLU(0.2, inplace=True), SelfAttention(256), nn.Conv2d(256, 512, 4, stride=2, padding=1), nn.BatchNorm2d(512), nn.LeakyReLU(0.2, inplace=True), nn.Conv2d(512, 1, 4, stride=1, padding=0), ) def forward(self, x): x = self.conv(x) x = x.view(-1, 1) return x # 训练GAN latent_dim = 100 batch_size = 64 epochs = 200 device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize([0.5], [0.5]), ]) train_dataset = datasets.MNIST('./data', train=True, download=True, transform=transform) train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True) generator = Generator(latent_dim).to(device) discriminator = Discriminator().to(device) optimizer_G = optim.Adam(generator.parameters(), lr=0.0002) optimizer_D = optim.Adam(discriminator.parameters(), lr=0.0002) criterion = nn.BCEWithLogitsLoss() real_label = 1 fake_label = 0 for epoch in range(epochs): for i, (real_images, _) in enumerate(train_loader): # 训练判别器 discriminator.zero_grad() real_images = real_images.to(device) batch_size = real_images.size(0) labels = torch.full((batch_size,), real_label, dtype=torch.float, device=device) output = discriminator(real_images) error_real = criterion(output, labels) error_real.backward() noise = torch.randn(batch_size, latent_dim, device=device) fake_images = generator(noise) labels.fill_(fake_label) output = discriminator(fake_images.detach()) error_fake = criterion(output, labels) error_fake.backward() optimizer_D.step() # 训练生成器 generator.zero_grad() labels.fill_(real_label) output = discriminator(fake_images) error_G = criterion(output, labels) error_G.backward() optimizer_G.step() if i % 100 == 0: print("[Epoch %d/%d] [Batch %d/%d] [D loss: %f] [G loss: %f]" % (epoch+1, epochs, i+1, len(train_loader), error_fake+error_real, error_G)) # 每个epoch结束后保存模型 torch.save(generator.state_dict(), 'gan_generator.pth') torch.save(discriminator.state_dict(), 'gan_discriminator.pth') ``` 在上面的代码中,我们定义了一个SelfAttention类来实现注意力机制,并将其添加到了生成器和判别器的架构中。在生成器中,我们添加了一个SelfAttention层来帮助生成器更好地捕捉图像中的全局信息。在判别器中,我们也添加了一个SelfAttention层来帮助判别器更好地关注图像中的重要部分。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值