本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.
一、GAN的基本概念
GAN是由Ian Goodfellow于2014年首次提出,学习GAN的初衷,即生成不存在于真实世界的数据。类似于AI具有创造力和想象力。
GAN有两大护法G和D:
G是generator,生成器: 负责凭空捏造数据出来
D是discriminator,判别器: 负责判断数据是不是真数据
这样可以简单看作是两个网络的博弈过程。在原始的GAN论文里面,G和D都是两个多层感知机网络。GAN操作的数据不一定非是图像数据,在此用图像数据为例解释以下GAN:
上图中,z是随机噪声(随机生成的一些数,也是GAN生成图像的源头)。D通过真图和假图的数据,进行一个二分类神经网络训练。G根据一串随机数就可以捏造出一个”假图像”出来,用这些假图去欺骗D,D负责辨别这是真图还是假图,会给出一个score。比如,G生成了一张图,在D这里评分很高,说明G生成能力是很成功的;若D给出的评分不高,可以有效区分真假图,则G的效果还不太好,需要调整参数。
二、GAN的基本原理
GAN的训练在同一轮梯度反转的过程中可以细分为2步:(1)先训练D;(2)再训练G。注意,不是等所有的D训练好了才开始训练G,因为D的训练也需要上一轮梯度反转中的G的输出值作为输入。
当训练D的时候:上一轮G产生的图片和真实图片,直接拼接在一起作为x。然后按顺序摆放成0和1,假图对应0,真图对应1。然后就可以通过D,x输入生成一个score(从0到1之间的数),通过score和y组成的损失函数,就可以进行梯度反传了。(我在图片上举的例子是batch = 1,len(y)=2*batch,训练时通常可以取较大的batch)
当训练G的时候:需要把G和D当作一个整体,这里取名叫做’D_on_G’。这个整体(简称DG系统)的输出仍然是score。输入一组随机向量z,就可以在G生成一张图,通过D对生成的这张图进行打分得到score,这就是DG系统的前向过程。score=1就是DG系统需要优化的目标,score和y=1之间的差异可以组成损失函数,然后可以采用反向传播梯度。注意,这里的D的参数是不可训练的。这样就能保证G的训练是符合D的打分标准的。这就好比:如果你参加考试,你别指望能改变老师的评分标准。
GAN模型的目标函数如下:
在这里,训练网络D使得最大概率地分对训练样本的标签(最大化log D(x)和log(1—D(G(z)))),训练网络G最小化log(1-D(G(z))),即最大化D的损失。而训练过程中固定一方,更新另一个网络的参数,交替迭代,使得对方的错误最大化,最终,G 能估测出样本数据的分布,也就是生成的样本更加的真实。
三、案例、用GAN对输入的随机数生成手写字体的图片
程序如下:
import torch import torch.nn as nn import torch.optim as optim import torch.nn.functional as F import numpy as np import matplotlib.pyplot as plt import torchvision from torchvision import transforms import os import pandas os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE" #对数据做归一化(-1 --- 1) tranform = transforms.Compose([ transforms.ToTensor(), # 0-1归一化:channel,high,width transforms.Normalize(0.5,0.5), #均值,方差均为0.5 ]) train_ds = torchvision.datasets.MNIST("data",train = True,transform = tranform,download=True) dataloader = torch.utils.data.DataLoader(train_ds,batch_size = 64,shuffle = True) imgs, _ =next(iter(dataloader)) print(imgs.shape) #torch.Size([64, 1, 28, 28]) # batch_size=64 图片大小:1,28,28 # 定义生成器 # 输入是长度为100的噪声(正态分布随机数) # 输出为(1,28,28)的图片 # linear1:100---256 # linear2:256---512 # linear1:512---28*28 # linear2:28*28---(1,28,28) class Generator(nn.Module): def __init__(self): super(Generator,self).__init__() self.main = nn.Sequential( nn.Linear(100,256), nn.ReLU(), nn.Linear(256,512), nn.ReLU(), nn.Linear(512, 28*28), nn.Tanh() ) def forward(self,x): # x表示长度为100的噪声输入 img = self.main(x) img = img.view(-1,28,28) return img # 定义判别器 # 输入为(1,28,28)的图片,输出为二分类的概率值,输出使用sigmoid激活 # BCELose计算交叉熵损失 # nn.LeakyReLU f(x):x>0,输出x,如果x<0,输出a*x,a表示一个很小的斜率,比如0.1 #判别器中一般推荐使用nn.LeakyReLU class Discriminator(nn.Module): def __init__(self): super(Discriminator, self).__init__() self.main = nn.Sequential( nn.Linear(28*28,512), nn.LeakyReLU(), nn.Linear(512,256), nn.LeakyReLU(), nn.Linear(256, 1), nn.Sigmoid() ) def forward(self,x): x = x.view(-1,28*28) x = self.main(x) return x # device = 'cuda' if torch.cuda.is_available() else 'cpu' device = 'cuda' if 0 else 'cpu' gen = Generator().to(device) dis = Discriminator().to(device) d_optim = torch.optim.Adam(dis.parameters(),lr=0.0001) g_optim = torch.optim.Adam(gen.parameters(),lr=0.0001) loss_fn = torch.nn.BCELoss() def gen_img_plot(model,test_input): prediction = np.squeeze(model(test_input).detach().cpu().numpy()) # detach()截断梯度,np.squeeze可以去掉维度为1 fig = plt.figure(figsize=(4,4)) for i in range(16): # prediction.size(0)=16 plt.subplot(4,4,i+1) plt.imshow((prediction[i]+1)/2) # tanh 得到的是-1 - 1之间,-》0-1之间 plt.axis("off") plt.show() test_input = torch.randn(16,100,device=device) # 16个长度为100的正态随机数 # print(test_input) D_loss = [] G_loss = [] # 训练循环 for epoch in range(20): d_epoch_loss = 0 g_epoch_loss = 0 count = len(dataloader) # len(dataloader)返回批次数 # len(dataset)返回样本数 for step,(img,_) in enumerate(dataloader): img = img.to(device) size = img.size(0) random_noise = torch.randn(size,100,device=device) d_optim.zero_grad() real_output = dis(img) # 对判别器输入真实图片 real_output对真实图片的预测结果 #得到判别器在真实图像上面的损失 d_real_loss = loss_fn(real_output,torch.ones_like(real_output)) d_real_loss.backward() gen_img = gen(random_noise) #detach()截断生成器梯度,更新判别器梯度 fake_output = dis(gen_img.detach()) # 判别器输入生成图片。fake_output对生成图片的预测 # 得到判别器在生成图像上面的损失 d_fake_loss = loss_fn(fake_output,torch.zeros_like(fake_output)) d_fake_loss.backward() d_loss = d_fake_loss + d_real_loss d_optim.step() g_optim.zero_grad() fake_output = dis(gen_img) # 得到生成器的损失 g_loss = loss_fn(fake_output,torch.ones_like(fake_output)) g_loss.backward() g_optim.step() with torch.no_grad(): d_epoch_loss +=d_loss g_epoch_loss +=g_loss with torch.no_grad(): d_epoch_loss /=count g_epoch_loss /=count D_loss.append(d_epoch_loss) G_loss.append(g_epoch_loss) print("Epoch",epoch) gen_img_plot(gen,test_input)
Be First to Comment