GAN生成对抗网络
什么是GAN?
GANs(Generativeadversarial networks,对抗式生成网络)可以把这三个单词拆分理解。
Generative:生成式模型
Adversarial:采取对抗的策略
Networks:网络(不一定是深度学习)
模型通过框架中(至少)两个模块:生成模型(Generative Model)和判别模型(Discriminative Model)的互相博弈学习产生相当好的输出。原始 GAN 理论中,并不要求 G 和 D 都是神经网络,只需要是能拟合相应生成和判别的函数即可。但实用中一般均使用深度神经网络作为 G 和 D 。
判别模型和生成模型
判别模型需要输入变量 ,通过某种模型来预测 。生成模型是给定某种隐含信息,来随机产生观测数据。举个简单的例子,
判别模型:给定一张图,判定图中的动物是什么类别
生成模型:给一系列猫的图片,生成一张新的猫咪
相同点是:
- 这两个模型都可以看成是一个黑匣子,接受输入然后有一个输出,类似一个函数,一个输入输出映射。
不同点是:
- 生成模型功能:比作是一个样本生成器,输入一个噪声/样本,然后把它包装成一个逼真的样本,也就是输出。
- 判别模型:比作一个二分类器(如同0-1分类器),来判断输入的样本是真是假。(就是输出值大于0.5还是小于0.5);
基本原理
假设我们有两个网络,G(Generator)和D(Discriminator)。正如它的名字所暗示的那样,它们的功能分别是:
G是一个生成图片的网络,它接收一个随机的噪声z,通过这个噪声生成图片,记做G(z)。
D是一个判别网络,判别一张图片是不是“真实的”。它的输入参数是x,x代表一张图片,输出D(x)代表x为真实图片的概率,如果为1,就代表100%是真实的图片,而输出为0,就代表不可能是真实的图
在训练过程中,生成网络G的目标就是尽量生成真实的图片去欺骗判别网络D。而D的目标就是尽量把G生成的图片和真实的图片分别开来。这样,G和D构成了一个动态的“博弈过程”。
二人零和博弈思想(two-player game)
GAN 的思想是一种二人零和博弈思想(two-player game)博弈双方的利益之和是一个常数,比如两个人掰手腕,假设总的空间是一定的,你的力气大一点,那你就得到的空间多一点,相应的我的空间就少一点,相反我力气大我就得到的多一点,但有一点是确定的就是,我两的总空间是一定的,这就是二人博弈,但是呢总利益是一定的。
在GAN中就是可以看成,GAN中有两个这样的博弈者,一个人名字是生成模型(G),另一个人名字是判别模型(D)。他们各自有各自的功能。
两者的相同点:
这两个模型都可以看成是一个黑匣子,接受输入然后有一个输出,类似一个函数,一个输入输出映射
不同点:
生成模型功能:比作是一个样本生成器,输入一个噪声/样本,然后把它包装成一个逼真的样本,也就是输出。
判别模型:比作一个二分类器(如同0-1分类器),来判断输入的样本是真是假。(就是输出值大于0.5还是小于0.5)
生成器
对于生成器,输入需要一个n维度向量,输出为图片像素大小的图片。因而首先我们需要得到输入的向量。
这里的生成器可以是任意可以输出图片的模型,比如最简单的全连接神经网络,又或者是反卷积网络等。
这里输入的向量我们将其视为携带输出的某些信息,比如说手写数字为数字几,手写的潦草程度等等。由于这里我们对于输出数字的具体信息不做要求,只要求其能够最大程度与真实手写数字相似(能骗过判别器)即可。所以我们使用随机生成的向量来作为输入即可,这里面的随机输入最好是满足常见分布比如均值分布,高斯分布等。
假如我们后面需要获得具体的输出数字等信息的时候,我们可以对输入向量产生的输出进行分析,获取到哪些维度是用于控制数字编号等信息的即可以得到具体的输出。而在训练之前往往不会去规定它。
判别器
对于判别器不用多说,往往是常见的判别器,输入为图片,输出为图片的真伪标签。
GAN的训练
1.在噪声数据分布中随机采样,输入生成模型,得到一组假数据,记为D(z)
2.在真实数据分布中随机采样,作为真实数据,记做x;
将前两步中某一步产生的数据作为判别网络的输入(因此判别模型的输入为两类数据,真/假),判别网络的输出值为该输入属于真实数据的概率,real为1,fake为0.
3.然后根据得到的概率值计算损失函数;
4.根据判别模型和生成模型的损失函数,可以利用反向传播算法,更新模型的参数。(先更新判别模型的参数,然后通过再采样得到的噪声数据更新生成器的参数)
GAN强大之处在于能自动学习原始真实样本集的数据分布,不管这个分布多么的复杂,只要训练的足够好就可以学出来。
GAN的生成模型最后可以通过噪声生成一个完整的真实数据(比如人脸),说明生成模型掌握了从随机噪声到人脸数据的分布规律。GAN一开始并不知道这个规律是什么样,也就是说GAN是通过一次次训练后学习到的真实样本集的数据分布。
GAN的应用
根据MSE生成图像
导入pytorch及所需要的库
import os
import torch
import torch.nn as nn
from torch.autograd import Variable
import torch.optim as optim
import torch.nn.functional as F
import torchvision.datasets as dsets
import torchvision.transforms as transforms
import torchvision.utils as vutil
import matplotlib.pyplot as plt
import numpy as np
定义超参数
image_size = 28
input_dim = 100
num_channels = 1
num_features = 64
batch_size = 64
定义数据集
train_dataset = dsets.MNIST(root='./data',
train=True,
transform=transforms.ToTensor(),
download=True)
test_dataset = dsets.MNIST(root='./data',
train=False,
transform=transforms.ToTensor())
indices = range(len(test_dataset))
indices_val = indices[:5000]
indices_test = indices[5000:]
定义采样器
sampler_val = torch.utils.data.sampler.SubsetRandomSampler(indices_val)
sampler_test = torch.utils.data.sampler.SubsetRandomSampler(indices_test)
validation_loader = torch.utils.data.DataLoader(dataset =test_dataset,
batch_size = batch_size,
sampler = sampler_val
)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
batch_size=batch_size,
sampler = sampler_test
)
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
batch_size=batch_size,
shuffle=True
)
def make_show(img):
img = img.data.expand(batch_size, 3, image_size, image_size)
return img
def imshow(inp, title=None, ax=None):
"""Imshow for Tensor."""
if inp.size()[0] > 1:
inp = inp.numpy().transpose((1, 2, 0))
else:
inp = inp[0].numpy()
mvalue = np.amin(inp)
maxvalue = np.amax(inp)
if maxvalue > mvalue:
inp = (inp - mvalue)/(maxvalue - mvalue)
ax.imshow(inp)
if title is not None:
ax.set_title(title)
定义生成器
class ModelG(nn.Module):
def __init__(self):
super(ModelG,self).__init__()
self.model=nn.Sequential()
self.model.add_module('deconv1',nn.ConvTranspose2d(input_dim, num_features*2, 5, 2, 0, bias=False))
self.model.add_module('bnorm1',nn.BatchNorm2d(num_features*2))
self.model.add_module('relu1',nn.ReLU(True))
self.model.add_module('deconv2',nn.ConvTranspose2d(num_features*2, num_features, 5, 2, 0, bias=False))
self.model.add_module('bnorm2',nn.BatchNorm2d(num_features))
self.model.add_module('relu2',nn.ReLU(True))
self.model.add_module('deconv3',nn.ConvTranspose2d(num_features, num_channels, 4, 2, 0,bias=False))
self.model.add_module('sigmoid',nn.Sigmoid())
def forward(self,input):
output = input
for name, module in self.model.named_children():
output = module(output)
return(output)
def weight_init(m):
class_name=m.__class__.__name__
if class_name.find('conv')!=-1:
m.weight.data.normal_(0,0.02)
if class_name.find('norm')!=-1:
m.weight.data.normal_(1.0,0.02)
准备训练
net = ModelG()
net = net.cuda() if use_cuda else net
criterion = nn.MSELoss()
optimizer = optim.SGD(net.parameters(), lr=0.0001, momentum=0.9)
samples = np.random.choice(10, batch_size)
samples = Variable(torch.from_numpy(samples).type(dtype))
samples.resize_(batch_size,1,1,1)
samples = Variable(samples.data.expand(batch_size, input_dim, 1, 1))
samples = samples.cuda() if use_cuda else samples
def save_evaluation_samples(netModel, save_path='gan'):
save_path = save_path.strip()
if not os.path.exists(save_path):
os.makedirs(save_path)
fake_u = netModel(samples)
fake_u = fake_u.cpu() if use_cuda else fake_u
img = make_show(fake_u)
vutil.save_image(img, save_path + '/fake%s.png'% (epoch))
def train_ModelG(target, data):
if use_cuda:
target, data = target.cuda(), data.cuda()
data = data.type(dtype)
data = data.reshape(data.size()[0], 1, 1, 1)
data = data.expand(data.size()[0], input_dim, 1, 1)
net.train()
output = net(data)
loss = criterion(output, target)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if use_cuda:
loss = loss.cpu()
return loss
def evaluation_ModelG():
net.eval()
val_loss = []
'''开始在校验数据集上做循环,计算校验集上面的准确度'''
idx = 0
for (data, target) in validation_loader:
target, data = Variable(data), Variable(target)
idx += 1
if use_cuda:
target, data = target.cuda(), data.cuda()
data = data.type(dtype)
data = data.reshape(data.size()[0], 1, 1, 1)
data = data.expand(data.size()[0], input_dim, 1, 1)
output = net(data)
loss = criterion(output, target)
if use_cuda:
loss = loss.cpu()
val_loss.append(loss.data.numpy())
return val_loss
训练模型
print('Initialized!')
step = 0
num_epochs = 1
record = []
for epoch in range(num_epochs):
train_loss = []
for batch_idx, (data, target) in enumerate(train_loader):
target, data = Variable(data), Variable(target)
loss = train_ModelG(target, data)
train_loss.append(loss.data.numpy())
step += 1
if step % 100 == 0:
val_loss = evaluation_ModelG()
print('训练周期: {} [{}/{} ({:.0f}%)]\t训练数据Loss: {:.6f}\t校验数据Loss: {:.6f}'.format(
epoch, batch_idx * batch_size, len(train_loader.dataset),
100. * batch_idx / len(train_loader), np.mean(train_loss), np.mean(val_loss)))
record.append([np.mean(train_loss), np.mean(val_loss)])
save_evaluation_samples(net, 'MSE')
验证模型
net = torch.load('ModelG_CPU.mdl')
fake_u = net(samples)
fake_u = fake_u.cpu() if use_cuda else fake_u
samples = samples.cpu() if use_cuda else samples
img = fake_u.data
fig = plt.figure(figsize = (15, 15))
f, axarr = plt.subplots(8,8, sharex=True, figsize=(15,15))
for i in range(batch_size):
axarr[i // 8, i % 8].axis('off')
imshow(img[i], samples.data.numpy()[i][0,0,0].astype(int),axarr[i // 8, i % 8])
定义识别器
depth = [4, 8]
class ConvNet(nn.Module):
def __init__(self):
super(ConvNet, self).__init__()
def forward(self, x):
x = F.relu(self.conv1(x))
x = self.pool(x)
x = F.relu(self.conv2(x))
x = self.pool(x)
x = x.view(-1, image_size // 4 * image_size // 4 * depth[1])
x = F.relu(self.fc1(x))
x = F.dropout(x, training=self.training)
x = self.fc2(x)
x = F.log_softmax(x, dim=1)
return x
def retrieve_features(self, x):
feature_map1 = F.relu(self.conv1(x))
x = self.pool(feature_map1)
feature_map2 = F.relu(self.conv2(x))
return (feature_map1, feature_map2)
def rightness(predictions, labels):
pred = torch.max(predictions.data, 1)[1]
rights = pred.eq(labels.data.view_as(pred)).sum()
return rights, len(labels)
netR = torch.load('minst_conv_checkpoint')
netR = netR.cuda() if use_cuda else netR
for para in netR.parameters():
para.requires_grad = False
准备训练(生成器与识别器)
netG = ModelG()
netG = netG.cuda() if use_cuda else netG
netG.apply(weight_init)
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(netG.parameters(), lr=0.0001, momentum=0.9)
def train_ConvNet(target, data):
if use_cuda:
target, data = target.cuda(), data.cuda()
label = data.clone()
data = data.type(dtype)
data = data.reshape(data.size()[0], 1, 1, 1)
data = data.expand(data.size()[0], input_dim, 1, 1)
netG.train()
netR.train()
output1 = netG(data)
output = netR(output1)
loss = criterion(output, label)
optimizer.zero_grad()
loss.backward()
optimizer.step()
right = rightness(output, label)
if use_cuda:
loss = loss.cpu()
return right, loss
验证生成器与识别器
def evaluation_ConvNet():
netG.eval()
netR.eval()
val_loss = []
val_rights = []
'''开始在校验数据集上做循环,计算校验集上面的准确度'''
for (data, target) in validation_loader:
target, data = Variable(data), Variable(target)
if use_cuda:
target, data = target.cuda(), data.cuda()
label = data.clone()
data = data.type(dtype)
data = data.reshape(data.size()[0], 1, 1, 1)
data = data.expand(data.size()[0], input_dim, 1, 1)
output1 = netG(data)
output = netR(output1)
loss = criterion(output, label)
if use_cuda:
loss = loss.cpu()
val_loss.append(loss.data.numpy())
right = rightness(output, label)
val_rights.append(right)
return val_loss, val_rights
开始训练
samples = np.random.choice(10, batch_size)
samples = Variable(torch.from_numpy(samples).type(dtype))
samples.resize_(batch_size,1,1,1)
samples = Variable(samples.data.expand(batch_size, input_dim, 1, 1))
samples = samples.cuda() if use_cuda else samples
print('Initialized!')
num_epochs = 1
statistics = []
for epoch in range(num_epochs):
train_loss = []
train_rights = []
for batch_idx, (data, target) in enumerate(train_loader):
target, data = Variable(data), Variable(target)
right, loss = train_ConvNet(target, data)
train_loss.append(loss.data.numpy())
train_rights.append(right)
step += 1
if step % 100 == 0:
val_loss, val_rights = evaluation_ConvNet()
val_r = (sum([tup[0] for tup in val_rights]), sum([tup[1] for tup in val_rights]))
train_r = (sum([tup[0] for tup in train_rights]), sum([tup[1] for tup in train_rights]))
print(('训练周期: {} [{}/{} ({:.0f}%)]\t训练数据Loss: {:.6f},正确率: {:.2f}%\t校验数据Loss:' +
'{:.6f},正确率:{:.2f}%').format(epoch, batch_idx * batch_size, len(train_loader.dataset),
100. * batch_idx / len(train_loader), np.mean(train_loss),
100. * train_r[0] / train_r[1],
np.mean(val_loss),
100. * val_r[0] / val_r[1]))
statistics.append({'loss':np.mean(train_loss),'train': 100. * train_r[0] / train_r[1],
'valid':100. * val_r[0] / val_r[1]})
save_evaluation_samples(netG, 'ConvNet')
验证结果
batch = next(iter(test_loader))
indx = torch.nonzero(batch[1] == 6)
data = batch[0][indx[0]]
img = data.expand(1, 1, image_size, image_size)
print(img.size())
plt.axis('off')
imshow(img[0], 6, plt.gca())
input_x_real = Variable(data)
input_x_real = input_x_real.cuda() if use_cuda else input_x_real
output = netR(input_x_real)
_, prediction = torch.max(output, 1)
print('识别器对真实图片的识别结果:', prediction)
idx = 6
ax = plt.gca()
ax.axis('off')
imshow(fake_u[idx].data, 6, plt.gca())
print(samples[idx][0])
input_fake = fake_u[idx]
input_fake = input_fake.unsqueeze(0)
input_fake = input_fake.cuda() if use_cuda else input_fake
output = netR(input_fake)
_, prediction = torch.max(output, 1)
print('识别器对生成图片的识别结果:', prediction)
|