快疯了…刚刚差点写完了,电脑突然重启了,还要重新写一遍,吐血啊!!!
最近在学深度学习,了解到了GAN,不得不说,GAN真的是一个神奇的工作。做这个的理由也是因为看了,这就是其中的一个课后作业——利用GAN生成动漫头像(这个也太酷了吧,燃烧中二之魂~)。
下面放上一些生成结果:
因为训练的次数并没有很多,效果看起来还是那么一回事,但是仔细看其实有一些结果就很奇怪了。
这里我是使用的Keras,因为Keras对新手太友好了,非常好上手,看看就能够很容易的搭建自己的网络了
本文使用的全部代码大概只有170行左右!!!
网上的Keras的例子其实挺多的,但是基本只有作用在mnist数据集,这里我们使用自己的数据集(不知是谁分享的,感谢~),而且使用DCGAN作为网络结构。
获得数据集(提取码: 7s9b 数据集有五万多个头像)
获取代码 (提取码: 2y75 )
本文默认是对GAN和CNN有一定的了解的,只介绍实现过程和代码。如果没有了解过的同学可以看一下我写的
一、生成器模型
网络的输入是一个100维的噪声z,最终输出是一个64*64*3的图像,当然这个图片也可以自己指定大小(不过要调整网络参数),本文使用的是96*96*3的图片
代码实现:这里我们定义一个线性模型,我们可以使用add函数不断的加层,最后完成网络模型的搭建。Dense是一个全连接层,输入的维度就是100,输出就是512*6*6,表示100个神经元与512*6*6的神经元相连,Reshape是吧512*6*6转换为(6,6,512)这种格式的数据。UpSampling2D是一个上采样层,他会使n*n*3的图片变成2n*2n*3的图片,Conv2D是一个卷积层,他的卷积核是5*5的,padding=‘same’表示不改变图片的大小。Activation表示使用的激活函数,这里的激活函数除了最后一层是tanh其他均是relu。BatchNormalization是规范化,即使得其输出数据的均值接近0,其标准差接近1,使训练过程更加平稳。
def build_generator(self): model = Sequential() model.add(Dense(512*6*6,activation=relu,input_dim=self.latent_dim)) #输入维度为100 model.add(Reshape((6,6,512))) model.add(UpSampling2D()) #进行上采样,变成14*14*128 model.add(Conv2D(256,kernel_size=5,padding=same)) model.add(BatchNormalization(momentum=0.8)) model.add(Activation("relu"))# model.add(UpSampling2D()) model.add(Conv2D(128, kernel_size=5, padding="same")) model.add(BatchNormalization(momentum=0.8)) model.add(Activation("relu")) model.add(UpSampling2D()) model.add(Conv2D(64, kernel_size=5, padding="same")) model.add(BatchNormalization(momentum=0.8)) model.add(Activation("relu")) model.add(UpSampling2D()) model.add(Conv2D(self.channels, kernel_size=5, padding="same")) model.add(Activation("tanh")) model.summary() #打印网络参数 noise = Input(shape=(self.latent_dim,)) img = model(noise) return Model(noise,img) #定义一个 一个输入noise一个输出img的模型
二、判别器模型
判别器的输入是一个图片(这里没有画出来,其实生成器的输出就是判别器的输入或者是真实图片),最终的输出是一个0~1的数,表示图片是真实图片的概率,输入的图片越像真实的,这个概率就越大。
代码实现:
这里我们仍然定义一个线性模型,这里我除了最后一层使用sigmoid其他激活函数均是LeakyReLU(alpha表示函数在第三象限的斜率,是一个大于零的数),因为我们最终要输出一个概率值,而sigmoid可以让输出的值取值为0~1之间。Dropout层是用来防止过拟合的,其他和生成器相差不大。
def build_discriminator(self): model = Sequential() dropout = 0.4 model.add(Conv2D(64, kernel_size=5, strides=2, input_shape=self.img_shape, padding="same")) model.add(LeakyReLU(alpha=0.2)) model.add(Dropout(dropout)) model.add(Conv2D(128, kernel_size=5, strides=2, padding="same")) model.add(ZeroPadding2D(padding=((0, 1), (0, 1)))) model.add(BatchNormalization(momentum=0.8)) model.add(LeakyReLU(alpha=0.2)) model.add(Dropout(dropout)) model.add(Conv2D(256, kernel_size=5, strides=2, padding="same")) model.add(BatchNormalization(momentum=0.8)) model.add(LeakyReLU(alpha=0.2)) model.add(Dropout(dropout)) model.add(Conv2D(512, kernel_size=5, strides=1, padding="same")) model.add(BatchNormalization(momentum=0.8)) model.add(LeakyReLU(alpha=0.2)) model.add(Dropout(dropout)) model.add(Flatten()) model.add(Dense(1, activation=sigmoid)) model.summary() img = Input(shape=self.img_shape) validity = model(img) return Model(img,validity)
三、训练
我们知道对于判别器D来说是非常好训练的,我们可以输入一个图片然后得到一个概率,然后回传调参,但是对于生成器G来说,他的输出是一个图片,那么如何训练G呢。我们这里是创建一个combined的模型,这个模型连接了D和G,然后我们控制D不进行参数调整,这样我们训练combined即是训练G了。
#对判别器进行构建和编译 self.discriminator = self.build_discriminator() self.discriminator.compile(loss=binary_crossentropy,optimizer=optimizer,metrics=[accuracy]) #对生成器进行构造 self.generator = self.build_generator() # The generator takes noise as input and generates imgs z = Input(shape=(self.latent_dim,)) img = self.generator(z) # 总体模型只对生成器进行训练 self.discriminator.trainable = False # 从生成器中生成的图 经过判别器获得一个valid valid = self.discriminator(img) self.combined = Model(z,valid) self.combined.compile(loss=binary_crossentropy, optimizer=optimizer)
这时候我们就可以开始训练我们的模型了,在训练GAN时,我们需要交替训练生成器G和判别器D。我们先训练D然后再训练G,如此交替
def train(self,epochs,batch_size=128,save_interval = 50): # ground truths valid = np.ones((batch_size, 1)) fake = np.zeros((batch_size, 1)) for epoch in range(epochs): imgs = self.load_batch_imgs(batch_size,faces) # 生成一个batch_size的噪声用于生成图片 noise = np.random.normal(0, 1, (batch_size, self.latent_dim)) gen_imgs = self.generator.predict(noise) # 训练判别器 d_loss_real = self.discriminator.train_on_batch(imgs, valid) d_loss_fake = self.discriminator.train_on_batch(gen_imgs, fake) d_loss = 0.5 * np.add(d_loss_real, d_loss_fake) # 训练生成器 g_loss = self.combined.train_on_batch(noise, valid) print ("%d [D loss: %f, acc.: %.2f%%] [G loss: %f]" % (epoch, d_loss[0], 100*d_loss[1], g_loss)) if epoch % save_interval == 0: self.combined.save(./model/combined_model_%d.h5%epoch) self.discriminator.save(./model/discriminator_model_%d.h5%epoch ) self.save_imgs(epoch)
虽然代码是写出来了,但是现在只是知其所以然…,还是有很多问题是不清楚的,所以还是要继续学习深度学习的原理。
这里说一下训练的问题,如果训练的时候d_loss很快就趋近于零,这个时候说明判别器训练的太好了,这个时候我们采取的措施可以是多训练几次生成器,而判别器只训练一次。
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/290432.html