这个框架可以针对多种模型和优化算法提供特定的训练算法。在这篇文章中,我们探讨了生成模型通过将随机噪声传输到多层感知机来生成样本的特例,同时判别模型也是通过多层感知机实现的。我们称这个特例为对抗网络。在这种情况下,我们可以仅使用非常成熟的反向传播和丢弃算法训练两个模型,生成模型在生成样本时只使用前向传播算法。并且不需要近似推理和马尔可夫链作为前题。
含隐变量的有向图模型可以由含隐变量的无向图模型替代,例如受限制波兹曼机(RBM),深度波兹曼机(DBM)和它们很多的变种。这些模型之间的相互影响可以被表达为非标准化的势函数的乘积,再通过随机变量的所有状态的全局整合来标准化。这个数量(配分函数)和它的梯度的估算是很棘手的,尽管他们能够依靠马尔可夫链和蒙特卡罗(MCMC)算法来估计,同时依靠MCMC算法的混合也会引发一个严重的问题。
深度信念网络(DBN)是一个包含一个无向层和若干有向层的混合模型。当使用一个快速逐层训练法则时,DBNS 会引发无向模型和有向模型相关的计算难题。
不是利用似然函数的估计或约数的选择准则已经被提出来了,例如分数匹配和噪音压缩评估(NCE)。他们都需要知道先验概率密度知识用来分析指定一个规范化的常量。请注意,许多有趣的带有一些隐层变量的生成模型(如DBN和DBM),它们甚至不需要一些难以处理的非标准化的概率密度先验知识。一些模型如自动编码降噪机和压缩编码的学习准则与分数匹配在RBM上的应用非常相似。在NCE中,使用一个判别训练准则来拟合一个生成模型。然而,生成模型常常被用来判别从一个固定噪音分布中抽样生成的数据,而不是拟合一个独立的判别模型。由于NCE使用一个固定的噪音分布,仅仅是从观测变量的一个小子集中学习到一个大致正确的分布后,模型的学习便急剧减慢。
最后,一些技术并没有用来明确定义概率分布,而是用来训练一个生成器来从期望的分布中拟合出样本。这个方法优势在于这些机器学习算法能够设计使用反向传播算法训练。这个领域最近比较突出的工作包含生成随机网络(GSN),它扩展了广义的除噪自动编码器:两者都可以看作是定义了一个参数化的马尔可夫链,即一个通过执行生成马尔科夫链的一个步骤来学习机器参数的算法。同GSNs相比,对抗网络不需要使用马尔可夫链来采样。由于对抗网络在生成阶段不需要循环反馈信息,它们能够更好的利用分段线性单元,这可以提高反向传播的效率。大部分利用反向传播算法来训练生成器的例子包括贝叶斯变分自动编码和随机反向传播。
GAN是由两部分主成:生成模型G(generative model)和判别模型D(discriminative model)。生成模型捕捉样本数据的分布,判别模型是一个二分类器,判别输入是真实数据还是生成的样本。
X是真实数据,真实数据符合Pdata(x)分布。z是噪声数据,噪声数据符合Pz(z)分布,比如高斯分布或者均匀分布。然后从噪声z进行抽样,通过G之后生成数据x=G(z)。然后真实数据与生成数据一起送入分类器D,后面接一个sigmoid函数,输出判定类别。
这里,z是噪声数据,从z中采样作为x,形成绿色的高耸的部分,原始数据Xdata是不变的。蓝色虚线是分类器(sigmoid),黑色虚线是原始数据,绿色实线是生成数据。最初的时候D可以很好的区分开真实数据和生成数据,看图(b),对于蓝色虚线而言,单纯的绿色部分可以很明确的划分为0,黑色虚线的部分可以很明确的划分成1,二者相交的部分划分不是很明确。看图(c)绿色实现生成数据更新,与原始数据相似度增加。最后一张图,经过多次更新与对抗生成之后,生成数据已经和原始数据非常相像的了,这时分类器已经无法判断到底是输出1还是0,于是就变成了0.5一条水平线。
这张图形象的说明了一下G和D的优化方向。优化D的时候需要很好的画出黑色虚线,使它能够区分开真实数据和生成数据。优化G的时候,生成数据更加接近原始数据的样子,使得D难以区分数据真假。如此反复直到最后再也画不出区分的黑色虚线。
(1)GAN可以用来产生data。
(2)根据GAN的组成结构我们可以知道,GAN 整个的过程就是,G产生一个自创的假数据和真数据放在一起让D来区分,在这种不停的较量中G就模拟出来了跟真实数据十分相近的数据。所以GAN主要的应用场景就是能够学习出这样模拟分布的数据,并且可以用模拟分布代替原始数据的地方!
D想办法增加V的值,G想办法减小V的值,两人在相互的对抗。
下面讲这个式子的数学意义。
首先固定G训练D :
1)训练D的目的是希望这个式子的值越大越好。真实数据希望被D分成1,生成数据希望被分成0。
第一项,如果有一个真实数据被分错,那么log(D(x))<<0,期望会变成负无穷大。
第二项,如果被分错成1的话,第二项也会是负无穷大。
很多被分错的话,就会出现很多负无穷,那样可以优化的空间还有很多。可以修正参数,使V的数值增大。
2)训练G ,它是希望V的值越小越好,让D分不开真假数据。
因为目标函数的第一项不包含G,是常数,所以可以直接忽略 不受影响。
对于G来说 它希望D在划分他的时候能够越大越好,他希望被D划分1(真实数据)。
与其他生成式模型相比较,生成式对抗网络有以下四个优势:
①解决不收敛(non-convergence)的问题。
目前面临的基本问题是:所有的理论都认为 GAN 应该在纳什均衡(Nash equilibrium)上有卓越的表现,但梯度下降只有在凸函数的情况下才能保证实现纳什均衡。当博弈双方都由神经网络表示时,在没有实际达到均衡的情况下,让它们永远保持对自己策略的调整是可能的。
②难以训练:崩溃问题(collapse problem)
GAN模型被定义为极小极大问题,没有损失函数,在训练过程中很难区分是否正在取得进展。GAN的学习过程可能发生崩溃问题(collapse problem),生成器开始退化,总是生成同样的样本点,无法继续学习。当生成模型崩溃时,判别模型也会对相似的样本点指向相似的方向,训练无法继续。当然Goodfellow在【Improved Techniques for Training GANs】文章中也有一些相应的改进方法例如:特征映射等。
③无需预先建模,模型过于自由不可控。
与其他生成式模型相比,GAN这种竞争的方式不再要求一个假设的数据分布,即不需要formulate p(x),而是使用一种分布直接进行采样sampling,从而真正达到理论上可以完全逼近真实数据,这也是GAN最大的优势。然而,这种不需要预先建模的方法缺点是太过自由了,对于较大的图片,较多的 pixel的情形,基于简单 GAN 的方式就不太可控了(超高维)。在GAN[Goodfellow Ian, Pouget-Abadie J] 中,每次学习参数的更新过程,被设为D更新k回,G才更新1回,也是出于类似的考虑。
import matplotlib.pyplot as plt
from torch.utils.data import DataLoader
from torch import nn
import numpy as np
from torch.autograd import Variable
import torch
batch_size=8
data_iter=DataLoader(data,batch_size=batch_size)
#定义生成器
class net_G(nn.Module):
def __init__(self):
super(net_G,self).__init__()
self.model=nn.Sequential(
nn.Linear(2,2),
)
self._initialize_weights()
def forward(self,x):
x=self.model(x)
return x
def _initialize_weights(self):
for m in self.modules():
if isinstance(m,nn.Linear):
m.weight.data.normal_(0,0.02)
m.bias.data.zero_()
#定义识别器
class net_D(nn.Module):
def __init__(self):
super(net_D,self).__init__()
self.model=nn.Sequential(
nn.Linear(2,5),
nn.Tanh(),
nn.Linear(5,3),
nn.Tanh(),
nn.Linear(3,1),
nn.Sigmoid()
)
self._initialize_weights()
def forward(self,x):
x=self.model(x)
return x
def _initialize_weights(self):
for m in self.modules():
if isinstance(m,nn.Linear):
m.weight.data.normal_(0,0.02)
m.bias.data.zero_()
#训练
# Saved in the d2l package for later use
def update_D(X,Z,net_D,net_G,loss,trainer_D):
batch_size=X.shape[0]
Tensor=torch.FloatTensor
ones=Variable(Tensor(np.ones(batch_size))).view(batch_size,1)
zeros = Variable(Tensor(np.zeros(batch_size))).view(batch_size,1)
real_Y=net_D(X.float())
fake_X=net_G(Z)
fake_Y=net_D(fake_X)
loss_D=(loss(real_Y,ones)+loss(fake_Y,zeros))/2
loss_D.backward()
trainer_D.step()
return float(loss_D.sum())
# Saved in the d2l package for later use
def update_G(Z,net_D,net_G,loss,trainer_G):
batch_size=Z.shape[0]
Tensor=torch.FloatTensor
ones=Variable(Tensor(np.ones((batch_size,)))).view(batch_size,1)
fake_X=net_G(Z)
fake_Y=net_D(fake_X)
loss_G=loss(fake_Y,ones)
loss_G.backward()
trainer_G.step()
return float(loss_G.sum())
def train(net_D,net_G,data_iter,num_epochs,lr_D,lr_G,latent_dim,data):
loss=nn.BCELoss()
Tensor=torch.FloatTensor
trainer_D=torch.optim.Adam(net_D.parameters(),lr=lr_D)
trainer_G=torch.optim.Adam(net_G.parameters(),lr=lr_G)
plt.figure(figsize=(7,4))
d_loss_point=[]
g_loss_point=[]
d_loss=0
g_loss=0
for epoch in range(1,num_epochs+1):
d_loss_sum=0
g_loss_sum=0
batch=0
for X in data_iter:
batch+=1
X=Variable(X)
batch_size=X.shape[0]
Z=Variable(Tensor(np.random.normal(0,1,(batch_size,latent_dim))))
trainer_D.zero_grad()
d_loss = update_D(X, Z, net_D, net_G, loss, trainer_D)
d_loss_sum+=d_loss
trainer_G.zero_grad()
g_loss = update_G(Z, net_D, net_G, loss, trainer_G)
g_loss_sum+=g_loss
d_loss_point.append(d_loss_sum/batch)
g_loss_point.append(g_loss_sum/batch)
plt.ylabel('Loss', fontdict={'size': 14})
plt.xlabel('epoch', fontdict={'size': 14})
plt.xticks(range(0,num_epochs+1,3))
plt.plot(range(1,num_epochs+1),d_loss_point,color='orange',label='discriminator')
plt.plot(range(1,num_epochs+1),g_loss_point,color='blue',label='generator')
plt.legend()
plt.show()
print(d_loss,g_loss)
Z =Variable(Tensor( np.random.normal(0, 1, size=(100, latent_dim))))
fake_X=net_G(Z).detach().numpy()
plt.figure(figsize=(3.5,2.5))
plt.scatter(data[:,0],data[:,1],color='blue',label='real')
plt.scatter(fake_X[:,0],fake_X[:,1],color='orange',label='generated')
plt.legend()
plt.show()
if __name__ == '__main__':
lr_D,lr_G,latent_dim,num_epochs=0.05,0.005,2,20
generator=net_G()
discriminator=net_D()
train(discriminator,generator,data_iter,num_epochs,lr_D,lr_G,latent_dim,data)
参考:https://zhuanlan.zhihu.com/p/28853704
我们提出,要建立图像的良好特征,训练GAN是一种方法,之后,我们会把判别器和生成器都作为可再用的特征提取器,用到监督学习的任务中。GAN实际上为最大似然估计的相关技术提供了一种颇具吸引力的替代方案。更进一步,GAN的学习过程,和对启发式损失函数要求不高,这两点对表征学习具有吸引力。GAN出了名的训练不稳定,这经常导致生成器会产出很多荒谬的结果。对于GAN中到底学习到了什么样的中间表征,在当前出版的研究里也并不多见。
In this paper, we make the following contributions:
在这篇文章里,我们做出如下贡献:
我们提出并评价了这样一件事情:什么样的GAN体系拓扑能让训练更加稳定。 我们将这一类架构称作是DCGAN
我们使用图像分类任务上训练出来的判别器和其他的非监督算法做了比较。
我们对GAN学习到的特征做出了可视化,并经验性的证明了特殊的特征表征了特殊的对象。
对图像上下文的表征:一个经典的非监督表征学习手段是做出数据聚类(例如使用k-means),之后利用聚类结果来改善分类结果。在图像这一类场景下,可以对图像进行批处理,利用多个图像的聚类来学习到更有效的图像表征,另一个很流行的理论是训练自编码器(卷积式的自编码器),主流存在两种方式:一种是分离编码中向量的意义和位置,另一种是分析编码的梯度结构,这两种方式都能将图像作成紧编码,并且尽可能的通过解码器还原图像。这些方法已经被证明能很好的通过图像像素来学习表征。深度置信网络同样也能学习到表征的连续表达。
。
图像的生成模型划分为两个领域:参数化领域和非参数化领域 。非参数领域通常是在图像数据库下做匹配,经常对成批的图像做匹配,它在纹理合成,超分辨率重建和in-paiting中用的较多。参数模型已经被广泛研究过(例如,MNIST中手写数字的纹理合成)。尽管生成真实世界的自然图像这一点在当前并没有很大成功。尽管其中的一些变种已经取得了一定成功,但是这种采样通常十分模糊。其他的,页游诸如面向扩散过程的生成方法。GAN在图像生成方面,具有不可思议的抗噪特性。这种方法中,一种添加拉普拉斯金字塔的方法展示出了较高质量的图像,但是由于在链式乘法模型中引入了噪声,导致生成的对象看上去是摇摆的。循环网络和反卷积网络似乎在自然图像的生成上取得了一定成功,但它们并没有应用到监督任务上。
对现行CNN架构三个改进的学习和纠正。
在生成器中,使用Relu来做激活函数,并在输出层上使用Tanh。我们发现,使用有界的函数更有助于模型迅速在训练分布中覆盖颜色空间。在判别器中,尤其是对高分辨率模型的时候,我们发现Weak Relu更好用。 这是与原始GAN论文的对比,在那篇论文中,它使用Maxout来激活。
稳定DCGAN的架构指导:
所有的图像,都缩放到Tanh激活函数的定义域[-1,1]之内,除此之外没有做任何预处理。所有的模型都使用小批量SGD,一批是128张图。所有的权重都使用正态分布初始化,期望为1,方差为0.02。在LeakReLU中,负向权重全部设置为0.1。有鉴于之前的GAN中使用了动量,我们使用Adam来优化超参数。学习率中发现0.001太大了,使用0.0002。此外,我们发现动量参数β1=0.9β1=0.9的时候,训练波动大也不稳定,所以设置为0.5使训练稳定。
图1:用于LSUN场景的DCGAN的生成器。一个100维,服从均匀分布的噪声Z在一个小范围卷积表征空间中投影为许多特征映射,接着连续4个小步长卷积,将这个噪声作成一个64*64的像素图像,毫无疑问,没有全连接层或者pooling层。
通过训练我们得到如下结论:
import matplotlib.pyplot as plt
from torch.utils.data import DataLoader
from torch import nn
import numpy as np
from torch.autograd import Variable
import torch
from torchvision.datasets import ImageFolder
from torchvision.transforms import transforms
import zipfile
cuda = True if torch.cuda.is_available() else False
print(cuda)
#数据预处理
data_dir='/home/kesci/input/pokemon8600/'
batch_size=256
transform=transforms.Compose([
transforms.Resize((64,64)),
transforms.ToTensor(),
transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))
])
pokemon=ImageFolder(data_dir+'pokemon',transform)
data_iter=DataLoader(pokemon,batch_size=batch_size,shuffle=True)
#定义生成器
class G_block(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size=4,strides=2, padding=1):
super(G_block,self).__init__()
self.conv2d_trans=nn.ConvTranspose2d(in_channels, out_channels, kernel_size=kernel_size,
stride=strides, padding=padding, bias=False)
self.batch_norm=nn.BatchNorm2d(out_channels,0.8)
self.activation=nn.ReLU()
def forward(self,x):
return self.activation(self.batch_norm(self.conv2d_trans(x)))
Tensor=torch.cuda.FloatTensor
x=Variable(Tensor(np.zeros((2,3,16,16))))
g_blk=G_block(3,20)
g_blk.cuda()
print(g_blk(x).shape)
class net_G(nn.Module):
def __init__(self,in_channels):
super(net_G,self).__init__()
n_G=64
self.model=nn.Sequential(
G_block(in_channels,n_G*8,strides=1,padding=0),
G_block(n_G*8,n_G*4),
G_block(n_G*4,n_G*2),
G_block(n_G*2,n_G),
nn.ConvTranspose2d(
n_G,3,kernel_size=4,stride=2,padding=1,bias=False
),
nn.Tanh()
)
def forward(self,x):
x=self.model(x)
return x
def weights_init_normal(m):
classname = m.__class__.__name__
if classname.find("Conv") != -1:
torch.nn.init.normal_(m.weight.data, mean=0, std=0.02)
elif classname.find("BatchNorm2d") != -1:
torch.nn.init.normal_(m.weight.data, mean=1.0, std=0.02)
torch.nn.init.constant_(m.bias.data, 0.0)
#定义识别器
class D_block(nn.Module):
def __init__(self,in_channels,out_channels,kernel_size=4,strides=2,
padding=1,alpha=0.2):
super(D_block,self).__init__()
self.conv2d=nn.Conv2d(in_channels,out_channels,kernel_size,strides,padding,bias=False)
self.batch_norm=nn.BatchNorm2d(out_channels,0.8)
self.activation=nn.LeakyReLU(alpha)
def forward(self,X):
return self.activation(self.batch_norm(self.conv2d(X)))
class net_D(nn.Module):
def __init__(self,in_channels):
super(net_D,self).__init__()
n_D=64
self.model=nn.Sequential(
D_block(in_channels,n_D),
D_block(n_D,n_D*2),
D_block(n_D*2,n_D*4),
D_block(n_D*4,n_D*8)
)
self.conv=nn.Conv2d(n_D*8,1,kernel_size=4,bias=False)
self.activation=nn.Sigmoid()
# self._initialize_weights()
def forward(self,x):
x=self.model(x)
x=self.conv(x)
x=self.activation(x)
return x
#训练
def update_D(X,Z,net_D,net_G,loss,trainer_D):
batch_size=X.shape[0]
Tensor=torch.cuda.FloatTensor
ones=Variable(Tensor(np.ones(batch_size,)),requires_grad=False).view(batch_size,1)
zeros = Variable(Tensor(np.zeros(batch_size,)),requires_grad=False).view(batch_size,1)
real_Y=net_D(X).view(batch_size,-1)
fake_X=net_G(Z)
fake_Y=net_D(fake_X).view(batch_size,-1)
loss_D=(loss(real_Y,ones)+loss(fake_Y,zeros))/2
loss_D.backward()
trainer_D.step()
return float(loss_D.sum())
def update_G(Z,net_D,net_G,loss,trainer_G):
batch_size=Z.shape[0]
Tensor=torch.cuda.FloatTensor
ones=Variable(Tensor(np.ones((batch_size,))),requires_grad=False).view(batch_size,1)
fake_X=net_G(Z)
fake_Y=net_D(fake_X).view(batch_size,-1)
loss_G=loss(fake_Y,ones)
loss_G.backward()
trainer_G.step()
return float(loss_G.sum())
def train(net_D,net_G,data_iter,num_epochs,lr,latent_dim):
loss=nn.BCELoss()
Tensor=torch.cuda.FloatTensor
trainer_D=torch.optim.Adam(net_D.parameters(),lr=lr,betas=(0.5,0.999))
trainer_G=torch.optim.Adam(net_G.parameters(),lr=lr,betas=(0.5,0.999))
plt.figure(figsize=(7,4))
d_loss_point=[]
g_loss_point=[]
d_loss=0
g_loss=0
for epoch in range(1,num_epochs+1):
d_loss_sum=0
g_loss_sum=0
batch=0
for X in data_iter:
X=X[:][0]
batch+=1
X=Variable(X.type(Tensor))
batch_size=X.shape[0]
Z=Variable(Tensor(np.random.normal(0,1,(batch_size,latent_dim,1,1))))
trainer_D.zero_grad()
d_loss = update_D(X, Z, net_D, net_G, loss, trainer_D)
d_loss_sum+=d_loss
trainer_G.zero_grad()
g_loss = update_G(Z, net_D, net_G, loss, trainer_G)
g_loss_sum+=g_loss
d_loss_point.append(d_loss_sum/batch)
g_loss_point.append(g_loss_sum/batch)
print(
"[Epoch %d/%d] [D loss: %f] [G loss: %f]"
% (epoch, num_epochs, d_loss_sum/batch_size, g_loss_sum/batch_size)
)
plt.ylabel('Loss', fontdict={ 'size': 14})
plt.xlabel('epoch', fontdict={ 'size': 14})
plt.xticks(range(0,num_epochs+1,3))
plt.plot(range(1,num_epochs+1),d_loss_point,color='orange',label='discriminator')
plt.plot(range(1,num_epochs+1),g_loss_point,color='blue',label='generator')
plt.legend()
plt.show()
print(d_loss,g_loss)
Z = Variable(Tensor(np.random.normal(0, 1, size=(21, latent_dim, 1, 1))),requires_grad=False)
fake_x = generator(Z)
fake_x=fake_x.cpu().detach().numpy()
plt.figure(figsize=(14,6))
for i in range(21):
im=np.transpose(fake_x[i])
plt.subplot(3,7,i+1)
plt.imshow(im)
plt.show()
if __name__ == '__main__':
lr,latent_dim,num_epochs=0.005,100,50
train(discriminator,generator,data_iter,num_epochs,lr,latent_dim)
参考:https://blog.csdn.net/xiening0618/article/details/79417734