深度学习(PyTorch)——生成对抗网络(GAN)

一、GAN的基本概念

GAN是由Ian Goodfellow于2014年首次提出,学习GAN的初衷,即生成不存在于真实世界的数据。类似于AI具有创造力和想象力。

GAN有两大护法G和D:

G是generator,生成器: 负责凭空捏造数据出来

D是discriminator,判别器: 负责判断数据是不是真数据

这样可以简单看作是两个网络的博弈过程。在原始的GAN论文里面,G和D都是两个多层感知机网络。GAN操作的数据不一定非是图像数据,在此用图像数据为例解释以下GAN:

深度学习(PyTorch)——生成对抗网络(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模型的目标函数如下:

深度学习(PyTorch)——生成对抗网络(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,&#x8F93;&#x51FA;x&#xFF0C;&#x5982;&#x679C;x<0,输出a*x,a表示一个很小的斜率,比如0.1 0 #判别器中一般推荐使用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.linear(256, 1), nn.sigmoid() ) forward(self,x): x="x.view(-1,28*28)" return # device="cuda" if torch.cuda.is_available() 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()" 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="[]" 训练循环 epoch range(20): d_epoch_loss="0" g_epoch_loss="0" count="len(dataloader)" len(dataloader)返回批次数 len(dataset)返回样本数 step,(img,_) 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_optim.step() g_optim.zero_grad() 得到生成器的损失 g_loss.backward() g_optim.step() with torch.no_grad(): =count d_loss.append(d_epoch_loss) g_loss.append(g_epoch_loss) print("epoch",epoch) gen_img_plot(gen,test_input) < code></0,输出a*x,a表示一个很小的斜率,比如0.1>

Original: https://blog.csdn.net/qq_42233059/article/details/126579791
Author: 清泉_流响
Title: 深度学习(PyTorch)——生成对抗网络(GAN)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/707266/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球