GAN简介
生成式对抗网络(GAN, Generative Adversarial Networks )是一种深度学习模型,是近年来复杂分布上无监督学习最具前景的方法之一。
模型通过框架中(至少)两个模块:生成模型(Generative Model)和判别模型(Discriminative Model)的互相博弈学习产生相当好的输出。
通俗的讲,初始的生成网络(generated)与判别网络(discriminator)什么都不懂, 把初始的生成网络比喻成画家,那么初始的判别网络就是鉴画师,什么都不懂的画师画了幅画,于是给鉴画师辨认是否画的好,鉴画师也什么都不懂,只能对比世界名画判断好坏, 将意见反馈给画师,不停重复操作,画师不断进步的同时,鉴画师也会进步,画师最终会努力接近世界名画的水平,这就是GAN。
GAN应用
目前GAN最常使用的地方就是图像生成,如超分辨率任务,语义分割, 数据增加等等
GAN的基本结构
GAN是将一组向量矩阵输入生成网络中训练出一组虚假数据,再与真数据进行合并进入判别网络训练,虚假数据的判别网络训练标签为0,真数据的判别网络训练标签为1,不断循环训练,使生成图像逼近原始图像。
GAN的基本原理
首先从现有的数据中挑选出一批数据,组成Pdata(x)Pdata(x),然后训练一个PG(x;θ)PG(x;θ)来产生数据(例如一个高斯混合模型),我们希望产生的数据集PG(x;θ)PG(x;θ)与原来的数据Pdata(x)Pdata(x)越接近越好,即使得下面的似然函数达到最大值:
因此需要求得的参数为:
取对数,得:
GAN实践
激活函数
tanh
ReLu
sigmoid
Leaky ReLUs
ReLU是将所有的负值都设为零,相反,Leaky ReLU是给所有负值赋予一个非零斜率。Leaky ReLU激活函数是在声学模型(2013)中首次提出的。以数学的方式我们可以表示为:
是(1,+∞)区间内的固定参数。
下图是ReLU、Leaky ReLU、PReLU和RReLU的比较:
GAN在MNIST数据集上的体现
import os
os.environ["KERAS_BACKEND"] = "tensorflow"
import numpy as np
from tqdm import tqdm
import matplotlib.pyplot as plt
from keras.layers import Input
from keras.models import Model, Sequential
from keras.layers.core import Reshape, Dense, Dropout, Flatten
from keras.layers.advanced_activations import LeakyReLU
from keras.layers.convolutional import Convolution2D, UpSampling2D
from keras.layers.normalization import BatchNormalization
from keras.datasets import mnist
from keras.optimizers import Adam
from keras import backend as K
from keras import initializers
K.set_image_dim_ordering('th')
np.random.seed(1000)
# 为了与其他GAN实现保持一致,维度保留为100。
randomDim = 100
# MNIST数据加载
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = (X_train.astype(np.float32) - 127.5) / 127.5
X_train = X_train.reshape(60000, 784)
# Optimizer
adam = Adam(lr=0.0002, beta_1=0.5)
generator = Sequential()
generator.add(Dense(256, input_dim=randomDim, kernel_initializer=initializers.RandomNormal(stddev=0.02)))
generator.add(LeakyReLU(0.2))
generator.add(Dense(512))
generator.add(LeakyReLU(0.2))
generator.add(Dense(1024))
generator.add(LeakyReLU(0.2))
generator.add(Dense(784, activation='tanh'))
generator.compile(loss='binary_crossentropy', optimizer=adam)
discriminator = Sequential()
discriminator.add(Dense(1024, input_dim=784, kernel_initializer=initializers.RandomNormal(stddev=0.02)))
discriminator.add(LeakyReLU(0.2))
discriminator.add(Dropout(0.3))
discriminator.add(Dense(512))
discriminator.add(LeakyReLU(0.2))
discriminator.add(Dropout(0.3))
discriminator.add(Dense(256))
discriminator.add(LeakyReLU(0.2))
discriminator.add(Dropout(0.3))
discriminator.add(Dense(1, activation='sigmoid'))
discriminator.compile(loss='binary_crossentropy', optimizer=adam)
# 联合网络
discriminator.trainable = False
ganInput = Input(shape=(randomDim,))
x = generator(ganInput)
ganOutput = discriminator(x)
gan = Model(inputs=ganInput, outputs=ganOutput)
gan.compile(loss='binary_crossentropy', optimizer=adam)
dLosses = []
gLosses = []
# 绘制每个批次的损失
def plotLoss(epoch):
plt.figure(figsize=(10, 8))
plt.plot(dLosses, label='Discriminitive loss')
plt.plot(gLosses, label='Generative loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.savefig('images/gan_loss_epoch_%d.png' % epoch)
#创建一个生成MNIST图像
def plotGeneratedImages(epoch, examples=100, dim=(10, 10), figsize=(10, 10)):
noise = np.random.normal(0, 1, size=[examples, randomDim])
generatedImages = generator.predict(noise)
generatedImages = generatedImages.reshape(examples, 28, 28)
plt.figure(figsize=figsize)
for i in range(generatedImages.shape[0]):
plt.subplot(dim[0], dim[1], i + 1)
plt.imshow(generatedImages[i], interpolation='nearest', cmap='gray_r')
plt.axis('off')
plt.tight_layout()
plt.savefig('images/gan_generated_image_epoch_%d.png' % epoch)
# 保存生成器和鉴别器网络(和权重),以供以后使用
def saveModels(epoch):
generator.save('models/gan_generator_epoch_%d.h5' % epoch)
discriminator.save('models/gan_discriminator_epoch_%d.h5' % epoch)
def train(epochs=1, batchSize=128):
batchCount = X_train.shape[0] / batchSize
print('Epochs:', epochs)
print('Batch size:', batchSize)
print('Batches per epoch:', batchCount)
for e in range(1, epochs + 1):
print('-' * 15, 'Epoch %d' % e, '-' * 15)
# tqdm在运行行记录输出每轮训练进度
for _ in tqdm(range(int(batchCount))):
# 获得一组随机的输入噪声和图像
noise = np.random.normal(0, 1, size=[batchSize, randomDim])
imageBatch = X_train[np.random.randint(0, X_train.shape[0], size=batchSize)]
# 生成虚假MNIST图像
generatedImages = generator.predict(noise)
X = np.concatenate([imageBatch, generatedImages])
# 生成和实际数据的标签
yDis = np.zeros(2 * batchSize)
# One-sided label smoothing
yDis[:batchSize] = 0.9
# 训练判别器
discriminator.trainable = True
dloss = discriminator.train_on_batch(X, yDis)
# 训练生成器
noise = np.random.normal(0, 1, size=[batchSize, randomDim])
yGen = np.ones(batchSize)
discriminator.trainable = False
gloss = gan.train_on_batch(noise, yGen)
dLosses.append(dloss)
gLosses.append(gloss)
if e == 1 or e % 20 == 0:
plotGeneratedImages(e)
saveModels(e)
# 描绘每个时代的损失
plotLoss(e)
if __name__ == '__main__':
train(200, 128)
训练结果
迭代1轮对抗生成的图片
迭代100轮对抗生成的图片
迭代200轮对抗生成的图片
模型实际在迭代80轮左右就相对成型了