Gan论文阅读笔记

GAN论文阅读笔记

2014年老论文了,主要记录一些重要的东西。论文链接如下:

Generative Adversarial Nets (neurips.cc)

文章目录

  • GAN论文阅读笔记
    • 出发点
    • 创新点
    • 设计
    • 训练代码
    • 网络结构代码
    • 测试代码

出发点

Deep generative models have had less of an impact, due to the difficulty of approximating many intractable probabilistic computations that arise in maximum likelihood estimation and related strategies, and due to difficulty of leveraging the benefits of piecewise linear units in the generative context.

​ 当时的生成模型效果不佳在于近似许多棘手的概率计算十分困难,如最大似然估计等。除此之外,把利用分段线性单元运用到生成场景中也有困难。于是作者提出新的生成模型:GAN。

​ 我的理解是,当时的生成模型都是去学习模型生成数据的分布,比如确定方差,确定均值之类的参数,然而这种方法十分难以学习,而且计算量大而复杂,作者考虑到这一点,对生成模型采用端到端的学习策略,不去学习生成数据的分布,而是直接学习模型,只要这个模型的生成结果能够逼近Ground-Truth,那么就可以直接用这个模型代替分布去生成数据。这是典型的黑箱思想。

创新点

adiscriminative model that learns to determine whether a sample is from the model distribution or the data distribution. The generative model can be thought of as analogous to a team of counterfeiters, trying to produce fake currency and use it without detection, while the discriminative model is analogous to the police, trying to detect the counterfeit currency. Competition in this game drives both teams to improve their methods until the counterfeits are indistiguishable from the genuine articles.

创新点1:提出对抗学习策略:提出两个model之间相互对抗,相互抑制的策略。一个model名为生成器Generator,一个model名为判别器Discriminator,生成器尽可能生成接近真实的数据,判别器尽可能识别出生成器数据是Fake。

In this article, we explore the special case when the generative model generates samples by passing random noise through a multilayer perceptron, and the discriminative model is also a multilayer perceptron.

创新点2:当两个model都使用神经网络时,可以运用反向传播和Dropout等算法进行学习,这样就可以避免使用马尔科夫链。

设计

To learn the generator’s distribution pgover data x, we define a prior on input noise variables pz(z), then represent a mapping to data space as G(z; θg), where G is a differentiable function represented by a multilayer perceptron with parameters θg. We also define a second multilayer perceptron D(x; θd) that outputs a single scalar. D(x) represents the probability that x came from the data rather than pg.

1.输入:为了让生成器G生成的数据分布pg与真实数据分布x接近,策略是给G输入一个噪音变量z,然后学习参数θg,这个θg是G网络权重。因此,G可以被写作:G(z;θg)。
m i n G m a x D V ( D , G ) = E x ∼ p d a t a ( x ) [ l o g D ( x ) ] + E z ∼ p z ( z ) [ l o g ( 1 − D ( G ( z ) ) ) ] \underset{G}{min}\underset{D}{max}V(D, G) =\mathbb{E}_{x \sim p_{data}(x)}\left[ logD(x)\right] + \mathbb{E}_{z \sim p_z(z)}\left[log(1 - D(G(z)))\right] GminDmaxV(D,G)=Expdata(x)[logD(x)]+Ezpz(z)[log(1D(G(z)))]
2.对抗性损失函数:从代码可知,对抗性损失是两个BCELoss的和,V尽可能使D(x)更大,在此基础上尽可能使G(z)更小。这是有先后顺序的,在后面会做说明。

在代码中可知,先人为生成两个标签,第一个标签是用torch.ones生成的全为1的矩阵,形状为(batch,1)。其中batch是输入噪声的batch,第二维度只是一个数字——1,这个标签用于判别器D的BCELoss中,代入BCELoss即可得到上面对抗性损失中左侧的期望。第二个标签是用torch.zeors生成的全为0的矩阵,形状同理为(batch,1),运用于生成器G的BCELoss中,代入即可得到对抗性损失的右侧期望。

we alternate between k steps of optimizing D and one step of optimizing G.

This results in D being maintained near its optimal solution, so long as G changes slowly enough.

3.D与G的训练有先后顺序:判别器D先于生成器G训练,而且要求先对D训练k步,再为G训练1步,这就保证G的训练比D足够慢。

如果生成器G足够强大,那么判别器无法再监测生成器,也就没有对抗的必要了。相反,如果判别器D太过于强大,那么生成器也训练地十分缓慢。

在这里插入图片描述

4.算法图如上。

训练代码

import torch
import torch.nn as nn
from torchvision import datasets
from torchvision import transforms
from torchvision.utils import save_image
from torch.utils.data import DataLoader
from Model import generator
from Model import discriminatorimport osif not os.path.exists('gan_train.py'):  # 报错中间结果os.mkdir('gan_train.py')def to_img(x):  # 将结果的-0.5~0.5变为0~1保存图片out = 0.5 * (x + 1)out = out.clamp(0, 1)out = out.view(-1, 1, 28, 28)return outbatch_size = 96
num_epoch = 200
z_dimension = 100# 数据预处理
img_transform = transforms.Compose([transforms.ToTensor(),  # 图像数据转换成了张量,并且归一化到了[0,1]。transforms.Normalize([0.5], [0.5])  # 这一句的实际结果是将[0,1]的张量归一化到[-1, 1]上。前面的(0.5)均值, 后面(0.5)标准差,
])
# MNIST数据集
mnist = datasets.MNIST(root='./data', train=True, transform=img_transform, download=True)
# 数据集加载器
dataloader = torch.utils.data.DataLoader(dataset=mnist, batch_size=batch_size, shuffle=True)D = discriminator()  # 创建生成器
G = generator()  # 创建判别器
if torch.cuda.is_available():  # 放入GPUD = D.cuda()G = G.cuda()criterion = nn.BCELoss()  # BCELoss 因为可以当成是一个分类任务,如果后面不加Sigmod就用BCEWithLogitsLoss
d_optimizer = torch.optim.Adam(D.parameters(), lr=0.0003)  # 优化器
g_optimizer = torch.optim.Adam(G.parameters(), lr=0.0003)  # 优化器# 开始训练
for epoch in range(num_epoch):for i, (img, _) in enumerate(dataloader):  # img[96,1,28,28]G.train()num_img = img.size(0)  # num_img=batchsize# =================train discriminatorimg = img.view(num_img, -1)  # 把图片拉平,为了输入判别器 [96,784]real_img = img.cuda()  # 装进cuda,真实图片real_label = torch.ones(num_img).reshape(num_img, 1).cuda()  # 希望判别器对real_img输出为1 [96,1]fake_label = torch.zeros(num_img).reshape(num_img, 1).cuda()  # 希望判别器对fake_img输出为0  [96,1]# 先训练鉴别器# 计算真实图片的lossreal_out = D(real_img)  # 将真实图片输入鉴别器 [96,1]d_loss_real = criterion(real_out, real_label)  # 希望real_out越接近1越好 [1]real_scores = real_out  # 后面print用的# 计算生成图片的lossz = torch.randn(num_img, z_dimension).cuda()  # 创建一个100维度的随机噪声作为生成器的输入 [96,1]#   这个z维度和生成器第一个Linear第一个参数一致# 避免计算G的梯度fake_img = G(z).detach()  # 生成伪造图片 [96,748]fake_out = D(fake_img)  # 给判别器判断生成的好不好 [96,1]d_loss_fake = criterion(fake_out, fake_label)  # 希望判别器给fake_out越接近0越好 [1]fake_scores = fake_out  # 后面print用的d_loss = d_loss_real + d_loss_faked_optimizer.zero_grad()d_loss.backward()d_optimizer.step()# 训练生成器# 计算生成图片的lossz = torch.randn(num_img, z_dimension).cuda()  # 生成随机噪声 [96,100]fake_img = G(z)  # 生成器伪造图像 [96,784]output = D(fake_img)  # 将伪造图像给判别器判断真伪 [96,1]g_loss = criterion(output, real_label)  # 生成器希望判别器给的值越接近1越好 [1]# 更新生成器g_optimizer.zero_grad()g_loss.backward()g_optimizer.step()if (i + 1) % 100 == 0:print(f'Epoch [{epoch}/{num_epoch}], d_loss: {d_loss.cpu().detach():.6f}, g_loss: {g_loss.cpu().detach():.6f}',f'D real: {real_scores.cpu().detach().mean():.6f}, D fake: {fake_scores.cpu().detach().mean():.6f}')if epoch == 0:  # 保存图片real_images = to_img(real_img.detach().cpu())save_image(real_images, './img_gan/real_images.png')fake_images = to_img(fake_img.detach().cpu())save_image(fake_images, f'./img_gan/fake_images-{epoch + 1}.png')G.eval()with torch.no_grad():new_z = torch.randn(batch_size, 100).cuda()test_img = G(new_z)print(test_img.shape)test_img = to_img(test_img.detach().cpu())test_path = f'./test_result/the_{epoch}.png'save_image(test_img, test_path)# 保存模型
torch.save(G.state_dict(), './generator.pth')
torch.save(D.state_dict(), './discriminator.pth')

网络结构代码

import torch
from torch import nn# 判别器 判别图片是不是来自MNIST数据集
class discriminator(nn.Module):def __init__(self):super(discriminator, self).__init__()self.dis = nn.Sequential(nn.Linear(784, 256),  # 784=28*28nn.LeakyReLU(0.2),nn.Linear(256, 256),nn.LeakyReLU(0.2),nn.Linear(256, 1),nn.Sigmoid()#   sigmoid输出这个生成器是或不是原图片,是二分类)def forward(self, x):x = self.dis(x)return x# 生成器 生成伪造的MNIST数据集
class generator(nn.Module):def __init__(self):super(generator, self).__init__()self.gen = nn.Sequential(nn.Linear(100, 256),  # 输入为100维的随机噪声nn.ReLU(),nn.Linear(256, 256),nn.ReLU(),nn.Linear(256, 784),#   生成器输出的特征维和正常图片一样,这是一个可参考的点nn.Tanh())def forward(self, x):x = self.gen(x)return xclass FinetuneModel(nn.Module):def __init__(self, weights):super(FinetuneModel, self).__init__()self.G = generator()base_weights = torch.load(weights)model_parameters = dict(self.G.named_parameters())#   不是对model进行named_parameters,而是对model里面的具体网络进行named_parameters取出参数,否则取出的是model冗余的参数去测试pretrained_weights = {k: v for k, v in base_weights.items() if k in model_parameters}new_state_dict = {k: pretrained_weights[k] for k in model_parameters.keys()}self.G.load_state_dict(new_state_dict)def forward(self, input):output = self.G(input)return output

测试代码

import os
import sys
import numpy as np
import torch
import argparse
import torch.utils.data
from PIL import Image
from Model import FinetuneModel
from Model import generator
from torchvision.utils import save_imageparser = argparse.ArgumentParser("GAN")
parser.add_argument('--save_path', type=str, default='./test_result')
parser.add_argument('--gpu', type=int, default=0)
parser.add_argument('--seed', type=int, default=2)
parser.add_argument('--model', type=str, default='generator.pth')args = parser.parse_args()
save_path = args.save_path
os.makedirs(save_path, exist_ok=True)def to_img(x):  # 将结果的-0.5~0.5变为0~1保存图片out = 0.5 * (x + 1)out = out.clamp(0, 1)out = out.view(-1, 1, 28, 28)return outdef main():if not torch.cuda.is_available():print("no gpu device available")sys.exit(1)model = FinetuneModel(args.model)model = model.to(device=args.gpu)model.eval()z_dimension = 100with torch.no_grad():for i in range(100):z = torch.randn(96, z_dimension).cuda()  # 创建一个100维度的随机噪声作为生成器的输入 [96,100]output = model(z)print(output.shape)u_name = f'the_{i}.png'print(f'processing {u_name}')u_path = save_path + '/' + u_nameoutput = to_img(output.cpu().detach())save_image(output, u_path)if __name__ == '__main__':main()

本文毕

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

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

相关文章

IntelliJ IDEA安装

文章目录 IntelliJ IDEA安装说明下载执行安装 IntelliJ IDEA安装 说明 操作系统:windows10 版本:2020.1 下载 官网地址 执行安装

Kubernetes(K8s 1.27.x) 快速上手+实践,无废话纯享版

文章目录 1 基础知识1.1 K8s 有用么?1.2 K8s 是什么?1.3 k8s 部署方式1.4 k8s 环境解析 2 环境部署2.1 基础环境配置2.2 容器环境操作2.3 cri环境操作2.4 harbor仓库操作2.5 k8s集群初始化2.6 k8s环境收尾操作 3 应用部署3.1 应用管理解读3.2 应用部署实…

教你pycharm运行Django第一个项目

文章目录 前言搭建Django:1.新建Django项目:2.为Django项目指定远程中创建的虚拟环境下的python解释器:3.配置ubuntu的端口转发(添加端口号为1234的端口):关于Python技术储备一、Python所有方向的学习路线二、Python基…

Spring基于注解开发

Component的使用 基本Bean注解&#xff0c;主要是使用注解的方式替代原有的xml的<bean>标签及其标签属性的配置&#xff0c;使用Component注解替代<bean>标签中的id以及class属性&#xff0c;而对于是否延迟加载或是Bean的作用域&#xff0c;则是其他注解 xml配置…

苹果股价为何会在11月份突然暴涨?12月份还会继续上涨吗?

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 苹果股价受益于大盘而上涨 随着第四季度财报的公布&#xff0c;全球市值最高的公司苹果(AAPL)的股价在上个月出现了暴涨&#xff0c;并在11月份剩下的大部分时间里一直保持着与标普500指数一致的走势。 猛兽财经认为主要原…

Vue3.0在软件开发中的能力展示

经过技术的调整与迁移之后&#xff0c;JNPF开发平台已经上线了Vue3.0版本。 JNPF是从 2014 开始研发低代码前端渲染&#xff0c;2018 年开始研发后端低代码数据模型&#xff0c;发布了JNPF开发平台。 基于SpringBootVue3的全栈开发平台&#xff0c;微服务、前后端分离架构&…

c语言-动态内存管理

文章目录 一、为什么会有动态内存管理二、申请内存函数1、malloc2、free3、calloc4、realloc 三、常见的动态内存的错误四、练习 一、为什么会有动态内存管理 1.我们一般的开辟空间方式&#xff1a; int a 0;//申请4个字节空间 int arr[10] { 0 };//申请40个字节空间2.这样…

“爆款大健康产品背后的创新营销策略“

我的朋友去年创立了一家创新型大健康产品公司&#xff0c;并在短短三个月内将其业务规模推到了2300万用户的高峰。你相信吗&#xff1f; 这位朋友是一个有着冒险精神的企业家&#xff0c;他并没有任何大健康产品方面的经验。他先找到了一家领先的科技公司&#xff0c;帮助他把他…

Python-算术运算符详解

运算符 算术运算符 关系运算符 逻辑运算符 赋值运算符 算术运算符&#xff1a;加减乘除 %求余 **平方 // 先算乘方&#xff0c;再算乘除&#xff0c;最后是加减。括号可以改变优先级 0不能作为除数&#xff08;不论是整型0还是浮点0&#xff09; 除法截断&#xff1a;舍弃小…

0013Java程序设计-基于Vue的上课签到系统的设计与实现

文章目录 **摘 要**目录系统设计4.2学生签到4.3 签到信息列表4.4 用户信息管理5.1系统登录5.1.1 登录5.1.2 清除用户登记记录5.1.3 登录拦截 5.2用户管理5.2.2 用户添加5.2.3 用户编辑5.2.4 用户删除5.2.5 用户分页 5.3签到信息5.3.1签到信息列表 5.4学生签到5.4.1学生签到 开发…

Vue 子路由页面发消息给主路由页面 ,实现主页面显示子页面的信息

需求 子页面进入后&#xff0c;能在主页面显示子页的相关信息&#xff0c;比如说主页面的菜单激活的是哪个子页面的菜单项 如上图&#xff0c;当刷新浏览器页面时&#xff0c;让菜单的激活项仍保持在【最近浏览】。 实现方式&#xff1a; 在子页面的create事件中增加&#xff…

软件测试--selenium安装使用

安装selenium不少人使用pip命令来安装selenium&#xff0c;辛辛苦苦安装完之后&#xff0c;还是不能使用。所以我们可以是直接使用编译器&#xff0c;pycharm直接安装selenium扩展包。 file中点击settings 在Settings中点击Project Interpreter,点击加号就可以安装各种需要的扩…