文章目录

  • 1 导包
  • 2 数据准备
  • 3 定义生成器
  • 4 定义判别器
  • 5 定义损失函数和优化器
  • 6 定义训练批次函数
  • 7 定义可视化训练结果函数
  • 8 定义训练主函数
  • 9 训练
  • 10 结果
  • 11 使用生成器



DCGAN(深度卷积生成对抗网络Deep Convolutional GAN)的论文地址:

论文地址

1 导包

tensorflow版本为2.6.2

import tensorflow as tf 
from tensorflow import keras
from tensorflow.keras import layers
import numpy as np
import os
import glob # 用于读取
import matplotlib.pyplot as plt 
%matplotlib inline

2 数据准备

数据下载地址:kaggle anime-faces数据集,共21551张人物头像,每张图片size为64*64。

image_path = glob.glob(r"../input/anime-faces/data/*.png") # 数据读取

原数据介绍:

This is a dataset consisting of 21551 anime faces scraped from www.getchu.com, which are then cropped using the anime face detection algorithm in https://github.com/nagadomi/lbpcascade_animeface. All images are resized to 64 * 64 for the sake of convenience. Please also cite the two sources when using this dataset.

python头像 python真人头像转二次元_tensorflow

数据预处理:

def load_preprosess_image(path): # 定义每张图片的处理函数
    image = tf.io.read_file(path) # 读取路径文件
    image = tf.image.decode_png(image,channels=3) # 解码png图片,通道为3
    image = tf.cast(image,tf.float32) # 转换为float类型
    image = (image/127.5) - 1 # -1至1之间
    return image
image_ds = tf.data.Dataset.from_tensor_slices(image_path) # 创建Dataset对象
AUTOTUNE = tf.data.experimental.AUTOTUNE # 让TensorFlow自动选择合适的数值,充分利用计算机资源
image_ds = image_ds.map(load_preprosess_image,num_parallel_calls = AUTOTUNE) # 对于每张图片应用load_preprosess_image函数进行处理

设置Batch大小,定义图片总数

BATCH_SIZE = 64
image_count = len(image_path) # 21551
image_ds = image_ds.shuffle(image_count).batch(BATCH_SIZE) # 打乱图片顺序,设置batch大小
image_ds = image_ds.prefetch(AUTOTUNE) # 在GPU进行训练的同时 CPU进行数据预加载,提高训练效率

image_ds为<PrefetchDataset shapes: (None, None, None, 3), types: tf.float32>

其中每个batch的shape为(64, 64, 64, 3)

3 定义生成器

输入为100维的noise,输出维度为(64,64,3)

def generator_model():
    
    model = tf.keras.Sequential()
    model.add(layers.Dense(8*8*256, use_bias=False, input_shape=(100,)))
    model.add(layers.BatchNormalization())
    model.add(layers.LeakyReLU())
    
    model.add( layers.Reshape((8,8,256)) )  # 输出8*8*256
    # 反卷积
    model.add(layers.Conv2DTranspose(128,(5,5),strides=(1,1),padding='same',use_bias='false'))
    model.add(layers.BatchNormalization())
    model.add(layers.LeakyReLU())      # 输出8*8*128
    
    model.add(layers.Conv2DTranspose(64,(5,5),strides=(2,2),padding='same',use_bias='false'))
    model.add(layers.BatchNormalization())
    model.add(layers.LeakyReLU())      # 输出16*16*64
    
    model.add(layers.Conv2DTranspose(32,(5,5),strides=(2,2),padding='same',use_bias='false'))
    model.add(layers.BatchNormalization())
    model.add(layers.LeakyReLU())      # 输出32*32*32
    
    model.add(layers.Conv2DTranspose(3,(5,5),
                                     strides=(2,2),
                                     padding='same',
                                     use_bias=False,
                                     activation='tanh'))  # 输出64*64*3
    
    
    return model

4 定义判别器

输入维度(64,64,3),输出1维判定结果

def discriminator_model():
    
    # 第一层 不用BatchNormalization
    model = tf.keras.Sequential()
    model.add(layers.Conv2D(32,
                            (5,5),
                            strides = (2,2),
                            padding='same',
                            input_shape=(64,64,3))) # 输入为64*64*3
    model.add(layers.LeakyReLU())
    model.add(layers.Dropout(0.3)) # 32*32*32
   
    model.add(layers.Conv2D(64,
                            (5,5),
                            strides = (2,2),
                            padding='same')) 
    model.add(layers.BatchNormalization())
    model.add(layers.LeakyReLU())
    model.add(layers.Dropout(0.3)) # 16*16*64
    
    
    model.add(layers.Conv2D(128,
                            (5,5),
                            strides = (2,2),
                            padding='same')) 
    model.add(layers.BatchNormalization())
    model.add(layers.LeakyReLU())
    model.add(layers.Dropout(0.3)) # 8*8*128
    
    model.add(layers.Conv2D(256,
                            (5,5),
                            strides = (2,2),
                            padding='same')) 
    model.add(layers.BatchNormalization())
    model.add(layers.LeakyReLU())   # 4*4*256
    
    model.add(layers.GlobalAveragePooling2D())
    # Global Average Pooling(简称GAP,全局池化层),被认为是可以替代全连接层的一种新技术
    
    model.add(layers.Dense(1024))
    model.add(layers.BatchNormalization())
    model.add(layers.LeakyReLU())
    model.add(layers.Dense(1)) 
    
    return model

为了避免训练震荡和模型的不稳定,生成器的输出层和判别器的输入层不使用BatchNormalization()

5 定义损失函数和优化器

判别器损失:

  • 将判别器中对于真实图像的判别结果与1进行比较,求交叉熵损失
  • 将判别器中对于生成图像的判别结果与0进行比较,求交叉熵损失
  • 两者相加即为判别器损失

生成器损失:

  • 将判别器中对于生成图像结果与0进行比较,求交叉熵损失
cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True) # 因为判别器最后没有使用激活 所以我们添加from_logits=True
# 求判别器损失的函数
def discriminator_loss(real_out,fake_out):
    real_loss = cross_entropy(tf.ones_like(real_out),real_out) # 真实图片的输出与1比较
    fake_loss = cross_entropy(tf.zeros_like(fake_out),fake_out) # 生成图片的输出与0比较
    return real_loss + fake_loss
# 求生成器损失的函数
def generator_loss(fake_out):
    fake_loss = cross_entropy(tf.ones_like(fake_out),fake_out)
    return fake_loss

优化器:

# 优化器
generator_opt = keras.optimizers.Adam(1e-5)
discriminator_opt = keras.optimizers.Adam(1e-5)

定义损失评价指标,用于损失绘图

epoch_loss_avg_gen = tf.keras.metrics.Mean('g_loss')
epoch_loss_avg_disc = tf.keras.metrics.Mean('d_loss')
g_loss_results = [] 
d_loss_results = []

6 定义训练批次函数

EPOCHS = 500
noise_dim = 100 # 随机数的维度

num_example_to_generate = 6 # 用于绘图过程中生成图片的数量

seed = tf.random.normal([num_example_to_generate,noise_dim]) # 生成6个长度为100的随机向量

获取生成器模型和判别器模型

generator = generator_model() # 获取生成器模型
discriminator = discriminator_model() # 获取判别器模型

利用自动求导机制定义模型参数更新函数

# 接收一个批次的图片,对其进行训练
@tf.function
def train_step(images):
    noise = tf.random.normal([BATCH_SIZE,noise_dim]) # 生成BTATH_SIZE个长度为100的随机向量
    # images 是真实图片的输入
    # noise 是噪声输入
    with tf.GradientTape() as gen_tape,tf.GradientTape() as disc_tape:
        real_out = discriminator(images,training = True)
        gen_image = generator(noise,training = True)
        fake_out = discriminator(gen_image,training = True)
        # 通过损失函数计算损失
        gen_loss = generator_loss(fake_out)
        disc_loss = discriminator_loss(real_out,fake_out)

    # 自动计算机损失函数关于自变量(模型参数)的梯度    
    gradient_gen = gen_tape.gradient(gen_loss,generator.trainable_variables)
    gradient_disc = disc_tape.gradient(disc_loss,discriminator.trainable_variables)
    # 根据梯度更新参数
    generator_opt.apply_gradients(zip(gradient_gen,generator.trainable_variables))
 	discriminator_opt.apply_gradients(zip(gradient_disc,discriminator.trainable_variables))
    
    return gen_loss, disc_loss

7 定义可视化训练结果函数

使用固定的一个noise再训练的过程中来测试生成器生成的人物头像效果

# 画图函数
def generate_plot_image(gen_model,test_noise):
    pre_image = gen_model(test_noise,training = False)
    print(pre_image.shape)
    fig = plt.figure(figsize=(16,3)) # figsize:指定figure的宽和高,单位为英寸
    for i in range(pre_image.shape[0]):   # pre_image的shape的第一个维度就是个数,这里是6
        plt.subplot(1,6,i+1) # 几行几列的 第i+1个图片(从1开始)
        plt.imshow((pre_image[i,:,:,:] + 1)/2) # 加1除2: 将生成的-1~1的图片弄到0-1之间,
        plt.axis('off') # 不要坐标
    plt.show()

8 定义训练主函数

在每个epoch中循环取出一个batch大小真实图像进行生成器与判别器训练,同时记录每次的loss信息

def train(dataset,epochs):  
    for epoch in range(1,epochs+1): # 总共训练epochs次
        print("\n epoch:",epoch)
        for image_batch in dataset: # 从数据集中遍历所有batch
            gen_loss, disc_loss = train_step(image_batch)# 训练一个batch
            
            epoch_loss_avg_gen(gen_loss)
            epoch_loss_avg_disc(disc_loss)
            
            print(".",end="")
        g_loss_results.append(epoch_loss_avg_gen.result())
        d_loss_results.append(epoch_loss_avg_disc.result())
        
        epoch_loss_avg_gen.reset_states()
        epoch_loss_avg_disc.reset_states()
        
        if epoch%10 == 0: # 每10个epoch绘图一次
            generate_plot_image(generator,seed) # 绘图,用随机数seed在生成器
    generate_plot_image(generator,seed) # 绘图,用随机数seed在生成器

9 训练

train(image_ds,EPOCHS)
# 绘制loss变化图
plt.plot(range(1, len(g_loss_results)+1), g_loss_results, label='g_loss')
plt.plot(range(1, len(d_loss_results)+1), d_loss_results, label='d_loss')
plt.legend()

10 结果

epoch 10:

python头像 python真人头像转二次元_python头像_02

epoch 20:

python头像 python真人头像转二次元_tensorflow_03

epoch 30:

python头像 python真人头像转二次元_生成器_04

epoch 100:

python头像 python真人头像转二次元_深度学习_05

epoch 200:

python头像 python真人头像转二次元_生成器_06


epoch 500:

python头像 python真人头像转二次元_深度学习_07

11 使用生成器

seed2 = tf.random.normal([30,noise_dim]) # 生成30个长度为100的随机向量
a = seed2.numpy()
pre = generator.predict(a)
print(pre[0].shape) # 查看输出的第一个图片的shape为(64, 64, 3)
plt.imshow((pre[0] + 1)/2, cmap = 'gray')

python头像 python真人头像转二次元_深度学习_08


如上是600epoch效果,还是有些糊啦

代码下载(GitHub):DCGAN-anime-faces