在进阶阶段,将介绍一下GAN生成对抗网络在除图像领域的一些其他应用,本文将先介绍一下利用GAN进行曲线
y
=
x
2
y=x^2
y=x2拟合。
目录
- 一、GAN结构
- 二、函数代码
- 2.1 生成器Generator
- 2.2 判别器Discriminator
- 2.3 生成真实数据
- 2.4 train函数
- 2.5 显示图像sample_images函数
- 三、结果显示
- 四、完整代码
- 五、拓展
一、GAN结构
这里进行曲线拟合利用的是普通GAN的结构,即利用全连接层+激活函数,这里不过多介绍。
二、函数代码
2.1 生成器Generator
生成器的目标是输入一行正态分布随机数,生成曲线
y
=
x
2
y=x^2
y=x2上的点,因此它的输入是一个长度为5的一维的向量,输出一个2维的数据。
函数代码:
#定义生成器模型
#输入:5维向量;输出:2维数据
def build_generator(self):
model=Sequential()
noise=Input(shape=(self.latent_dim,))#输入
# 第一层全连接层:5-->15
model.add(Dense(15, activation='relu', kernel_initializer='he_uniform', input_dim=self.latent_dim))
# 第二层全连接层:15-->2
model.add(Dense(2, activation='linear'))
img = model(noise)
return Model(noise, img)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
生成器模型如下:
2.2 判别器Discriminator
判别器的目的是根据输入的数据判断出真伪。因此它的输入一个长度为2的数据,输出是0到1之间的数,越接近1则代表数据越真实,越接近0则代表数据越虚假。
函数代码:
#定义判别器模型
#输入:2维数据;输出:0-1数字
def build_discriminator(self):
model=Sequential()
img=Input(shape=(self.img_shape,))
# 第一层全连接层:5-->25
model.add(Dense(25, activation='relu', kernel_initializer='he_uniform', input_dim=self.img_shape)) # 全连接层
# 第二层全连接层:25-->1
model.add(Dense(1, activation='sigmoid')) # 输出
validity = model(img)
return Model(img, validity)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
判别器模型如下:
2.3 生成真实数据
生成真实数据的函数目标是生成真实样本。该样本是二维数据,第一维度为x,第二维度为 x 2 x^2 x2,并且生成真伪判别矩阵y,返回X和y数据。
函数代码:
def generate_real_samples(n):
# 生成输入[-0.5, 0.5]
X1 = np.random.rand(n) - 0.5
# 生成输出X1的平方
X2 = X1 * X1
X1 = X1.reshape(n, 1) # (n,1)
X2 = X2.reshape(n, 1) # (n,1)
X = np.hstack((X1, X2)) # 水平堆叠成(n,2)
y = np.ones((n, 1)) # 生成类标签(n,1)
return X, y
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
2.4 train函数
训练的主要思路为:
细致解释:
-
首先对判别器进行训练:
这时候输入值有2种:
一是随机噪声输入生成器产生的假数据和标签0(第一次epoch时,生成器中可训练参数只是初始化的参数值);
二是真正的数据和标签1。对这两种输入值分别经过二元交叉熵函数(binary crossentropy)计算损失值求出d_loss_real和d_loss_fake,最终判别器D的损失值为二者的平均值
d_loss = 0.5 * d_loss_real + 0.5 * d_loss_fake -
然后训练生成器:
训练生成器的时候,停止对判别器的训练,设置=False
为生成器输入随机噪声,将生成器产生的数据打上真实值标签1送入在这次epoch中已经训练好的判别器中,继续通过二元交叉熵函数(binary crossentropy)做loss进行生成器的训练,这样的话生成器就会向着真实值靠近。 -
再次迭代训练:
训练完每次epoch后生成器性能就会增强,然后回到我们的第一步继续训练判别器,再训练生成器,在这样不断地相互博弈和促进下,最终生成器会达到以假乱真的效果。
函数代码:
#定义训练函数
def train(self, epochs=10000, batch_size=128, sample_interval=50):
half_batch = int(batch_size / 2) # 一半真实样本,一半虚假样本
# 创建标签(0或者1)--二分类问题
# valid = ((half_batch, 1))#全1阵--真
fake = np.zeros((half_batch, 1))#全0阵--假
for i in range(epochs):
imgs, y_real = generate_real_samples(half_batch) # 生成真实样本,x_real(64,2);y_real(64,1)
noise = np.random.normal(0, 1, (half_batch, self.latent_dim)) # 生成正态分布噪声
gen_imgs = self.generator.predict(noise) # 假图像
#训练discriminator
d_loss_real = self.discriminator.train_on_batch(imgs, y_real)#真数据训练
d_loss_fake = self.discriminator.train_on_batch(gen_imgs, fake)#假数据训练
d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)#数据求和
#训练生成器
x_gan = np.random.randn(self.latent_dim * batch_size)
x_gan = x_gan.reshape(batch_size, self.latent_dim) # reshape成网络输入
y_gan = np.ones((batch_size, 1)) # 为假样本创建假标签
g_loss=self.combined.train_on_batch(x_gan,y_gan)
print("steps:%d [D loss: %f, acc.: %.2f%%] [G loss: %f]"% (i, d_loss[0], 100*d_loss[1],g_loss))
#迭代50次打印一次
if i % sample_interval == 0:
self.sample_images(i)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
2.5 显示图像sample_images函数
分别绘制真实数据和生成数据的散点图,可视化查看拟合效果,其中红色点为真实样本,蓝色点为生成样本。
函数代码:
def sample_images(self,epoch):
n=100
x_real, y_real = generate_real_samples(n)
x_input = np.random.randn(self.latent_dim * n) # 生成随机噪声
noise = x_input.reshape(n, self.latent_dim) # reshape成网络输入
x_fake = self.generator.predict(noise)#生成器预测的数据
# 绘制散点图
plt.scatter(x_real[:, 0], x_real[:, 1], color='red') # 真实样本
plt.scatter(x_fake[:, 0], x_fake[:, 1], color='blue') # 虚假样本
# ()
plt.savefig("images/%" % epoch)
plt.close()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
三、结果显示
迭代0次、5000次、10000次和20000次结果如下:
可以发现,生成的样本已经非常接近给定的样本了,说明曲线拟合效果还是比较好的。
四、完整代码
完整代码如下:
from keras.optimizers import Adam
from keras.models import Sequential,Model
from keras.layers import Input,Dense
import numpy as np
import matplotlib.pyplot as plt
import os
def generate_real_samples(n):
# 生成输入[-0.5, 0.5]
X1 = np.random.rand(n) - 0.5
# 生成输出X1的平方
X2 = X1 * X1
X1 = X1.reshape(n, 1) # (n,1)
X2 = X2.reshape(n, 1) # (n,1)
X = np.hstack((X1, X2)) # 水平堆叠成(n,2)
y = np.ones((n, 1)) # 生成类标签(n,1)
return X, y
class GAN():
def __init__(self):
self.img_shape =2
self.latent_dim=5 # 输入维度
optimizer=Adam(0.0002,0.5) # 优化器
# 判别器
self.discriminator = self.build_discriminator()
self.discriminator.compile(loss='binary_crossentropy', optimizer=optimizer,
metrics=['accuracy']) # 定义loss函数和优化器
# 生成器
self.generator = self.build_generator()
gan_input = Input(shape=(self.latent_dim,))
img = self.generator(gan_input)
# 训练生成器
# 在训练generator的时候不训练discriminator
self.discriminator.trainable = False # 冻结discriminator
validity = self.discriminator(img) # 对生成的假数据进行预测
self.combined = Model(gan_input, validity) # 在Model中discriminator已经被冻结,仅剩下generator在运行了
self.combined.compile(loss='binary_crossentropy', optimizer=optimizer) # 定义loss函数和优化器
#定义生成器模型
#输入:5维向量;输出:2维数据
def build_generator(self):
model=Sequential()
noise=Input(shape=(self.latent_dim,))#输入
# 第一层全连接层:5-->15
model.add(Dense(15, activation='relu', kernel_initializer='he_uniform', input_dim=self.latent_dim))
# 第二层全连接层:15-->2
model.add(Dense(2, activation='linear'))
img = model(noise)
return Model(noise, img)
#定义判别器模型
#输入:2维数据;输出:0-1数字
def build_discriminator(self):
model=Sequential()
img=Input(shape=(self.img_shape,))
# 第一层全连接层:5-->25
model.add(Dense(25, activation='relu', kernel_initializer='he_uniform', input_dim=self.img_shape)) # 全连接层
# 第二层全连接层:25-->1
model.add(Dense(1, activation='sigmoid')) # 输出
validity = model(img)
return Model(img, validity)
# 定义生成真实样本
#定义训练函数
def train(self, epochs=10000, batch_size=128, sample_interval=50):
half_batch = int(batch_size / 2) # 一半真实样本,一半虚假样本
# 创建标签(0或者1)--二分类问题
# valid = ((half_batch, 1))#全1阵--真
fake = np.zeros((half_batch, 1))#全0阵--假
for i in range(epochs):
imgs, y_real = generate_real_samples(half_batch) # 生成真实样本,x_real(64,2);y_real(64,1)
noise = np.random.normal(0, 1, (half_batch, self.latent_dim)) # 生成正态分布噪声
gen_imgs = self.generator.predict(noise) # 假图像
#训练discriminator
d_loss_real = self.discriminator.train_on_batch(imgs, y_real)#真数据训练
d_loss_fake = self.discriminator.train_on_batch(gen_imgs, fake)#假数据训练
d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)#数据求和
#训练生成器
x_gan = np.random.randn(self.latent_dim * batch_size)
x_gan = x_gan.reshape(batch_size, self.latent_dim) # reshape成网络输入
y_gan = np.ones((batch_size, 1)) # 为假样本创建假标签
g_loss=self.combined.train_on_batch(x_gan,y_gan)
print("steps:%d [D loss: %f, acc.: %.2f%%] [G loss: %f]"% (i, d_loss[0], 100*d_loss[1],g_loss))
#迭代50次打印一次
if i % sample_interval == 0:
self.sample_images(i)
def sample_images(self,epoch):
n=100
x_real, y_real = generate_real_samples(n)
x_input = np.random.randn(self.latent_dim * n) # 生成随机噪声
noise = x_input.reshape(n, self.latent_dim) # reshape成网络输入
x_fake = self.generator.predict(noise)#生成器预测的数据
# 绘制散点图
plt.scatter(x_real[:, 0], x_real[:, 1], color='red') # 真实样本
plt.scatter(x_fake[:, 0], x_fake[:, 1], color='blue') # 虚假样本
# ()
plt.savefig("images/%" % epoch)
plt.close()
if __name__=='__main__':
if not os.path.exists("./images"):
os.makedirs("./images")
gan = GAN()
gan.train(epochs=100000, batch_size=128, sample_interval=5000)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
五、拓展
为了更加直观的显示生成数据的拟合效果,可以对生成的数据点进行拟合,拟合代码如下:
x_fake = sorted(x_fake, key=lambda x_fake: x_fake[0])
x_fake = np.array(x_fake)
z1 = np.polyfit(x_fake[:, 0], x_fake[:, 1], 2) # 用7次多项式拟合,可改变多项式阶数;
p1 = np.poly1d(z1) # 得到多项式系数,按照阶数从高到低排列
print(p1) # 显示多项式
yvals = p1(x_fake[:, 0]) # 可直接使用yvals=(z1,xx)
plt.plot(x_fake[:, 0], yvals, 'b', label='polyfit values')
- 1
- 2
- 3
- 4
- 5
- 6
- 7
第一行代码作用为对x_fake内的二维数据按照x坐标进行从小到大排序,这样才能进行拟合,将排序后的list类型形式转换为ndarray形式,然后利用poly1d函数进行拟合。
训练后拟合结果如下:
可以看到,经过训练,生成样本可以很好的拟合曲线。
如果对你有所帮助,记得点个赞哟~