基于Keras的生成对抗网络(4)——利用Keras搭建ACGAN生成手写体数字并贴上标签
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,UpSampling2D,Conv2D,ZeroPadding2D,Conv2DTranspose
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 ACGAN():
def __init__(self):
#28,28,1
self.img_shape = (28,28,1)
self.latent_dim=100 #输入维度--100
self.num_classes=10 #10分类
optimizer = Adam(0.0002, 0.5)#定义Adam优化器
losses=['binary_crossentropy','sparse_categorical_crossentropy']
#判别器
self.discriminator=self.build_discriminator()
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,1)--tanh)
def build_generator(self):
model=Sequential()
model.add(Dense(256*7*7, activation='relu',input_dim=self.latent_dim))#全连接
model.add(BatchNormalization(momentum=0.8))
model.add(Reshape((7,7,256)))
#第一层转置卷积层
model.add(Conv2DTranspose(128,kernel_size=3,strides=2,padding="same"))
model.add(BatchNormalization(momentum=0.8))
model.add(Activation('relu'))#激活函数
#第二层转置卷积层
model.add(Conv2DTranspose(64,kernel_size=3,strides=2,padding="same"))
model.add(BatchNormalization(momentum=0.8))
model.add(Activation('relu'))#激活函数
#第三层转置卷积层
model.add(Conv2DTranspose(32,kernel_size=3,padding="same"))
model.add(BatchNormalization(momentum=0.8))
model.add(Activation('relu'))#激活函数
#第四层转置卷积层
model.add(Conv2DTranspose(1,kernel_size=3,padding="same"))
model.add(Activation('tanh'))#激活函数
model.summary()#输出模型参数状况
noise=Input(shape=(self.latent_dim,))#输入
label = Input(shape=(1,), dtype='int32')
label_embedding = Flatten()(Embedding(self.num_classes, 100)(label))
model_input = multiply([noise, label_embedding])
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):
img=Input(shape=self.img_shape)
model=Sequential()
#卷积层1
model.add(Conv2D(64,kernel_size=3,strides=2,input_shape=self.img_shape,padding="same"))
model.add(LeakyReLU(alpha=0.2))#激活函数
model.add(Dropout(0.25))
#卷积层2
model.add(Conv2D(128,kernel_size=3,strides=2,padding="same"))
model.add(LeakyReLU(alpha=0.2))#激活函数
model.add(Dropout(0.25))
#卷积层3
model.add(Conv2D(256,kernel_size=3,strides=2,padding="same"))
model.add(LeakyReLU(alpha=0.2))#激活函数
model.add(Dropout(0.25))
#卷积层4
model.add(Conv2D(512,kernel_size=3,strides=1,padding="same"))
model.add(LeakyReLU(alpha=0.2))#激活函数
model.add(Dropout(0.25))
#输出层
model.add(Flatten())#铺平
model.summary()#输出模型参数状况
features = model(img)
#一个是真假,一个是类别向量
validity=Dense(1,activation="sigmoid")(features)#判断真伪
label=Dense(self.num_classes+1,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.save_model()#需要在同级文件夹下新建一个名为saved_model的文件夹
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)
#绘制2*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()
def save_model(self):
def save(model, model_name):
model_path = "saved_model/%" % model_name
weights_path = "saved_model/%s_weights.hdf5" % model_name
options = {"file_arch": model_path,
"file_weights": weights_path}
json_string = model.to_json()
open(options['file_arch'], 'w').write(json_string)
model.save_weights(options['file_weights'])
save(self.generator, "generator")
save(self.discriminator, 'discriminator')
if __name__=='__main__':
if not os.path.exists("./images"):
os.makedirs("./images")
acgan = ACGAN()
acgan.train(epochs=5000, batch_size=256, sample_interval=200)