基于Keras的生成对抗网络(3)——利用Keras搭建CGAN生成手写体数字并贴上标签
from keras.datasets import mnist
from keras.models import Sequential,Model
from keras.layers import Input, Dense, Reshape, Flatten,Embedding,multiply
from keras.layers import BatchNormalization, Activation,Dropout
from keras.layers import LeakyReLU
from keras.optimizers import Adam
from keras.utils.vis_utils import plot_model
import matplotlib.pyplot as plt
import numpy as np
import os
class CGAN():
def __init__(self):
#28,28,1
self.img_shape = (28,28,1)
self.num_classes=10 #分成10类
self.latent_dim=100 #输入维度--100
optimizer = Adam(0.0002, 0.5)#定义Adam优化器
#判别器
self.discriminator=self.build_discriminator()
losses=['binary_crossentropy','sparse_categorical_crossentropy']
self.discriminator.compile(loss=losses, optimizer=optimizer, metrics=['accuracy'])#定义loss函数和优化器
#生成器
self.generator=self.build_generator()
noise = Input(shape=(self.latent_dim,))
label=Input(shape=(1,))
img = self.generator([noise,label])
#训练生成器
# 在训练generator的时候不训练discriminator
self.discriminator.trainable = False#冻结discriminator
valid,target_label = self.discriminator(img)# 对生成的假图片进行预测
self.combined = Model([noise,label], [valid,target_label])#在Model中discriminator已经被冻结,仅剩下generator在运行了
self.combined.compile(loss=losses, optimizer=optimizer)#定义loss函数和优化器
#定义生成器模型
#输入:100维向量;输出:28*28图像(像素大小为(-1,1)--tanh)
def build_generator(self):
model=Sequential()
#输入
label=Input(shape=(1,),dtype='int32')
label_embedding=Flatten()(Embedding(self.num_classes, self.latent_dim)(label))
noise=Input(shape=(self.latent_dim,))
#带标签的随机数
model_input=multiply([noise,label_embedding])#相乘作为输入
#第一层全连接层:784-->256
model.add(Dense(256, input_dim=self.latent_dim))#全连接
model.add(LeakyReLU(alpha=0.2))#激活函数
model.add(BatchNormalization(momentum=0.8))#标准化
#第二层全连接层:256-->512
model.add(Dense(512))
model.add(LeakyReLU(alpha=0.2))
model.add(BatchNormalization(momentum=0.8))
#第三层全连接层:512-->1024
model.add(Dense(1024))
model.add(LeakyReLU(alpha=0.2))
model.add(BatchNormalization(momentum=0.8))
#第四层全连接层(输出层):1024-->784
model.add(Dense(np.prod(self.img_shape), activation='tanh'))#product--乘积,即28*28*1=784
model.add(Reshape(self.img_shape))
img = model(model_input)
plot_model(model, to_file='', show_shapes=True, show_layer_names=False, rankdir='TB')#绘制模型
return Model([noise,label], img)
#定义判别器模型
#输入:28*28图像;输出:0-1数字
def build_discriminator(self):
model=Sequential()
img=Input(shape=self.img_shape)
label=Input(shape=(1,),dtype='int32')
model.add(Flatten(input_shape=self.img_shape))#铺平
#第一层全连接层:784-->512
model.add(Dense(512))
model.add(LeakyReLU(alpha=0.2))
#第二层全连接层:512-->512
model.add(Dense(512))
model.add(LeakyReLU(alpha=0.2))
model.add(Dropout(0.2))
#第三层全连接层:512-->512
model.add(Dense(512))
model.add(LeakyReLU(alpha=0.2))
model.add(Dropout(0.2))
features = model(img)
#一个是真假,一个是类别向量
validity=Dense(1,activation="sigmoid")(features)#判断真伪
label=Dense(self.num_classes,activation="softmax")(features)#判断是几
plot_model(model, to_file='', show_shapes=True, show_layer_names=False, rankdir='TB')#绘制模型
return Model(img,[validity,label])
#定义训练函数
def train(self,epochs,batch_size=128,sample_interval=50):
#创建训练数据集
(X_train, y_train), (_, _) = mnist.load_data() # 从mnist数据集中获得数据
X_train =(X_train.astype(np.float32)-127.5)/127.5 # 将X_train的值转换为float类型,并标准化为(-1,1)
X_train = np.expand_dims(X_train, axis=3) #60000*28*28变为60000*28*28*1;X_train的shape是60000*28*28*1
y_train=y_train.reshape(-1,1)
#创建标签(0或者1)--二分类问题
valid = np.ones((batch_size, 1))#全1阵--真
fake = np.zeros((batch_size, 1))#全0阵--假
for epoch in range(epochs):
#训练discriminator
idx=np.random.randint(0,X_train.shape[0],batch_size)#随机选择batch_size个数
imgs,labels=X_train[idx],y_train[idx]#真图像,真标签
noise = np.random.normal(0, 1, (batch_size, self.latent_dim))#生成正态分布噪声
sampled_labels=np.random.randint(0,10,(batch_size,1))
gen_imgs = self.generator.predict([noise,sampled_labels])#假图像
#训练discriminator
d_loss_real = self.discriminator.train_on_batch(imgs, [valid,labels])#真图像训练
d_loss_fake = self.discriminator.train_on_batch(gen_imgs, [fake,sampled_labels])#假图像训练
d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)#数据求和
#训练生成器
g_loss=self.combined.train_on_batch([noise,sampled_labels],[valid,sampled_labels])
print("steps:%d [D loss: %f, acc.: %.2f%%,ap_acc:%.2f%%] [G loss: %f]"% (epoch, d_loss[0], 100*d_loss[3],100*d_loss[4],g_loss[0]))
#迭代50次打印一次
if epoch % sample_interval == 0:
self.sample_images(epoch)
def sample_images(self,epoch):
r,c=2,5
noise=np.random.normal(0,1,(r*c,self.latent_dim))#生成随机噪声
sampled_labels = np.arange(0, 10).reshape(-1, 1)
gen_imgs = self.generator.predict([noise,sampled_labels])#生成器预测的图像
gen_imgs = 0.5 * gen_imgs + 0.5 #将generator输出的(-1,1)像素值反归一化为(0,1)
#绘制5*5图像
fig, axs = plt.subplots(r, c)
cnt = 0
for i in range(r):
for j in range(c):
axs[i,j].imshow(gen_imgs[cnt, :,:,0], cmap='gray')
axs[i,j].set_title("Digit: %d" % sampled_labels[cnt])
axs[i,j].axis('off')
cnt += 1
fig.savefig("images/%" % epoch)
plt.close()
if __name__=='__main__':
if not os.path.exists("./images"):
os.makedirs("./images")
cgan = CGAN()
cgan.train(epochs=30000, batch_size=256, sample_interval=200)