基于DCGAN的动漫头像生成

基于DCGAN的动漫头像生成

数据

  • 数据集:动漫图库爬虫获得,经过数据清洗,裁剪得到动漫头像。分辨率为3 * 96 * 96,共5万多张动漫头像的图片,从知乎用户何之源处下载。
  • 生成器:输入为随机噪声,输出为归一化到[-1,1]之间的原图大小。
  • 判别器:输入图片,输出为图片为真实的概率,范围为[0,1]。

模型

DCGAN目前是GAN在实际工程实践中被采用最多的衍生网络,为了提高图像生成质量,增强其稳定性,许多研究学者尝试进行优化,并提出了四点设计原则:

(1)卷积层代替池化层

池化操作会使卷积核在缩小的特征图上覆盖了更大的图像视野,但是对网络性能的优化效果较小,使用卷积层代替池化层,让网络自动选择筛去不必要信息,学习上采样和下采样过程,提高计算机运算能力。

(2)去掉全连接层

全连接层一般添加在网络的末层,用于将图像特征进行连接,可以减少特征信息的损失,但是由于其参数过多,会产生过拟合、计算速度降低等问题。由于面部图像特征提取的感受野范围较小,不需要提取全图特征,所以为了避免上述问题,本项目中网络模型去掉了全连接层。

(3)批量归一化

本课题中的生成器和判别器都是五层神经网络,每一层输入的数据的复杂度都会逐层递增,使输出数据的分布发生变化,对网络参数的初始化和BP算法的性能产生影响。将数据进行批量归一化(Bach Normalization,BN),可以使输出的数据服从某个固定数据的分布,把数据特征转换为相同尺度,从而加速神经网络的收敛速度。

(4)激活函数

激活函数(Activation Function)具有连续可导的特性,可以使神经网络进行非线性变化,通过对数值优化来学习网络参数,提升网络的扩展性。本课题的生成器和判别器均为五层网络模型,计算量较大,每一层的激活函数选择需要满足高计算效率和训练稳定两点,其导函数的值域分布合理。

生成器

DCGAN生成器模型如下图,共五层,本项目中生成器输出通道数ngf默认为64,所以其中的通道数都减半,其他一样。从输入的100维的随机噪声,不断上采样反卷积操作,最终得到生成的假图片。

生成器模型

判别器

生成器整体框架逆过来,其中反卷积变为卷积,卷积核大小,步长等设置一样,除最后一层外ReLU激活函数变为LeakyReLU,不断下采样,最后通过sigmoid函数输出真实样本概率值,也就是一个二分类网络。

损失函数

BCELoss是CrossEntropyLoss的一个特例,用于计算输入 input 和标签 label 之间的二值交叉熵损失值。

由于生成网络和判别网络的输出层的激活函数分别为Than函数和Sigmoid函数,两者都是S型函数,其函数特性会导致反向传播算法收敛速度降低,使用BCELoss函数后,解决了因sigmoid函数导致的梯度消失问题。

criterion = t.nn.BCELoss().to(device)
# 训练判别器,分开训练
## 尽可能的把真图片判别为正确
error_d_real = criterion(output, true_labels)
error_d_real.backward()
## 尽可能把假图片判别为错误
error_d_fake = criterion(output, fake_labels)
error_d_fake.backward()
# 训练生成器
error_g = criterion(output, true_labels)

优化器

选用Adam优化程序调整超参数,它结合了 AdaGrad 和 RMSProp 算法最优的性能,不仅可以计算每个参数的自适应学习率,还可以通过训练数据的不断迭代使网络权重自动更新,相较于其他几种算法而言Adam算法实现简单、对计算机资源占用率较低,收敛速度也更快。

实验

从https://pan.baidu.com/s/1eSifHcA 提取码:g5qa下载数据(275M,约5万多张图片),把所有图片保存于data/face/目录下,这是因为用了默认的ImageFolder读取数据集,标签为faces,不需要重写datasets类。

data/
└── faces/
    ├── 0000fdee4208b8b7e12074c920bc6166-0.jpg
    ├── 0001a0fca4e9d2193afea712421693be-0.jpg
    ├── 0001d9ed32d932d298e1ff9cc5b7a2ab-0.jpg
    ├── 0001d9ed32d932d298e1ff9cc5b7a2ab-1.jpg
    ├── 00028d3882ec183e0f55ff29827527d3-0.jpg
    ├── 00028d3882ec183e0f55ff29827527d3-1.jpg
    ├── 000333906d04217408bb0d501f298448-0.jpg
    ├── 0005027ac1dcc32835a37be806f226cb-0.jpg

训练过程

(1)训练判别器

  • 先固定生成器
  • 对于真图片,判别器的输出概率值尽可能接近1
  • 对于生成器生成的假图片,判别器尽可能输出0

(2)训练生成器

  • 固定判别器
  • 生成器生成图片,尽可能使生成的图片让判别器输出为1

(3)返回第一步,循环交替进行

本次训练过程,每1个batch训练一次判别器, 每5个batch训练一次生成器,可以尝试改变训练比例,改变两者的学习率实验,观察哪种效果最好。

在训练判别器时,需要对生成器生成的图片用detach()操作进行计算图截断,避免反向传播将梯度传到生成器中。因为在训练判别器时,我们不需要训练生成器,也就不需要生成器的梯度。

在训练判别器时,需要反向传播两次,一次是希望把真图片判定为1,一次是希望把假图片判定为0.也可以将这两者的数据放到一个batch中,进行一次前向传播和反向传播即可。但是研究发现,分两次的方法更好。

对于假图片,在训练判别器时,希望判别器输出为0;而在训练生成器时,希望判别器输出为1,这样实现判别器和生成器互相对抗提升。

测试

python main.py generate --gpu --vis False --netd-path checkpoints/netd_199.pth --netg-path checkpoints/netg_199.pth --gen-img result.png --gen-num 64

使用最后一次迭代的到的训练网络进行验证,生成器网络为netd_199.pth,判别器网络为netg_199.pth,从生成的512张图中,根据判别器中输出的值,选择结果最好的64张图,并存储在本地,命名为result.png:
测试结果

结果分析

生成器和判别器损失函数变化如下,可以看到训练过程还是不稳定。

生成器损失变化
判别器损失变化

问题及改进:

  1. 样本数据有些比较模糊,检查图像样本库,在样本数量足够的情况下,检查样本中是否存在非动漫图像,动漫风格是否类似,样本的表情、发色等面部属性是否足够丰富。

  2. 模型训练不稳定,将训练次数比例和学习率结合,动态调整。判别器训练效果太好,会导致生成器反向调整参数,生成一些已经被识别为“真”的样本,特殊情况下,还输出许多面部特征畸变的图像,导致样本缺乏多样性和准确性。

完整代码

main.py用于训练和测试

# coding:utf8
import os
import ipdb
import torch as t
import torchvision as tv
import tqdm
from model import NetG, NetD
from torchnet.meter import AverageValueMeter


class Config(object):
    data_path = 'data/'  # 数据集存放路径
    num_workers = 4  # 多进程加载数据所用的进程数
    image_size = 96  # 图片尺寸
    batch_size = 256
    max_epoch = 200
    lr1 = 2e-4  # 生成器的学习率
    lr2 = 2e-4  # 判别器的学习率
    beta1 = 0.5  # Adam优化器的beta1参数
    gpu = True  # 是否使用GPU
    nz = 100  # 噪声维度
    ngf = 64  # 生成器feature map数
    ndf = 64  # 判别器feature map数

    save_path = 'imgs/'  # 生成图片保存路径

    vis = True  # 是否使用visdom可视化
    env = 'GAN'  # visdom的env
    plot_every = 20  # 每间隔20 batch,visdom画图一次

    debug_file = '/tmp/debuggan'  # 存在该文件则进入debug模式
    d_every = 1  # 每1个batch训练一次判别器
    g_every = 5  # 每5个batch训练一次生成器
    save_every = 10  # 每10个epoch保存一次模型
    netd_path = None  # 'checkpoints/netd_.pth' #预训练模型
    netg_path = None  # 'checkpoints/netg_211.pth'

    # 只测试不训练
    gen_img = 'result.png'
    # 从512张生成的图片中保存最好的64张
    gen_num = 64
    gen_search_num = 512
    gen_mean = 0  # 噪声的均值
    gen_std = 1  # 噪声的方差


opt = Config()


def train(**kwargs):
    for k_, v_ in kwargs.items():
        setattr(opt, k_, v_)

    device=t.device('cuda') if opt.gpu else t.device('cpu')
    if opt.vis:
        from visualize import Visualizer
        vis = Visualizer(opt.env)

    # 数据
    transforms = tv.transforms.Compose([
        tv.transforms.Resize(opt.image_size),
        tv.transforms
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值