240929-DCGAN生成漫画头像

news2024/11/17 15:49:42

240929-DCGAN生成漫画头像

DCGAN是GAN的直接扩展,简单从命名来理解,DCGAN(Deep Convolutional Generative Adversarial Networks)就是比GAN(Generative Adversarial Networks)多了DC(Deep Convolutional),也就是加入了深度卷积,把CNN和GAN相融合了起来,在生成器和判别器的网络结构中加入了CNN架构。

GAN前些天记录过,详见240925-GAN生成对抗网络-CSDN博客

DCGAN与GAN相对比,主要改进之处有以下几个方面:

image-20240928103007374

  1. 在融入CNN架构的基础上,生成器和判别器都舍弃了池化层,判别器保留了整体架构,生成器将卷积替换成了反卷积层。反卷积详见反卷积(Transposed conv deconv)实现原理(通俗易懂)-CSDN博客

  2. 在判别器和生成器中使用BN(batch normalization)层。

  3. 移除全连接层。使用1*1卷积层替换。

  4. 生成器输出层的激活函数使用Tanh,其余层使用ReLU激活函数。

  5. 鉴别器激活函数均使用LeakyReLU。

下面我们通过一个实战案例来介绍DCGAN

实战案例

案例中,我们使用的动漫头像数据集共有70,171张动漫头像图片,图片大小均为96*96。

首先自行下载数据集放在根目录,下载后的数据集目录结构如下:

image-20240928105824353

数据预处理

首先定义一些参数

batch_size = 128          # 批量大小
image_size = 64           # 训练图像空间大小
nc = 3                    # 图像彩色通道数
nz = 100                  # 隐向量的长度
ngf = 64                  # 特征图在生成器中的大小
ndf = 64                  # 特征图在判别器中的大小
num_epochs = 3           # 训练周期数
lr = 0.0002               # 学习率
beta1 = 0.5               # Adam优化器的beta1超参数

编写一个数据预处理方法对数据进行处理和增强

import numpy as np
import mindspore.dataset as ds
import mindspore.dataset.vision as vision

def create_dataset_imagenet(dataset_path):
    """数据加载"""
    dataset = ds.ImageFolderDataset(dataset_path,
                                    num_parallel_workers=4,
                                    shuffle=True,
                                    decode=True)

    # 数据增强操作
    transforms = [
        vision.Resize(image_size),
        vision.CenterCrop(image_size),
        vision.HWC2CHW(),
        # 图像数据归一化
        lambda x: ((x / 255).astype("float32"))
    ]

    # 数据映射操作
    # 仅保留名为 'image' 的这一列数据。其他任何列或字段都会被移除,只留下图像数据
    dataset = dataset.project('image')
    dataset = dataset.map(transforms, 'image')

    # 批量操作
    dataset = dataset.batch(batch_size)
    return dataset

dataset = create_dataset_imagenet('./faces')

数据载入后拿出来看看长什么样子,可视化操作

import matplotlib.pyplot as plt

def plot_data(data):
    # 可视化部分训练数据
    plt.figure(figsize=(10, 3), dpi=140)
    for i, image in enumerate(data[0][:30], 1):
        plt.subplot(3, 10, i)
        plt.axis("off")
        plt.imshow(image.transpose(1, 2, 0))
    plt.show()

sample_data = next(dataset.create_tuple_iterator(output_numpy=True))
plot_data(sample_data)

image-20240928110433869

构建网络

加载完数据就到了DCGAN和GAN主要差别的地方,网络构建了

生成器

下图是DCGAN的生成器网络架构图

image-20240928103924091

前面也说了,在DCGAN网络中,主要采用反卷积、Batch Normal层、以及ReLU激活函数来组成生成器(输出层使用Tahn)

结合网络架构图,我们可以简单的写出其网络结构代码,在该案例中未采用和上图完全一致的输入输出channel,而是采用二分之一的channel,经过查阅,pytorch官方代码也是采用二分之一channel。

下述代码中nz是隐向量z的长度,ngf与通过生成器传播的特征图的大小有关,nc是输出图像中的通道数,在之前都进行了初始化

image-20240928115404752

import mindspore as ms
from mindspore import nn, ops
from mindspore.common.initializer import Normal

weight_init = Normal(mean=0, sigma=0.02)
gamma_init = Normal(mean=1, sigma=0.02)

class Generator(nn.Cell):
    """DCGAN网络生成器"""

    def __init__(self):
        super(Generator, self).__init__()
        self.generator = nn.SequentialCell(
            nn.Conv2dTranspose(nz, ngf * 8, 4, 1, 'valid', weight_init=weight_init),
            nn.BatchNorm2d(ngf * 8, gamma_init=gamma_init),
            nn.ReLU(),
            nn.Conv2dTranspose(ngf * 8, ngf * 4, 4, 2, 'pad', 1, weight_init=weight_init),
            nn.BatchNorm2d(ngf * 4, gamma_init=gamma_init),
            nn.ReLU(),
            nn.Conv2dTranspose(ngf * 4, ngf * 2, 4, 2, 'pad', 1, weight_init=weight_init),
            nn.BatchNorm2d(ngf * 2, gamma_init=gamma_init),
            nn.ReLU(),
            nn.Conv2dTranspose(ngf * 2, ngf, 4, 2, 'pad', 1, weight_init=weight_init),
            nn.BatchNorm2d(ngf, gamma_init=gamma_init),
            nn.ReLU(),
            nn.Conv2dTranspose(ngf, nc, 4, 2, 'pad', 1, weight_init=weight_init),
            nn.Tanh()
            )

    def construct(self, x):
        return self.generator(x)

generator = Generator()

可以看到生成器我们主要采用反卷积构建,每个反卷积后跟一个BN层以及ReLU激活函数(输出层除外),一共有五个反卷积层,其

  • 输入通道数:依次为nz, ngf * 8, ngf * 4, ngf * 2, ngf
  • 输出通道数:依次为ngf * 8, ngf * 4, ngf * 2, ngf, nc
判别器

判别器是一个二分类网络模型,和生成器的网络结构正好相反,其输入输出维度也正好相反。

因为判别器的网络结构比较简单常规,我们可以理解为先有的判别器,然后我们把判别器反过来,卷积变成反卷积,ReLU激活变成LeakyReLU,就有了生成器。在维度上的变换可以类比U-Net网络的左右对称结构。

以下是判别器的代码实现:

class Discriminator(nn.Cell):
    """DCGAN网络判别器"""

    def __init__(self):
        super(Discriminator, self).__init__()
        self.discriminator = nn.SequentialCell(
            nn.Conv2d(nc, ndf, 4, 2, 'pad', 1, weight_init=weight_init),
            nn.LeakyReLU(0.2),
            nn.Conv2d(ndf, ndf * 2, 4, 2, 'pad', 1, weight_init=weight_init),
            nn.BatchNorm2d(ngf * 2, gamma_init=gamma_init),
            nn.LeakyReLU(0.2),
            nn.Conv2d(ndf * 2, ndf * 4, 4, 2, 'pad', 1, weight_init=weight_init),
            nn.BatchNorm2d(ngf * 4, gamma_init=gamma_init),
            nn.LeakyReLU(0.2),
            nn.Conv2d(ndf * 4, ndf * 8, 4, 2, 'pad', 1, weight_init=weight_init),
            nn.BatchNorm2d(ngf * 8, gamma_init=gamma_init),
            nn.LeakyReLU(0.2),
            nn.Conv2d(ndf * 8, 1, 4, 1, 'valid', weight_init=weight_init),
            )
        self.adv_layer = nn.Sigmoid()

    def construct(self, x):
        out = self.discriminator(x)
        out = out.reshape(out.shape[0], -1)
        return self.adv_layer(out)

discriminator = Discriminator()

损失函数

损失函数方面,还是使用和GAN相同的交叉熵损失函数,两个网络不同点主要就是在网络构建,后面就简单讲解。

# 定义损失函数
adversarial_loss = nn.BCELoss(reduction='mean')

优化器

与GAN一致,需要两个优化器,都是lr = 0.0002beta1 = 0.5的Adam优化器。

# 为生成器和判别器设置优化器
optimizer_D = nn.Adam(discriminator.trainable_params(), learning_rate=lr, beta1=beta1)
optimizer_G = nn.Adam(generator.trainable_params(), learning_rate=lr, beta1=beta1)
optimizer_G.update_parameters_name('optim_g.')
optimizer_D.update_parameters_name('optim_d.')

训练模型

训练主要分为两个部分,训练判别器和训练生成器。

  • 训练判别器

    训练判别器的目的是最大程度地提高判别图像真伪的概率。按照Goodfellow的方法,是希望通过提高其随机梯度来更新判别器,所以我们要最大化𝑙𝑜𝑔𝐷(𝑥)+𝑙𝑜𝑔(1−𝐷(𝐺(𝑧))logD(x)+log(1−D(G(z))的值。

  • 训练生成器

    如DCGAN论文所述,我们希望通过最小化𝑙𝑜𝑔(1−𝐷(𝐺(𝑧)))log(1−D(G(z)))来训练生成器,以产生更好的虚假图像。

在这两个部分中,分别获取训练过程中的损失,并在每个周期结束时进行统计,将fixed_noise批量推送到生成器中,以直观地跟踪G的训练进度。

def generator_forward(real_imgs, valid):
    """
    训练生成器。

    参数:
        real_imgs: 真实的图像样本。
        valid: 表示真实标签的张量。

    返回:
        g_loss: 生成器的损失。
        gen_imgs: 生成的图像样本。
    """
    # 将噪声采样为发生器的输入
    z = ops.standard_normal((real_imgs.shape[0], nz, 1, 1))

    # 生成一批图像
    gen_imgs = generator(z)

    # 损失衡量发生器绕过判别器的能力
    g_loss = adversarial_loss(discriminator(gen_imgs), valid)

    return g_loss, gen_imgs

def discriminator_forward(real_imgs, gen_imgs, valid, fake):
    """
    训练判别器。

    参数:
        real_imgs: 真实的图像样本。
        gen_imgs: 生成的图像样本。
        valid: 表示真实标签的张量。
        fake: 表示伪造标签的张量。

    返回:
        d_loss: 判别器的损失。
    """
    # 衡量鉴别器从生成的样本中对真实样本进行分类的能力
    real_loss = adversarial_loss(discriminator(real_imgs), valid)
    fake_loss = adversarial_loss(discriminator(gen_imgs), fake)
    d_loss = (real_loss + fake_loss) / 2
    return d_loss

# 创建计算生成器和判别器梯度的函数
grad_generator_fn = ms.value_and_grad(generator_forward, None,
                                      optimizer_G.parameters,
                                      has_aux=True)
grad_discriminator_fn = ms.value_and_grad(discriminator_forward, None,
                                          optimizer_D.parameters)

@ms.jit
def train_step(imgs):
    """
    执行一次训练步骤。

    参数:
        imgs: 输入的图像样本。

    返回:
        g_loss: 生成器的损失。
        d_loss: 判别器的损失。
        gen_imgs: 生成的图像样本。
    """
    # 准备真实和伪造标签
    valid = ops.ones((imgs.shape[0], 1), mindspore.float32)
    fake = ops.zeros((imgs.shape[0], 1), mindspore.float32)

    # 计算生成器损失和梯度,并更新生成器参数
    (g_loss, gen_imgs), g_grads = grad_generator_fn(imgs, valid)
    optimizer_G(g_grads)

    # 计算判别器损失和梯度,并更新判别器参数
    d_loss, d_grads = grad_discriminator_fn(imgs, gen_imgs, valid, fake)
    optimizer_D(d_grads)

    return g_loss, d_loss, gen_imgs

循环训练网络,每经过50次迭代,就收集生成器和判别器的损失,以便于后面绘制训练过程中损失函数的图像。

%%time
import mindspore

G_losses = []
D_losses = []
image_list = []

total = dataset.get_dataset_size()
for epoch in range(num_epochs):
    generator.set_train()
    discriminator.set_train()
    # 为每轮训练读入数据
    for i, (imgs, ) in enumerate(dataset.create_tuple_iterator()):
        g_loss, d_loss, gen_imgs = train_step(imgs)
        if i % 100 == 0 or i == total - 1:
            # 输出训练记录
            print('[%2d/%d][%3d/%d]   Loss_D:%7.4f  Loss_G:%7.4f' % (
                epoch + 1, num_epochs, i + 1, total, d_loss.asnumpy(), g_loss.asnumpy()))
        D_losses.append(d_loss.asnumpy())
        G_losses.append(g_loss.asnumpy())

    # 每个epoch结束后,使用生成器生成一组图片
    generator.set_train(False)
    fixed_noise = ops.standard_normal((batch_size, nz, 1, 1))
    img = generator(fixed_noise)
    image_list.append(img.transpose(0, 2, 3, 1).asnumpy())

    # 保存网络模型参数为ckpt文件
    mindspore.save_checkpoint(generator, "./generator.ckpt")
    mindspore.save_checkpoint(discriminator, "./discriminator.ckpt")

结果可视化

plt.figure(figsize=(10, 5))
plt.title("Generator and Discriminator Loss During Training")
plt.plot(G_losses, label="G", color='blue')
plt.plot(D_losses, label="D", color='orange')
plt.xlabel("iterations")
plt.ylabel("Loss")
plt.legend()
plt.show()

image-20240928130113807

可视化训练过程中通过隐向量fixed_noise生成的图像。

import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np  # 假设这个代码块之前已经有 import numpy as np

def showGif(image_list):
    """
    将图像列表生成并显示为GIF动画。

    参数:
    image_list: 图像列表,每个元素是一个图像数组列表,用于在不同的时间点显示。

    返回值:
    无返回值,但生成一个GIF动画文件。
    """
    # 初始化图像显示列表
    show_list = []
    # 创建绘图窗口,设置尺寸
    fig = plt.figure(figsize=(8, 3), dpi=120)
    # 遍历图像列表的每个时间点
    for epoch in range(len(image_list)):
        # 初始化当前时间点的图像块列表
        images = []
        # 每个时间点内,处理3行图像
        for i in range(3):
            # 按照一定步长拼接图像块为一行
            row = np.concatenate((image_list[epoch][i * 8:(i + 1) * 8]), axis=1)
            # 将拼接好的一行图像添加到列表
            images.append(row)
        # 将所有行图像垂直拼接成最终的图像,并确保图像值在有效范围内
        img = np.clip(np.concatenate((images[:]), axis=0), 0, 1)
        # 不显示坐标轴
        plt.axis("off")
        # 将图像添加到显示列表
        show_list.append([plt.imshow(img)])

    # 创建动画对象,设置间隔时间和重复延迟时间
    ani = animation.ArtistAnimation(fig, show_list, interval=1000, repeat_delay=1000, blit=True)
    # 使用pillow后端保存为GIF文件
    ani.save('./dcgan.gif', writer='pillow', fps=1)

# 调用函数,传入图像列表
showGif(image_list)

这里运行完我们可以看到一个不停跳动的git图,随着训练次数的增多,图像质量也越来越好。如果增大训练周期数,当num_epochs达到50以上时,生成的动漫头像图片与数据集中的较为相似,下面我们通过加载生成器网络模型参数文件来生成图像,代码如下:

# 从文件中获取模型参数并加载到网络中
mindspore.load_checkpoint("./generator.ckpt", generator)

fixed_noise = ops.standard_normal((batch_size, nz, 1, 1))
img64 = generator(fixed_noise).transpose(0, 2, 3, 1).asnumpy()

fig = plt.figure(figsize=(8, 3), dpi=120)
images = []
for i in range(3):
    images.append(np.concatenate((img64[i * 8:(i + 1) * 8]), axis=1))
img = np.clip(np.concatenate((images[:]), axis=0), 0, 1)
plt.axis("off")
plt.imshow(img)
plt.show()

原论文:1511.06434 (arxiv.org)

参考代码:DCGAN生成漫画头像.… - JupyterLab (mindspore.cn)

参考资料:

【GAN】三、DCGAN论文详解 - 知乎 (zhihu.com)

反卷积(Transposed conv deconv)实现原理(通俗易懂)-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2174177.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《黑神话:悟空》天命人速通法宝 | 北通鲲鹏20智控游戏手柄评测

《黑神话:悟空》天命人速通法宝 | 北通鲲鹏20智控游戏手柄评测 哈喽小伙伴们好,我是Stark-C~ 截止目前,《黑神话:悟空》已经面世一个多月,不知道还有多少天命人没有通关呢? 作为国内首款真正意义上的3A大作,《黑神话…

SSD在低地球轨道卫星应用中的挑战

随着太空技术的迅速发展,越来越多的卫星被发射到低地球轨道(Low-Earth-Orbit,缩写LEO,又称“近地轨道”),以支持通信、地球观测、技术开发等多种任务。然而,这些卫星在轨道运行期间面临着严峻的…

国庆头像制作小程序相关代码

↓↓ 点击下方搜索开始制作您的专属头像 ↓↓ 发现-》搜一搜-》最美易飞证件照制作 国庆头像自定义头像制作、微信头像直接获取制作小程序源码 index.wxml文件代码 // pages/userPhoto/userPhoto.js//获取应用实例const app getApp()import { Router} from ../../utils/ro…

Transformer 算法模型详解

核心点:完整讲解Transformer模型! 让我们用简单的语言来解释:想象一下,你正在阅读一本书,书中的每个字都很重要。但如果你每次只能关注一个字,理解整本书就会变得很慢。而Transformer模型就像是赋予你超能…

机器学习-SVM

线性感知机分类 支持向量机 线性感知机(Perceptron) 感知机是线性二值分类器。 注意:什么是线性?线性分割面就是,就是在分割面中,任意两个的连线也在分割面中,这个分割面,就是线…

【鸿蒙】HarmonyOS NEXT应用开发快速入门教程之布局篇(上)

系列文章目录 【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器(上) 【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器(下) 【鸿蒙】HarmonyOS NEXT应用开发快速入门教程之布局篇(上) 文…

在系统开发中提升 Excel 数据导出一致性与可维护性的统一规范与最佳实践

背景: 在系统开发过程中,数据导出为 Excel 格式是一个常见的需求。然而,由于各个开发人员的编码习惯和实现方式不同,导致导出代码风格不一。有的人使用第三方库,有的人则自定义实现。这种多样化不仅影响了代码的一致性…

Electron 安装以及搭建一个工程

安装Node.js 在使用Electron进行开发之前,需要安装 Node.js。 官方建议使用最新的LTS版本。 检查 Node.js 是否正确安装: # 查看node版本 node -v # 查看npm版本 npm -v注意 开发者需要在开发环境安装 Node.js 才能编写 Electron 项目,但是…

Vue之axios请求

Vue之axios请求 axios请求, 是Vue前端框架非常重要的一部分, 今天我们就讲解axios请求, 到底有什么作用, 以及会告诉大家axios的常见用法。 axios请求, 是网页向后端发起请求, 后端吧数据给我们网页, 这是一个前后端交互的过程。当我们学会了axios, 我们可以实现前端和后端练…

如何评估婚恋交友小程序的投资回报率

在这个数字化的时代,越来越多的人选择通过手机应用程序寻找自己的另一半。随着婚恋交友小程序的兴起,编辑h17711347205如何评估这类产品的投资回报率(ROI)成为了投资者和运营者关注的重点。本文将探讨如何有效地评估婚恋交友小程序…

生活中重大决定,除了你自己,谁也帮不了你!

随着年龄增长,越来越发现:生活是非常现实,更现实的社会,自己除了自己,谁也帮不了你。 因此,一个人的生活是好是坏,往往取决于我们自己的努力程度,越努力才会越幸运。没有伞的孩子&am…

程序设计题(65—72)

第六十五题 题目 请编写函数fun&#xff0c;它的功能是&#xff1a;计算下列级数和&#xff0c;和值由函数值返回。 例如&#xff0c;当n10&#xff0c;x0.3时&#xff0c;函数值为1.349859。 #include <conio.h> #include <stdio.h> #include <math.h> #…

万博智云CEO王嘉在华为全联接大会:以创新云应用场景,把握增长机遇

一、大会背景 2024年9月19-21日&#xff0c;第九届华为全联接大会将在上海世博展览馆和上海世博中心举办。作为华为的旗舰盛会&#xff0c;本次大会以“共赢行业智能化”为主题邀请了众多思想领袖、商业精英、技术专家、合作伙伴、开发者等业界同仁&#xff0c;从战略、产业、…

同比和环比怎么算?有什么区别一文讲清楚,附同比环比计算器

大家好&#xff0c;这里是效率办公指南&#xff01; &#x1f4ca; 在数据分析和财务报告中&#xff0c;同比和环比是两个常用的指标&#xff0c;它们帮助我们评估数据的时间序列变化。今天&#xff0c;我们将详细介绍同比和环比的定义、它们之间的区别以及如何计算这两个重要…

【漏洞复现】孚盟云oa AjaxSendDingdingMessage接口 存在sql注入漏洞

》》》产品描述《《《 孚盟与阿里强强联手将最受青睐的经典C系列产品打造成全新的孚盟云产品&#xff0c;让用户可以用云模式实现信息化管理&#xff0c;让用户的异地办公更加流畅&#xff0c;大大降低中小企业在信息化上成本&#xff0c;用最小的投入享受大型企业级别的信息化…

CSS 浏览器兼容问题探讨

目录 非 VIP 用户可前往公众号回复“css”进行免费阅读 浏览器介绍 css 选择器兼容介绍 ie6 微型盒子兼容解决方法 ie6双倍margin div中放入一个img元素导致div高度多出几像素 非 VIP 用户可前往公众号回复“css”进行免费阅读 浏览器介绍 在国内,常见的网页浏览…

Java:插入排序

目录 排序的概念 插入排序 直接插入排序 哈希排序 排序的概念 排序&#xff1a;所谓的排序&#xff0c;就是使一串记录&#xff0c;按照某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。 稳定性&#xff1a;假定在待排序的记录序列中&#xff0c;存在多个…

代码写得是真优雅呀!

工作多年后&#xff0c;越发觉得代码整洁真的是太重要了&#xff01;尤其是在团队开发中&#xff0c;写出优雅工整的代码&#xff0c;能让同事更乐于跟你合作。 下面&#xff0c;将通过命名、类、函数、测试这四个章节,使我们的代码变得整洁。 1.为什么要保持代码整洁? 不整…

大厂面试真题-说一下Mybatis的缓存

首先看一下原理图 Mybatis提供了两种缓存机制&#xff1a;一级缓存&#xff08;L1 Cache&#xff09;和二级缓存&#xff08;L2 Cache&#xff09;&#xff0c;旨在提高数据库查询的性能&#xff0c;减少数据库的访问次数。注意查询的顺序是先二级缓存&#xff0c;再一级缓存。…

牛肉高脂猫粮,福派斯猫粮新选择?乳鸽猫粮

福派斯鲜肉宠粮品牌近期对其旗舰产品——无麸质牛肉高脂猫粮单品进行了全面的配方和包装升级&#xff0c;这一举措不仅提升了产品的市场竞争力&#xff0c;更从多个维度确保了宠物食品的安全性与便捷性。 专业解析 "福派斯牛肉高脂猫粮&#xff0c;凭借其卓越的高肉配方&a…