举个例子:手写数字识别
什么是手写数字识别?就是将一个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)
生成器想要生成图片骗过鉴别器,鉴别器想要辨别出假的图片,由此可以看出它们是竞争关系
既然是竞争关系,那么必然不可能让一家独大,只有你来我往这样才会持久。所以这就证明了这两个模型是交替训练,而不是训练好一个模型之后再去训练另一个模型。
注:
因为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)
我们首先拿真实数据训练鉴别器,标签是真,然后使用生成器生成的数据训练鉴别器,标签为假,然后训练生成器,目标是真
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