Pytorch 使用GAN实现二次元人物头像生成 保姆级教程(数据集+实现代码+数学原理)

Pytorch 使用DCGAN实现二次元人物头像生成(实现代码+公式推导)

GAN介绍

  算法主体

  推导证明(之后将补全完整过程)

  随机梯度下降训练D,G

  DCGAN介绍及相关原理

Pytorch实现二次元人物头像生成

  如何使用GAN生成二次元头像

   数据准备

   代码实现

    判别、生成模型均一轮迭代

    判别每一轮迭代,生成模型每五轮迭代

  图片生成

总结

  本篇文章主要是关于李宏毅教授授课视频中的作业进行介绍,小白博主所作工作只是将现有的知识内容结合网路上一些优秀作者的总结以博文的形式加上自己的理解复述一遍。本文主要还是我的学习总结,因为网上的一些知识分布比较零散故作整理叙述。如果有不对的地方,还请帮忙指正,如有出现禁止转载的图片,文字内容请联系我修改。
 
相关参考:
 
  李宏毅2020机器学习深度学习(完整版)国语:链接: link.

  何之源:GAN学习指南:从原理入门到制作生成Demo链接: link

   张先生-您好.Pytorch实现GAN 生成动漫头像 链接: link.

  Dean0Winchester.深度学习—图像卷积与反卷积(最完美的解释)链接: link.

GAN介绍

  生成对抗网络 G A N GAN GAN, G e n e r a t i v e A d v e r s a r i a l N e t w o r k s Generative Adversarial Networks GenerativeAdversarialNetworks )是深度学习中一种无监督(即不需要给样本数据打标签)的学习方法, G A N GAN GAN的核心思想源自博弈论中的纳什均衡,博弈的双方分别是 G e n e r a t o Generato Generator(负责生成图片的生成器 G G G)和 D i s c r i m i n a t o r Discriminator Discriminator(负责判断图片真假的判别器 D D D)。 D D D的目的在于揪出由 G G G “伪造”的“假”图片给它打低分, G G G的目的在于尽最大可能地模仿真图片从而“欺骗” D D D获取高分。

  参与这场游戏双方再不断地较量中完成自身地优化,从而实现了各自判别能力和生成能力的提升,直到双方达到一种动态的平衡。
在这里插入图片描述
 
   如上图所示,蝴蝶扮演 G A N GAN GAN中的生成器,而波波鸟则扮演判别器,蝴蝶为了逃避波波鸟(判别器)的捕食(识别)需要不断的朝着树叶(真样本)进化,而波波鸟为了能够捕食(识别)出蝴蝶(用假样本伪装自己的生成器)也需要不断进化,比方说,最开始波波鸟理解的叶子(真样本)只停留在不是彩色这一层面上,随着训练,波波鸟的认知开始进化,从不是彩色转向棕色是叶子(真样本),而蝴蝶得到反馈之后为了继续欺骗波波鸟开始学会让生成的翅膀是棕色。
 
   这个过程将反复进行下去,最后可以达到,波波鸟可以真正认出叶子,蝴蝶可以让生成的翅膀(假样本)与叶子(真样本)近乎一致为止。

算法主体

算法总流程:

   G A N GAN GAN算法简单来说,就是固定生成器,训练判别器;然后固定训练好的判别器,再训练生成器的反复过程。

在这里插入图片描述
逐条解读算法流程:
 
  首先初始化 D D D, G G G的参数,先固定生成器,进入判别器的学习过程。
 
    判别器学习过程

  1. 采样 :从数据集中抽样出 m m m张样本,同时借助某种分布(通常使用正态分布)生成 m m m n n n维噪声样本。

  2. 获取假样本:将噪声样本作为输入,得到生成器输出的假样本。

  3. 判别器D的目标函数 :直观上来看,我们要达到的目的,就是让判别器对生成器生成的假样本尽可能地严格,对数据集抽样出来的真样本尽可能地宽松,那么我们可以构建这样一个目标函数,让判别器给真样本尽可能高的分数,给假样本尽可能低的分数。
     
                m a x D max_D maxD v ∗ v^* v = 1 m \dfrac{1}{m} m1 ∑ i = 1 m \sum_{i=1}^{m} i=1m l o g log log( D D D( x i x^i xi)) + + + 1 m \dfrac{1}{m} m1 ∑ i = 1 m \sum_{i=1}^{m} i=1m l o g log log( D D D(1 − - x ∗ i x*^i xi))
     
       D D D( x x x)代表 x x x为真实图片的概率,如果为 1 1 1,就代表100%是真实的图片,而输出为 0 0 0,就代表不可能是真实的图片。代码实现中通常借由 s i g m o i d sigmoid sigmoid函数来表达
     
      要让 l o g log log( D D D( x ∗ i x*^i xi))尽可能小,等效于让 l o g log log( D D D(1 − - x ∗ i x*^i xi))尽可能大。以此为我们的目的函数进行求导做梯度上升(亦可加负号求 m i n min min做梯度下降)从而更新判别器参数。
     
     
    生成器学习过程

  4. 噪声采样 :借助同种分布(通常使用正态分布)生成另外 m m m n n n维噪声样本。

  5. 生成器G的目标函数
     
                               v ∗ v^* v = 1 m \dfrac{1}{m} m1 ∑ i = 1 m \sum_{i=1}^{m} i=1m l o g log log( D D D( G G G( z i z^i zi)))
     
      先直观的理解为什么要这样定义 G G G目标函数,根据之前的分析,要让生成器生成的图片足够接近真样本,那么就需要骗过判别器,从而我们可以让最大化生成图片在 D D D的打分为目标来引导我们的生成器完成参数迭代。
     
       在最理想的状态下, G G G可以生成足以“以假乱真”的图片 G G G( z z z)。对于 D D D来说,它难以判定 G G G生成的图片究竟是不是真实的,因此 D D D( G G G( z z z)) = 0.5 0.5 0.5

推导证明

  说了那么多,那么我们如何用一条数学语言来描述 G A N GAN GAN的核心原理呢?
 
   G e n e r a t i v e A d v e r s a r i a l N e t w o r k s Generative Adversarial Networks GenerativeAdversarialNetworks 的作者 I a n Ian Ian G o o d f e l l o w Goodfellow Goodfellow大佬给出了答案:

在这里插入图片描述
分析这个公式:
 
1.整个式子由两项构成。 x x x表示真实图片, z z z表示输入 G G G网络的噪声,而 G G G( z z z)表示 G G G网络生成的图片。
 
2. D D D( x x x)表示 D D D网络判断真实图片是否真实的概率(因为 x x x就是真实的,所以对于 D D D来说,这个值越接近 1 1 1越好)。而 D D D( G G G( z z z))是 D D D网络判断 G G G生成的图片的是否真实的概率。
 
3. D D D的目的: D D D的能力越强, D D D( x x x)应该越大, D D D( G G G( x x x))应该越小。这时 V V V( D D D, G G G)会变大。因此式子对于 D D D来说是求最大( m a x max max D D D)
 
4. G G G的目的: 上面提到过, D D D( G G G( z z z))是 D D D网络判断 G G G生成的图片是否真实的概率, G G G应该希望自己生成的图片“越接近真实越好”。也就是说,(实际上,我们不是同时训练两者,当 D D D训练好时,第一项好样本的得分期望是定值), G G G希望 D D D( G G G( z z z))尽可能得大,那么 V V V( D D D, G G G)会变小。因此我们看到式子的最前面的记号是 m i n min min G G G
 
5. 为什么是期望: 我们假设我们取出的样本的统计特性可以代表整个数据分布(包括数据集数据分布和 G G G生成器生成数据的分布),那么我们之前表示的均值可以近似等价于各自数据分布的期望。
 
我们可以用张形象的图来描述我们是如何选择我们的生成器和判别器的。
 
在这里插入图片描述
 
   我们选取判别器的标准,是取 m a x max max V V V( D D D, G G G),那么对应到图片就是找到函数的最高点,在我们完成判别器的选取后(即找到函数最高点后),我们下一步的目标(更新生成器),就是找出 m i n min min m a x max max V V V( D D D, G G G)(此时 m a x max max V V V( D D D, G G G)即为红点),我们要做的就是选取红点中最低的,选出的 G i G_i Gi即为我们想要的生成器。

随机梯度下降训练D,G

   随机梯度下降法用于减少陷入局部最优的风险,,进而可以更接近全局最优的方法。
   该怎么做,论文也给出了算法(不愧是大佬),本文代码使用的是 A d a m 优 化 算 法 Adam优化算法 Adam(利用”惯性“来跳出局部最优的方法),此处不做详细说明:
在这里插入图片描述
  这里红框圈出的部分是我们要额外注意的。第一步我们训练 D D D D D D是希望 V V V( G G G, D D D)越大越好,所以是加上梯度( a s c e n d i n g ascending ascending)。第二步训练 G G G时, V V V( G G G, D D D)越小越好,所以是减去梯度( d e s c e n d i n g descending descending)。整个训练过程交替进行。

DCGAN介绍及相关原理

 
  使用过 C N N CNN CNN的朋友一定不会对运用 C N N CNN CNN进行图像样本分类的方法感到陌生,我们很容易就会想到, G A N GAN GAN中的判别器是否可以用 C N N CNN CNN做二元分类器来进行替代,那么如何把 C N N CNN CNN G A N GAN GAN很好的结合起来?
 
U n s u p e r v i s e d R e p r e s e n t a t i o n L e a r n i n g w i t h D e e p C o n v o l u t i o n a l G e n e r a t i v e A d v e r s a r i a l N e t w o r k s Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks UnsupervisedRepresentationLearningwithDeepConvolutionalGenerativeAdversarialNetworks》论文告诉了我们答案。
 
  论文作者将 G A N GAN GAN中的生成器,判别器都用两个卷积神经网络来进行替换。用二元分类的 C N N CNN CNN来做判别器,用反卷积代替卷积操作的卷积神经网络来做生成器,反卷积操作可简单理解为往原有的小像素矩阵中进行填充,之后再进行卷积操作,从而实现从”小到大”(从而可以实现用一组列向量生成一个像素矩阵)的变化。
 
在这里插入图片描述
D C G A N DCGAN DCGAN对卷积神经网络的结构做了一些改变,以提高样本的质量和收敛的速度,这些改变有:

1.取消所有 p o o l i n g pooling pooling层。 G G G网络中使用转置卷积( t r a n s p o s e d c o n v o l u t i o n a l l a y e r transposed convolutional layer transposedconvolutionallayer)进行上采样, D D D网络中用加入 s t r i d e stride stride的卷积代替 p o o l i n g pooling pooling
2.在 D D D G G G中均使用 b a t c h n o r m a l i z a t i o n batch normalization batchnormalization
3.去掉 F C FC FC层,使网络变为全卷积网络
4. G G G网络中使用 R e L U ReLU ReLU作为激活函数,最后一层使用 t a n h tanh tanh
5. D D D网络中使用 L e a k y R e L U LeakyReLU LeakyReLU作为激活函数

D C G A N DCGAN DCGAN中的 G G G网络示意:
在这里插入图片描述

Pytorch实现二次元人物头像生成

 

如何使用GAN生成二次元头像

 
  借助我们之前的推导快速地理解一下动漫头像生成的 G A N GAN GAN原理。如下图所示,判别器将数据集中的采样做真样本,由噪声采样经生成器反卷积生成的图像做假样本进行每代的更新,生成器则以让判别器尽可能地给自己生成的图像打高分为目的进行模型的更新迭代。
 
在这里插入图片描述
 
  我们也可以用合作的思想去理解生成器同判别器的关系:
 在这里插入图片描述
 
  初始化两个模型后,再依照我们之前提及的模型 G G G, D D D固定一方,更新另一方的方法进行反复迭代。数据集提供的样本做真样本,由 G G G生成的样本做假样本。
 在这里插入图片描述
 
  之所以要这样做,是因为,如果我们只用数据集抽样出的样本训练 D D D,(而我们收集的图片都是由真人绘制的好图片),换言之,我们只能为判别器提供标签为 1 1 1的样本,除此之外没有反例,这样训练出来的 D D D从来没有见过反例,此时给他一张由 G G G生成的假图片,它也会给它打高分,可以这样理解,在它看来假图片和真图片都是图片,都应该打高分( D D D并没有学会我们想要它学会的特征)。
 在这里插入图片描述
 
  所以,负面的例子对判别模型来说至关重要,于是我们可以让数据集中抽样出来的样本做真样本(得分为1,real),让生成器生成的样本做假样本(得分为0,fake)。
 在这里插入图片描述
 
   生成算法:给出一系列的正例,以及随机生成的反例。在每一个轮次中让 D D D学习这些正,反例,训练好判别器之后,固定住。接着训练我们的生成器,使它生成的反例能够尽可能地在判别器中得高分。
 在这里插入图片描述
 
   算法主体:和之前提及的一样,初始参数,固定一方,训练另一方,让 G G G学会“欺骗”判别器。
 在这里插入图片描述
 
  另一种思路,我们可以认为所有的画手在画二次元美少女的时候都服从一种固定的规律,比如眼睛的位置,头发的走向,依照一些让人感觉舒服的比例画出好的图片,又因为机器理解图片是借助像素矩阵实现的(所以你喜欢的不是二次元美少女是矩阵啊),用数学的思维来理解,就是所有的美少女头像都是服从一种复杂分布的。如图所示,我们在这个分布里面取样本可以得到我们想要的五官分布正常的好样本,而脱离这个分布之外,我们可以获取各种“抽象派”画作。
 在这里插入图片描述
 
  那么生成器 G G G要做的就是找出这个分布,并完成对它的模拟,从而让随机噪声经过生成器后能够得到满足这个好分布的图像。
 
 
  那么我们可以从极大似然估计的方法去计算这个分布的参数,通过图中的数学变化可以发现,求参数的最大似然等价于求 P d a t a P_data Pdata分布与 P G P_G PG分布的 K L KL KL散度。那么我们求似然最大就等价于求散度最小。
 
在这里插入图片描述
 
  同时我们再来看判别器的目标函数,经过求导带入极值点后我们发现,判别器的目标函数最大值就是两个 K L KL KL散度之和。从而判别器,生成器联系了起来,生成器的目标函数可以从 m i n min min K L KL KL改写成 m i n min min m a x max max V V V ( G G G, D D D)。到这各位可以回过头去再看看推到证明中的那个式子。
 在这里插入图片描述

 

数据准备

 
  本次采用的数据集约有 16000 16000 16000张,像素为96X96,可以看到一些熟悉的角色(诸如凉宫,长门),下文提供下载链接,网路上也有很多其他的数据集,诸君也可以借助爬虫放自己喜欢的角色进去(京都粉可以尝试爬京都的作画,生成图片会比较接近京都脸)。
 

在这里插入图片描述
 
网盘链接:链接:https://pan.baidu.com/s/1MFulwMQJ78U2MCqRUYjkMg
提取码:58v6
复制这段内容后打开百度网盘手机App,操作更方便哦
 

代码实现

代码部分主要参考:张先生-您好.Pytorch实现GAN 生成动漫头像

from tqdm import tqdm
import torch
import torchvision as tv
from torch.utils.data import DataLoader
import torch.nn as nn# config类中定义超参数,
class Config(object):"""定义一个配置类"""# 0.参数调整data_path = '/root/PycharmProjects/untitled/'virs = "result"num_workers = 4  # 多线程img_size = 96  # 剪切图片的像素大小batch_size = 256  # 批处理数量max_epoch = 400   # 最大轮次lr1 = 2e-4  # 生成器学习率lr2 = 2e-4  # 判别器学习率beta1 = 0.5  # 正则化系数,Adam优化器参数gpu = False  # 是否使用GPU运算(建议使用)nz = 100  # 噪声维度ngf = 64  # 生成器的卷积核个数ndf = 64  # 判别器的卷积核个数# 1.模型保存路径save_path = 'imgs2/'  # opt.netg_path生成图片的保存路径# 判别模型的更新频率要高于生成模型d_every = 1  # 每一个batch 训练一次判别器g_every = 5  # 每1个batch训练一次生成模型save_every = 5  # 每save_every次保存一次模型netd_path = Nonenetg_path = None# 测试数据gen_img = "result.png"# 选择保存的照片# 一次生成保存64张图片gen_num = 64gen_search_num = 512gen_mean = 0    # 生成模型的噪声均值gen_std = 1     # 噪声方差# 实例化Config类,设定超参数,并设置为全局参数
opt = Config()# 定义Generation生成模型,通过输入噪声向量来生成图片
class NetG(nn.Module):# 构建初始化函数,传入opt类def __init__(self, opt):super(NetG, self).__init__()# self.ngf生成器特征图数目self.ngf = opt.ngfself.Gene = nn.Sequential(# 假定输入为opt.nz*1*1维的数据,opt.nz维的向量# output = (input - 1)*stride + output_padding - 2*padding + kernel_size# 把一个像素点扩充卷积,让机器自己学会去理解噪声的每个元素是什么意思。nn.ConvTranspose2d(in_channels=opt.nz, out_channels=self.ngf * 8, kernel_size=4, stride=1, padding=0, bias =False),nn.BatchNorm2d(self.ngf * 8),nn.ReLU(inplace=True),# 输入一个4*4*ngf*8nn.ConvTranspose2d(in_channels=self.ngf * 8, out_channels=self.ngf * 4, kernel_size=4, stride=2, padding=1, bias =False),nn.BatchNorm2d(self.ngf * 4),nn.ReLU(inplace=True),# 输入一个8*8*ngf*4nn.ConvTranspose2d(in_channels=self.ngf * 4, out_channels=self.ngf * 2, kernel_size=4, stride=2, padding=1,bias=False),nn.BatchNorm2d(self.ngf * 2),nn.ReLU(inplace=True),# 输入一个16*16*ngf*2nn.ConvTranspose2d(in_channels=self.ngf * 2, out_channels=self.ngf, kernel_size=4, stride=2, padding=1, bias =False),nn.BatchNorm2d(self.ngf),nn.ReLU(inplace=True),# 输入一张32*32*ngfnn.ConvTranspose2d(in_channels=self.ngf, out_channels=3, kernel_size=5, stride=3, padding=1, bias =False),# Tanh收敛速度快于sigmoid,远慢于relu,输出范围为[-1,1],输出均值为0nn.Tanh(),)# 输出一张96*96*3def forward(self, x):return self.Gene(x)# 构建Discriminator判别器
class NetD(nn.Module):def __init__(self, opt):super(NetD, self).__init__()self.ndf = opt.ndf# DCGAN定义的判别器,生成器没有池化层self.Discrim = nn.Sequential(# 卷积层# 输入通道数in_channels,输出通道数(即卷积核的通道数)out_channels,此处设定filer过滤器有64个,输出通道自然也就是64。# 因为对图片作了灰度处理,此处通道数为1,# 卷积核大小kernel_size,步长stride,对称填0行列数padding# input:(bitch_size, 3, 96, 96),bitch_size = 单次训练的样本量# output:(bitch_size, ndf, 32, 32), (96 - 5 +2 *1)/3 + 1 =32# LeakyReLu= x if x>0 else nx (n为第一个函数参数),开启inplace(覆盖)可以节省内存,取消反复申请内存的过程# LeakyReLu取消了Relu的负数硬饱和问题,是否对模型优化有效有待考证nn.Conv2d(in_channels=3, out_channels= self.ndf, kernel_size= 5, stride= 3, padding= 1, bias=False),nn.LeakyReLU(negative_slope=0.2, inplace= True),# input:(ndf, 32, 32)nn.Conv2d(in_channels= self.ndf, out_channels= self.ndf * 2, kernel_size= 4, stride= 2, padding= 1, bias=False),nn.BatchNorm2d(self.ndf * 2),nn.LeakyReLU(0.2, True),# input:(ndf *2, 16, 16)nn.Conv2d(in_channels= self.ndf * 2, out_channels= self.ndf *4, kernel_size= 4, stride= 2, padding= 1,bias=False),nn.BatchNorm2d(self.ndf * 4),nn.LeakyReLU(0.2, True),# input:(ndf *4, 8, 8)nn.Conv2d(in_channels= self.ndf *4, out_channels= self.ndf *8, kernel_size= 4, stride= 2, padding= 1, bias=False),nn.BatchNorm2d(self.ndf *8),nn.LeakyReLU(0.2, True),# input:(ndf *8, 4, 4)# output:(1, 1, 1)nn.Conv2d(in_channels= self.ndf *8, out_channels= 1, kernel_size= 4, stride= 1, padding= 0, bias=True),# 调用sigmoid函数解决分类问题# 因为判别模型要做的是二分类,故用sigmoid即可,因为sigmoid返回值区间为[0,1],# 可作判别模型的打分标准nn.Sigmoid())def forward(self, x):# 展平后返回return self.Discrim(x).view(-1)def train(**kwargs):# 配置属性# 如果函数无字典输入则使用opt中设定好的默认超参数for k_, v_ in kwargs.items():setattr(opt, k_, v_)# device(设备),分配设备if opt.gpu:device = torch.device("cuda")else:device = torch.device('cpu')# 数据预处理1# transforms 模块提供一般图像转换操作类的功能,最后转成floatTensor# tv.transforms.Compose用于组合多个tv.transforms操作,定义好transforms组合操作后,直接传入图片即可进行处理# tv.transforms.Resize,对PIL Image对象作resize运算, 数值保存类型为float64# tv.transforms.CenterCrop, 中心裁剪# tv.transforms.ToTensor,将opencv读到的图片转为torch image类型(通道,像素,像素),且把像素范围转为[0,1]# tv.transforms.Normalize,执行image = (image - mean)/std 数据归一化操作,一参数是mean,二参数std# 因为是三通道,所以mean = (0.5, 0.5, 0.5),从而转成[-1, 1]范围transforms = tv.transforms.Compose([# 3*96*96tv.transforms.Resize(opt.img_size),   # 缩放到 img_size* img_size# 中心裁剪成96*96的图片。因为本实验数据已满足96*96尺寸,可省略tv.transforms.CenterCrop(opt.img_size),# ToTensor 和 Normalize 搭配使用tv.transforms.ToTensor(),tv.transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])# 加载数据并使用定义好的transforms对图片进行预处理,这里用的是直接定义法# dataset是一个包装类,将数据包装成Dataset类,方便之后传入DataLoader中# 写法2:# 定义类Dataset(Datasets)包装类,重写__getitem__(进行transforms系列操作)、__len__方法(获取样本个数)# ### 两种写法有什么区别dataset = tv.datasets.ImageFolder(root=opt.data_path, transform=transforms)# 数据预处理2# 查看drop_last操作,dataloader = DataLoader(dataset,      # 数据加载batch_size=opt.batch_size,    # 批处理大小设置shuffle=True,     # 是否进行洗牌操作#num_workers=opt.num_workers,     # 是否进行多线程加载数据设置drop_last=True           # 为True时,如果数据集大小不能被批处理大小整除,则设置为删除最后一个不完整的批处理。)# 初始化网络netg, netd = NetG(opt), NetD(opt)# 判断网络是否有权重数值# ### storage存储map_location = lambda storage, loc: storage# torch.load模型加载,即有模型加载模型在该模型基础上进行训练,没有模型则从头开始# f:类文件对象,如果有模型对象路径,则加载返回# map_location:一个函数或字典规定如何remap存储位置# net.load_state_dict将加载出来的模型数据加载到构建好的net网络中去if opt.netg_path:netg.load_state_dict(torch.load(f=opt.netg_path, map_location=map_location))if opt.netd_path:netd.load_state_dict(torch.load(f=opt.netd_path, map_location=map_location))# 搬移模型到之前指定设备,本文采用的是cpu,分配设备netd.to(device)netg.to(device)# 定义优化策略# torch.optim包内有多种优化算法,# Adam优化算法,是带动量的惯性梯度下降算法optimize_g = torch.optim.Adam(netg.parameters(), lr=opt.lr1, betas=(opt.beta1, 0.999))optimize_d = torch.optim.Adam(netd.parameters(), lr=opt.lr2, betas=(opt.beta1, 0.999))# 计算目标值和预测值之间的交叉熵损失函数# BCEloss:-w(ylog x +(1 - y)log(1 - x))# y为真实标签,x为判别器打分(sigmiod,1为真0为假),加上负号,等效于求对应标签下的最大得分# to(device),用于指定CPU/GPUcriterions = nn.BCELoss().to(device)# 定义标签,并且开始注入生成器的输入noisetrue_labels = torch.ones(opt.batch_size).to(device)fake_labels = torch.zeros(opt.batch_size).to(device)# 生成满足N(1,1)标准正态分布,opt.nz维(100维),opt.batch_size个数的随机噪声noises = torch.randn(opt.batch_size, opt.nz, 1, 1).to(device)# 用于保存模型时作生成图像示例fix_noises = torch.randn(opt.batch_size, opt.nz, 1, 1).to(device)# 训练网络# 设置迭代for epoch in range(opt.max_epoch):# tqdm(iterator()),函数内嵌迭代器,用作循环的进度条显示for ii_, (img, _) in tqdm((enumerate(dataloader))):# 将处理好的图片赋值real_img = img.to(device)# 开始训练生成器和判别器# 注意要使得生成的训练次数小于一些# 每一轮更新一次判别器if ii_ % opt.d_every == 0:# 优化器梯度清零optimize_d.zero_grad()# 训练判别器# 把判别器的目标函数分成两段分别进行反向求导,再统一优化# 真图# 把所有的真样本传进netd进行训练,output = netd(real_img)# 用之前定义好的交叉熵损失函数计算损失error_d_real = criterions(output, true_labels)# 误差反向计算error_d_real.backward()# 随机生成的假图# .detach() 返回相同数据的 tensor ,且 requires_grad=False#  .detach()做截断操作,生成器不记录判别器采用噪声的梯度noises = noises.detach()# 通过生成模型将随机噪声生成为图片矩阵数据fake_image = netg(noises).detach()# 将生成的图片交给判别模型进行判别output = netd(fake_image)# 再次计算损失函数的计算损失error_d_fake = criterions(output, fake_labels)# 误差反向计算# 求导和优化(权重更新)是两个独立的过程,只不过优化时一定需要对应的已求取的梯度值。# 所以求得梯度值很关键,而且,经常会累积多种loss对某网络参数造成的梯度,一并更新网络。error_d_fake.backward()‘’‘关于为什么要分两步计算loss:我们已经知道,BCEloss相当于计算对应标签下的得分,那么我们把真样本传入时,因为标签恒为1,BCE此时只有第一项,即真样本得分项要补齐成前文提到的判别器目标函数,需要再添置假样本得分项,故两次分开计算梯度,各自最大化各自的得分(假样本得分是log(1-D(x)))再统一进行梯度下降即可’‘’# 计算一次Adam算法,完成判别模型的参数迭代# 多个不同loss的backward()来累积同一个网络的grad,计算一次Adam即可optimize_d.step()# 训练判别器if ii_ % opt.g_every == 0:optimize_g.zero_grad()# 用于netd作判别训练和用于netg作生成训练两组噪声需不同noises.data.copy_(torch.randn(opt.batch_size, opt.nz, 1, 1))fake_image = netg(noises)output = netd(fake_image)# 此时判别器已经固定住了,BCE的一项为定值,再求最小化相当于求二项即G得分的最大化error_g = criterions(output, true_labels)error_g.backward()# 计算一次Adam算法,完成判别模型的参数迭代optimize_g.step()# 保存模型if (epoch + 1) % opt.save_every == 0:fix_fake_image = netg(fix_noises)tv.utils.save_image(fix_fake_image.data[:64], "%s/%s.png" % (opt.save_path, epoch), normalize=True)torch.save(netd.state_dict(),  'imgs2/' + 'netd_{0}.pth'.format(epoch))torch.save(netg.state_dict(),  'imgs2/' + 'netg_{0}.pth'.format(epoch))# @torch.no_grad():数据不需要计算梯度,也不会进行反向传播
@torch.no_grad()
def generate(**kwargs):# 用训练好的模型来生成图片for k_, v_ in kwargs.items():setattr(opt, k_, v_)device = torch.device("cuda") if opt.gpu else torch.device("cpu")# 加载训练好的权重数据netg, netd = NetG(opt).eval(), NetD(opt).eval()#  两个参数返回第一个map_location = lambda storage, loc: storage# opt.netd_path等参数有待修改netd.load_state_dict(torch.load('imgs2/netd_399.pth', map_location=map_location), False)netg.load_state_dict(torch.load('imgs2/netg_399.pth', map_location=map_location), False)netd.to(device)netg.to(device)# 生成训练好的图片# 初始化512组噪声,选其中好的拿来保存输出。noise = torch.randn(opt.gen_search_num, opt.nz, 1, 1).normal_(opt.gen_mean, opt.gen_std).to(device)fake_image = netg(noise)score = netd(fake_image).detach()# 挑选出合适的图片# 取出得分最高的图片indexs = score.topk(opt.gen_num)[1]result = []for ii in indexs:result.append(fake_image.data[ii])# 以opt.gen_img为文件名保存生成图片tv.utils.save_image(torch.stack(result), opt.gen_img, normalize=True, range=(-1, 1))def main():# 训练模型train()# 生成图片generate()if __name__ == '__main__':main()

 
 

判别、生成模型均一轮迭代,200轮次

 
50 50 50轮展示一次
 
 
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

 

判别每一轮迭代,生成模型每五轮迭代,400轮次

 
 
100 100 100轮展示一次
 
 
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

 
 
在这里插入图片描述
 
 
理论上,生成器的更新频率应该小于判别器,因为生成器变化太快,会导致 V V V( G G G, D D D)变化剧烈,从而最开始的最高点不再满足最高要求。
 
 

图片生成

 
  用训练好的生成器,随机输入一组噪声,生成结果如下。效果不理想,希望大佬们能够给出意见让我能够学习改进一下。(找到原因了,数据集太小了,目前在收集数据中,之后更新此处)
 
在这里插入图片描述

总结

 
  博主第一次写博文,很多不足之处希望多多包涵,之后会陆续补齐李教授的其他作业讲解,希望能够帮助到一些和我一样刚入门的朋友,也希望大佬可以帮忙提意见帮助我改进不足之处。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GAN动漫人像生成实现(附带源码)

基于生成对抗网络的人像转卡通像实现 写在前面先来看看效果吧大家最想要的项目原理简介(2021/11/16更)本项目C和Pytorch的OnnxRuntime使用方法(2022/1/3更)APP的代码(2022/4/4更)双城之战风格迁移数据集写在后面 写在前面 天气晴朗万物可爱,希望通过这篇文章对大家…

NVIDIA的StyleGAN、StyleGAN2、StyleGAN3系列论文解读,梳理基于风格的生成器架构

通俗来讲就是,张三造假币(Generator生成器),然后用验钞机去验证真假(Discriminator辨别器),如果是假的就继续提高造假技术,直到验钞机检验不出来为止,也就是说一个造假一个验假(验钞机也需升级),两者互相学…

AI绘画突然爆火?快速体验二次元画师NovelAI(diffusion)

目录 0 写在前面1 diffusion vs GAN2 NovelAI3 AI绘画环境搭建4 体验AI创作 0 写在前面 机器学习强基计划聚焦深度和广度,加深对机器学习模型的理解与应用。“深”在详细推导算法模型背后的数学原理;“广”在分析多个机器学习模型:决策树、支…

女神青涩时纤毫毕现,腾讯 AI 模型 GFPGAN 火上 GitHub 热榜第一,Demo 在线可玩

来源:量子位 作者:明敏 包浆老图立刻清晰到头发丝是一种怎样的体验?看这明亮的眼眸、清晰的发丝,原本模糊的人像立刻添了几分灵动(这就是用 AI 修复的高圆圆童年照)。 甚至时间更久远的历史名人照片也能搞…

游戏原画师

常用软件: 1. Photoshop著名的图形处理软件,也能绘画,适合绘画功底一般的初学者。 2. Painter很多专业人士都很爱用的一款软件。笔刷很多,适合有一定美术功底的人。 3. Sai画漫画的话,sai就比较好用了,对…

chatgpt赋能python:Python与前端连接:使用Python的Web框架构建后端API

Python与前端连接:使用Python的Web框架构建后端API Python是一种强大的编程语言,越来越受到开发者的欢迎。但是,对于Web开发,Python并不是一种前端语言。那么,如何将Python的后端与前端连接起来?本文将介绍…

Vue前端Flask后端

项目构建 本科期间因为学过相关课程,已经配好了相关的环境,所以直接进行了版本确认。 一、Vue2 1. 前端环境确认 Vue:3.2.47 | 踩过的坑使用1的时候一直为empty,加 -g仍是empty 在项目下1,非项目下要用2 1$ npm in…

chatgpt赋能python:Python前端与后端的数据交互

#Python前端与后端的数据交互 在现代Web应用程序中,前端与后端的数据交互至关重要。前端可能需要向后端发出请求来获取数据或触发某些操作,而后端则需要向前端返回数据或处理请求并采取适当的措施。Python是一种流行的编程语言,用于开发Web应…

上货避坑指南 私域上货选品工具 无货源选品上货 采集商品详情数据API分享 详情图 sku信息

电商开店之后,第一件事就是上货了,上货其实也是有技巧的。 上传商品时我们一定要注意细节,不可忽略一些重要细节,所以商家们在上传商品前,不可忽略是否预售、标题、主图、详情页、保证金、上架时间这几个细节。 详情…

批发供应系统批发订货发货管理系统开发功能

​ 古人说的一点都没错,不同行业的贸易企业看起来都是在卖货,但业务流程的确有很大的不同,而在疫情和现动态的双重打击下,传统商贸企业迫切需要面临转型。 1)在哪里转弯。怎么转。 专业点说是转型&#xff0c…

微商助理 防伪防窜货溯源代理授权查询系统源码

源码介绍 微商助理防伪防窜货溯源代理授权查询系统源码支持二维码扫码查询 程序架构:PHPMysql PHP版本要求PHP5.4以上(推荐PHP5.4) 把源码文件上传到网站根目录,此套系统功能较多,不推荐二级目录安装 安装方法一…

2021-07-10

基础研究方面有中科院、中科大、清华、南大、山西大学、北邮、上海交大等支持,设备研发由国盾量子、问天量子、科易理想量子、济南量子技术研究院、九州量子、中兴、华为等支持,量子网络、中创为量子、神州量子、亨通问天量子、神州信息等提供建设运维&a…

OpenCV:鼠标画直线并显示坐标

【天问】存于咸阳宫内,为秦始皇的配剑。当一个人成为天下至尊,权利已是他手中最锋利的宝剑,所谓“天问”不正是一种举剑问天、舍我其谁的王者气势的体现吗? 【鼠标操作】 1、回调函数 CV_EXPORTS void setMouseCallback(const string&…

神七问天 书中国航天史诗

2008年9月25日晚上9时10分,“神七”腾空而起,不到半个小时,北京航天飞控中心宣布“神七”发射成功。执行神舟七号载人航天飞行任务的航天员翟志刚、刘伯明、景海鹏出征前,向祖国和人民致以庄严的军礼。 “神舟七号”终于揭开了神秘…

C++ 组合模式

作者: 一去、二三里 个人微信号: iwaleon 微信公众号: 高效程序员 组合模式(Composite Pattern)组合多个对象形成树形结构以表示具有“整体-部分”关系的层次结构。组合模式对单个对象(即:叶子构件)和组合对象(即:容器构件)的使用具有一致性,组合模式又被称为“整体…

分享打造爆款书的方法,同时聊聊出版图书中的哪些事和哪些坑

想想若干年前,我通过一位非常热情的图书公司的编辑,出版了我的第一本计算机图书,从此后一发不可收拾,最近2年出版了2本,有1本已经完稿,还有两本尚在写作途中。确实,出版出一件名利双收的事&…

腾讯面经来了

前言 今天给大家分享自己投递腾讯上海IEG后台开发职位的面试经历分享。 腾讯感觉整个流程比较慢,这周才刚刚走完HR面,一二两轮技术面在同一周,三面隔一周,HR面又隔一周,但是每轮面试官的态度还是非常棒的,面…

AliCoCo:阿里电商知识图谱核心技术揭秘 Alibaba E-commerce Cognitive Concept Net

电商技术进入认知智能时代,在搜索推荐等电商核心业务场景上使用电商认知图谱,将给亿万用户带来更加智能的购物体验。 本文介绍 AliCoCo 的背景、定义、底层设计、构建过程中的一些算法问题,以及在电商搜索和推荐上的广泛应用,并分…

天池NLP大赛来了!

Datawhale赛事 主办方:阿里云天池、问天引擎 “阿里灵杰”问天引擎电商搜索算法赛已在阿里云天池平台拉开帷幕。本次比赛由阿里巴巴集团智能引擎事业部主办,阿里云天池平台承办,诚邀社会各界开发者共同探索智能零售场景AI新动力!…