实现一个简单的生成对抗网络(详细版)

生成对抗网络-实践篇

生成?怎么生成?

​ 举个例子:手写数字识别

​ 什么是手写数字识别?就是将一个28乘28的图片(图片内容是数据0-9,每个图片仅有一个数字)输入到一个模型中,模型会返回它认为的这张图像上0写的哪一个值。

​ 从例子中我们可以看出输出的值,是远远小于输入的值的。(输入784,输出1)

​ 既然可以从784到1,那么为什么不可以从1到784,也就是说我给模型一个数值3,给我生成784张写着3的图片。

​ 完成这个任务的模型就是生成模型。

​ 但是我们会发现一个问题,如果给定一个3,那么生成的784张图片全是一模一样的,这显然不行,因为既然784张完全一样,那我还不如就要一张好了。所以现在的问题就变成了要让生成的图片有多样性

对抗?怎么对抗?

​ 接着上面的问题来说,如果一个模型A接受到了3,那么无论怎么样,它生成的都会是一样的。可是要是这个模型A看不见3呢,它也许会生成一个6,当然也可能是一个篮球,当然这也不符合我们的要求,我们是要多样化,但是你不能太离谱,起码生成的得是个数字。这个时候,有一个模型B走过来看了看A生成的6和篮球,又看了看自己手中的3,对A说篮球零分,数字六70分。显然下次模型A不会在生成篮球了,很大可能会生成数字。到这里我们就解决了刚才的问题,既可以让模型生成的图片有多样性,而且图片符合我们的预期。

​ 上面例子的关键是有一个模型B知道我们要什么,而且能给模型A反馈。

​ 模型A:我们称它为生成器 (generator)。

​ 同时,我们把模型B称为鉴别器 (discriminator)

​ 生成器的作用是生成图片,并且能够让鉴别器认为这就是真的图片

​ 鉴别器的作用是看生成器生成的图片和真实的图片,判断生成的图片是不是真的图片

​ 由生成器和鉴别器组成的网络就被称作生成对抗网络(Generative Adversarial Network,GAN)

​ 生成器想要生成图片骗过鉴别器,鉴别器想要辨别出假的图片,由此可以看出它们是竞争关系

​ 既然是竞争关系,那么必然不可能让一家独大,只有你来我往这样才会持久。所以这就证明了这两个模型是交替训练,而不是训练好一个模型之后再去训练另一个模型。

注:

  • 分类是对数据的简化。分类神经网络把较多的输入值缩减成很少的输出值,每个输出值对应一个类别。
  • 生成是对数据的扩展。一个生成神经网络将少量的输入种子值扩展成大量的输出值,例如图像像素值。
  • 生成对抗网络(GAN)由两个神经网络组成,一个是生成器,另一个是鉴别器,它们被设计为竞争对手。鉴别器经过训练后,可将训练集中的数据分类为真实数据,将生成器产生的数据分类为伪造数据;生成器在训练后,能创建可以以假乱真的数据来欺骗鉴别器。
  • 标准的 GAN 训练循环有3个步骤。(1)用真实的训练数据集训练鉴别器;(2)用生成的数据训练鉴别器;(3)训练生成器生成数据,并使鉴别器以为它是真实数据。

动手构建一个GAN

​ 因为01分类最简单,所以我们在这里就使用01分类

​ 既然是01分类真实的数据肯定不是0就是1

#在这里我们给出三段代码,一个是真实数据,一个是01格式的数据,一个是噪声
#01格式数据
import numpy as np
def one_zeros():
    data = np.array([1,0,1,0])
    return data

def real_data():
    real = np.array([
        np.random.uniform(0.8,1.0),
        np.random.uniform(0,0.2),
        np.random.uniform(0.8,1.0),
        np.random.uniform(0,0.2),
    ])
    return real

def g_random(size):
    random_data = np.random.rand(size)
    return random_data

鉴别器

接下来实现鉴别器,我们不妨想想实现一个鉴别器需要什么?毫无疑问的是,首先确定输入输出,根据上文中的数据可以看出输入的4,那么输出应该是什么,既然是鉴别器,肯定是用判别我输入的格式是不是1010这种,那么输出应该是1,输出是真或者是假,显然使用1代表真,0代表假更合适。ok,目前已经确定了输入输出,我们还要想一下鉴别器应该怎么工作。假设经过一个最简单的三层神经网络,那么第一层, 和第三层已经确定了,神经元个数分别是4和1,中间那一层应该是几,嗯,这个的话,一般来说只要不是太离谱都可以,我们这里设为3,当然你也可以设置为2,或者是1000。当然一般来说不会有人设置为1000。好了,目前我们已经明白了怎么写,下面就一起来coding吧,

class discriminator(nn.Layer):
    
    def __init__(self):
        super().__init__()
        
        self.model = nn.Sequential(
            nn.Linear(4,3),
            nn.Sigmoid(),
            nn.Linear(3,1),
            nn.Sigmoid()
        )

    def forward(self,x):
        x = self.model(x)
        return x

写完之后,我们当然想要知道,这个鉴别器有没有用,下面该写训练函数了,思路仍然是先考虑输入输出,输入应该有需要预测的数据和目标数据,输出是什么,既然是训练,其实可以没有输出函数,那么接下来思考一下函数的功能,既然要训练了肯定要有损失函数,然后梯度清零,损失回传,梯度更新。结束,开始coding

def train(model,inputs,targets):
    outputs = model(inputs)
    loss_function = nn.MSELoss()
    loss = loss_function(outputs,targets)
    
    if(count%10==0):
        
        loss_list.append(loss.item())
    if(count%1000==0):
        print(count)
    optim = paddle.optimizer.SGD(learning_rate=0.01,parameters=model.parameters())
    optim.clear_grad()
    loss.backward()
    optim.step()

然后实例化就可

d = discriminator()
count = 0
loss_list = []
for i in range(10000):
    #float64不行
    count +=2
    inputs1 = paddle.to_tensor(real_data(),dtype="float32")
    targets1 = paddle.to_tensor([1.0],dtype="float32")
    train(d,inputs1,targets1)
    inputs2 = paddle.to_tensor(g_random(4),dtype="float32")
    targets2 = paddle.to_tensor([0.0],dtype="float32")
    train(d,inputs2,targets2)

生成器

​ 有了刚才鉴别器的经验,我们可以很轻松的写出生成器,生成器的输入输出刚好和判别器相反,所以我们完全可以拿来直接用,只不过输入输出翻一下就可

class generaotr(nn.Layer):
    
    def __init__(self):
        super().__init__()
        
        self.model = nn.Sequential(
            nn.Linear(1,3),
            nn.Sigmoid(),
            nn.Linear(3,4 ),
            nn.Sigmoid()
        )

    def forward(self,x):
        x = self.model(x)
        return x

​ 对于生成器的训练函数来说,我们需要借助鉴别器来帮它训练,因为它自己没有办法判断自己的生成是不是对的。所以生成器训练函数的输入应该有生成器,鉴别器,输入的数据(种子),目标数据,知道了这些就可以写代码了

def train_G(G,D,inputs,outputs):
    g_output = G.forward(inputs)
    d_output = D.forward(g_output)
    loss_fun = nn.MSELoss()
    loss = loss_fun(d_output,outputs)
    if(count%10==0):
        loss_list_G.append(loss.item())
    if(count%1000==0):
        print(count)
    optim = paddle.optimizer.SGD(learning_rate=0.01,parameters=model.parameters())
    optim.clear_grad()
    loss.backward()
    optim.step()

当然我也可以是实例化一下看看效果

D = discriminator()
G = generaotr()
count = 0
loss_list_G = []
for i in range(10000):
    #float64不行
    count +=1
    targets1 = paddle.to_tensor(real_data(),dtype="float32")
    inputs1 = paddle.to_tensor([42],dtype="float32")
    train_G(G,D,inputs1,targets1)

训练GAN

我们首先拿真实数据训练鉴别器,标签是真,然后使用生成器生成的数据训练鉴别器,标签为假,然后训练生成器,目标是真

G = generaotr()
D = discriminator()
count = 0
list_list = []
loss_list_G = []
for i in range(10000):
    count += 1
    #用真实的数据训练鉴别器
    inputs1 = paddle.to_tensor(real_data(),dtype="float32")
    targets1 = paddle.to_tensor([1.0],dtype="float32")
    train(D,inputs1,targets1)
    #用生成器训练鉴别器
    inputs2 = G.forward(paddle.to_tensor([0.5],dtype="float32")).detach()
    targets2 = paddle.to_tensor([0.0],dtype="float32")
    train(D,inputs2,targets2)

    #训练生成器
    inputs3 = paddle.to_tensor([0.5],dtype="float32")
    train_G(G,D,targets2,targets1)

不出意外的话,损失应该最后会在0.25处,其实也就是鉴别器看不出生成器生成的数据是真还是假,因为是均方损失,所以0.5的平方是0.25

你可能感兴趣的:(机器学习,生成对抗网络,人工智能,深度学习)