【Keras】 基于GAN自动生成动漫头像


快疯了…刚刚差点写完了,电脑突然重启了,还要重新写一遍,吐血啊!!!

最近在学深度学习,了解到了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

(0)
上一篇 2022年10月2日 19:27
下一篇 2022年10月2日 19:27

相关推荐

发表回复

登录后才能评论