今天是参加昇思学习打卡营的第22天,内容是GAN图像生成
以下是关键点概要:
模型简介
- 生成器(Generator):生成看起来像训练图像的“假”图像。
- 判别器(Discriminator):判断图像是真实的训练图像还是生成器生成的图像。
- GAN通过生成模型和判别模型的互相博弈学习产生高质量的输出。
数据集
- 使用MNIST手写数字数据集,包含70000张手写数字图片,分为60000张训练样本和10000张测试样本。
- 数据集下载和解压代码示例,以及数据加载和前处理的详细步骤。
数据集可视化
- 通过
create_dict_iterator
函数将数据转换成字典迭代器,并使用matplotlib
模块可视化部分训练数据。
隐码构造
- 利用随机种子创建一批隐码,用于跟踪生成器的学习进度。
模型构建
- 生成器:将隐码映射到数据空间,生成与真实图像大小相同的图像。使用五层
Dense
全连接层,每层与BatchNorm1d
批归一化层和ReLU
激活层配对,输出数据经过Tanh
函数。 - 判别器:二分类网络模型,输出判定图像为真实图的概率。通过一系列
Dense
层和LeakyReLU
层处理,最后通过Sigmoid
激活函数。
损失函数和优化器
- 使用二进制交叉熵损失函数
BCELoss
。 - 生成器和判别器都使用
Adam
优化器。
模型训练
- 训练分为训练判别器和训练生成器两个主要部分。
- 训练过程中记录损失,并在每轮迭代结束时生成图像以跟踪生成器的训练效果。
效果展示
- 描绘生成器和判别器损失与训练迭代的关系图。
- 将训练过程中生成的测试图转为动态图,展示图像质量随训练次数的提高。
模型推理
- 通过加载生成器网络模型参数文件来生成图像,并展示生成结果。
模型简介
生成式对抗网络(Generative Adversarial Networks,GAN)是一种生成式机器学习模型,是近年来复杂分布上无监督学习最具前景的方法之一。
最初,GAN由Ian J. Goodfellow于2014年发明,并在论文Generative Adversarial Nets中首次进行了描述,其主要由两个不同的模型共同组成——生成器(Generative Model)和判别器(Discriminative Model):
- 生成器的任务是生成看起来像训练图像的“假”图像;
- 判别器需要判断从生成器输出的图像是真实的训练图像还是虚假的图像。
GAN通过设计生成模型和判别模型这两个模块,使其互相博弈学习产生了相当好的输出。
GAN模型的核心在于提出了通过对抗过程来估计生成模型这一全新框架。在这个框架中,将会同时训练两个模型——捕捉数据分布的生成模型 𝐺𝐺 和估计样本是否来自训练数据的判别模型 𝐷𝐷 。
在训练过程中,生成器会不断尝试通过生成更好的假图像来骗过判别器,而判别器在这过程中也会逐步提升判别能力。这种博弈的平衡点是,当生成器生成的假图像和训练数据图像的分布完全一致时,判别器拥有50%的真假判断置信度。
用 𝑥𝑥 代表图像数据,用 𝐷(𝑥)𝐷(𝑥)表示判别器网络给出图像判定为真实图像的概率。在判别过程中,𝐷(𝑥)𝐷(𝑥) 需要处理作为二进制文件的大小为 1×28×281×28×28 的图像数据。当 𝑥𝑥 来自训练数据时,𝐷(𝑥)𝐷(𝑥) 数值应该趋近于 11 ;而当 𝑥𝑥 来自生成器时,𝐷(𝑥)𝐷(𝑥) 数值应该趋近于 00 。因此 𝐷(𝑥)𝐷(𝑥) 也可以被认为是传统的二分类器。
用 𝑧𝑧 代表标准正态分布中提取出的隐码(隐向量),用 𝐺(𝑧)𝐺(𝑧):表示将隐码(隐向量) 𝑧𝑧 映射到数据空间的生成器函数。函数 𝐺(𝑧)𝐺(𝑧) 的目标是将服从高斯分布的随机噪声 𝑧𝑧 通过生成网络变换为近似于真实分布 𝑝𝑑𝑎𝑡𝑎(𝑥)𝑝𝑑𝑎𝑡𝑎(𝑥) 的数据分布,我们希望找到 θθ 使得 𝑝𝐺(𝑥;𝜃)𝑝𝐺(𝑥;𝜃) 和 𝑝𝑑𝑎𝑡𝑎(𝑥)𝑝𝑑𝑎𝑡𝑎(𝑥) 尽可能的接近,其中 𝜃𝜃 代表网络参数。
𝐷(𝐺(𝑧))𝐷(𝐺(𝑧)) 表示生成器 𝐺𝐺 生成的假图像被判定为真实图像的概率,如Generative Adversarial Nets中所述,𝐷𝐷 和 𝐺𝐺 在进行一场博弈,𝐷𝐷 想要最大程度的正确分类真图像与假图像,也就是参数 log𝐷(𝑥)log𝐷(𝑥);而 𝐺𝐺 试图欺骗 𝐷𝐷 来最小化假图像被识别到的概率,也就是参数 log(1−𝐷(𝐺(𝑧)))log(1−𝐷(𝐺(𝑧)))。因此GAN的损失函数为:
min𝐺max𝐷𝑉(𝐷,𝐺)=𝐸𝑥∼𝑝𝑑𝑎𝑡𝑎(𝑥)[log𝐷(𝑥)]+𝐸𝑧∼𝑝𝑧(𝑧)[log(1−𝐷(𝐺(𝑧)))]min𝐺max𝐷𝑉(𝐷,𝐺)=𝐸𝑥∼𝑝𝑑𝑎𝑡𝑎(𝑥)[log𝐷(𝑥)]+𝐸𝑧∼𝑝𝑧(𝑧)[log(1−𝐷(𝐺(𝑧)))]
从理论上讲,此博弈游戏的平衡点是𝑝𝐺(𝑥;𝜃)=𝑝𝑑𝑎𝑡𝑎(𝑥)𝑝𝐺(𝑥;𝜃)=𝑝𝑑𝑎𝑡𝑎(𝑥),此时判别器会随机猜测输入是真图像还是假图像。下面我们简要说明生成器和判别器的博弈过程:
- 在训练刚开始的时候,生成器和判别器的质量都比较差,生成器会随机生成一个数据分布。
- 判别器通过求取梯度和损失函数对网络进行优化,将靠近真实数据分布的数据判定为1,将靠近生成器生成出来数据分布的数据判定为0。
- 生成器通过优化,生成出更加贴近真实数据分布的数据。
- 生成器所生成的数据和真实数据达到相同的分布,此时判别器的输出为1/2。
在上图中,蓝色虚线表示判别器,黑色虚线表示真实数据分布,绿色实线表示生成器生成的虚假数据分布,𝑧𝑧 表示隐码,𝑥𝑥 表示生成的虚假图像 𝐺(𝑧)𝐺(𝑧)。该图片来源于Generative Adversarial Nets。详细的训练方法介绍见原论文。
数据集
数据集简介
MNIST手写数字数据集是NIST数据集的子集,共有70000张手写数字图片,包含60000张训练样本和10000张测试样本,数字图片为二进制文件,图片大小为28*28,单通道。图片已经预先进行了尺寸归一化和中心化处理。
本案例将使用MNIST手写数字数据集来训练一个生成式对抗网络,使用该网络模拟生成手写数字图片。
数据集下载
使用download
接口下载数据集,并将下载后的数据集自动解压到当前目录下。数据下载之前需要使用pip install download
安装download
包。
下载解压后的数据集目录结构如下:
./MNIST_Data/
├─ train
│ ├─ train-images-idx3-ubyte
│ └─ train-labels-idx1-ubyte
└─ test
├─ t10k-images-idx3-ubyte
└─ t10k-labels-idx1-ubyte
数据下载的代码如下:
# 数据下载
from download import download
url = "https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/datasets/MNIST_Data.zip"
download(url, ".", kind="zip", replace=True)
数据加载
使用MindSpore自己的MnistDatase
接口,读取和解析MNIST数据集的源文件构建数据集。然后对数据进行一些前处理。
import numpy as np
import mindspore.dataset as ds
# 设置每个批次的样本数量
batch_size = 64
# 设置隐码的长度
latent_size = 100
# 加载MNIST数据集的训练集
train_dataset = ds.MnistDataset(dataset_dir='./MNIST_Data/train')
# 加载MNIST数据集的测试集
test_dataset = ds.MnistDataset(dataset_dir='./MNIST_Data/test')
def data_load(dataset):
"""
数据加载和预处理函数
:param dataset: 传入的数据集对象
:return: 处理后的数据集对象
"""
# 将数据集转换为生成器数据集,支持随机打乱和多进程处理
dataset1 = ds.GeneratorDataset(dataset, ["image", "label"], shuffle=True, python_multiprocessing=False, num_samples=10000)
# 数据增强:将图像转换为浮点数类型,并生成一个与图像数量相同的随机正态分布隐码
mnist_ds = dataset1.map(
operations=lambda x: (x.astype("float32"), np.random.normal(size=latent_size).astype("float32")),
output_columns=["image", "latent_code"])
# 选择输出的列
mnist_ds = mnist_ds.project(["image", "latent_code"])
# 将数据集分批,每个批次包含batch_size个样本,并进行打乱
mnist_ds = mnist_ds.batch(batch_size, True)
return mnist_ds
# 调用data_load函数加载训练数据集
mnist_ds = data_load(train_dataset)
# 获取数据集的迭代次数
iter_size = mnist_ds.get_dataset_size()
# 打印迭代次数
print('Iter size: %d' % iter_size)
数据集可视化
通过create_dict_iterator
函数将数据转换成字典迭代器,然后使用matplotlib
模块可视化部分训练数据。
import matplotlib.pyplot as plt # 导入matplotlib的pyplot模块,用于绘图
# 使用create_dict_iterator方法从数据集中创建一个字典迭代器,并将数据输出为numpy数组格式
data_iter = next(mnist_ds.create_dict_iterator(output_numpy=True))
# 创建一个图形对象,设置图形的大小为3x3英寸
figure = plt.figure(figsize=(3, 3))
# 设置网格的列数和行数,每行5张图,共5行
cols, rows = 5, 5
# 循环遍历每个图像,并在子图中显示
for idx in range(1, cols * rows + 1):
# 从字典迭代器中获取图像数据,索引idx从1开始
image = data_iter['image'][idx]
# 添加子图,rows行cols列的网格中的第idx个子图
figure.add_subplot(rows, cols, idx)
# 关闭子图的坐标轴
plt.axis("off")
# 显示图像,图像数据需要squeeze()去除单维度,cmap="gray"表示使用灰度颜色图
plt.imshow(image.squeeze(), cmap="gray")
# 显示所有子图组成的图形
plt.show()
隐码构造
为了跟踪生成器的学习进度,我们在训练的过程中的每轮迭代结束后,将一组固定的遵循高斯分布的隐码test_noise
输入到生成器中,通过固定隐码所生成的图像效果来评估生成器的好坏。
import random # 导入Python的random模块,用于生成伪随机数
import numpy as np # 导入NumPy库,用于高效的数值计算
from mindspore import Tensor # 从mindspore库导入Tensor,用于创建数据张量
from mindspore.common import dtype # 从mindspore.common导入dtype,用于指定数据类型
# 设置随机种子以确保结果的可复现性
np.random.seed(2323)
# 使用np.random.normal函数生成符合标准正态分布(均值为0,标准差为1)的随机数
# 大小为(25, 100),即生成25个样本,每个样本有100个特征
test_noise = Tensor(np.random.normal(size=(25, 100)), dtype=float32)
# 使用random.shuffle方法将生成的隐码随机打乱
# 这有助于在训练过程中更全面地评估生成器的性能
random.shuffle(test_noise)
模型构建
本案例实现中所搭建的 GAN 模型结构与原论文中提出的 GAN 结构大致相同,但由于所用数据集 MNIST 为单通道小尺寸图片,可识别参数少,便于训练,我们在判别器和生成器中采用全连接网络架构和 ReLU
激活函数即可达到令人满意的效果,且省略了原论文中用于减少参数的 Dropout
策略和可学习激活函数 Maxout
。
生成器
生成器 Generator
的功能是将隐码映射到数据空间。由于数据是图像,这一过程也会创建与真实图像大小相同的灰度图像(或 RGB 彩色图像)。在本案例演示中,该功能通过五层 Dense
全连接层来完成的,每层都与 BatchNorm1d
批归一化层和 ReLU
激活层配对,输出数据会经过 Tanh
函数,使其返回 [-1,1] 的数据范围内。注意实例化生成器之后需要修改参数的名称,不然静态图模式下会报错。
from mindspore import nn # 从mindspore库导入神经网络模块
import mindspore.ops as ops # 从mindspore库导入操作模块
# 设置训练图像的尺寸(长和宽)
img_size = 28
class Generator(nn.Cell):
def __init__(self, latent_size, auto_prefix=True):
super(Generator, self).__init__(auto_prefix=auto_prefix) # 调用基类的构造函数
# 初始化模型为一个顺序容器
self.model = nn.SequentialCell()
# 定义生成器的网络结构,逐步将输入数据上采样并变换到图像数据空间:
# 从隐码向量到图像数据的转换过程:
# 初始隐码维度 latent_size 通过全连接层变换到更大的维度空间。
# 第一层全连接层,将100维的隐码映射到128维
self.model.append(nn.Dense(latent_size, 128))
self.model.append(nn.ReLU()) # 使用ReLU激活函数
# 第二层全连接层,将128维映射到256维,并且使用批量归一化
self.model.append(nn.Dense(128, 256))
self.model.append(nn.BatchNorm1d(256))
self.model.append(nn.ReLU())
# 第三层全连接层,将256维映射到512维
self.model.append(nn.Dense(256, 512))
self.model.append(nn.BatchNorm1d(512))
self.model.append(nn.ReLU())
# 第四层全连接层,将512维映射到1024维
self.model.append(nn.Dense(512, 1024))
self.model.append(nn.BatchNorm1d(1024))
self.model.append(nn.ReLU())
# 最后一层全连接层,将1024维映射到图像的像素空间 (28x28)
self.model.append(nn.Dense(1024, img_size * img_size))
# 使用Tanh激活函数将数据范围变换到[-1, 1]
self.model.append(nn.Tanh())
def construct(self, x):
# 构造函数,用于前向传播
img = self.model(x) # 通过模型生成图像数据
# 使用reshape操作将图像数据变换为四维张量,以符合MindSpore的图像数据格式要求
return ops.reshape(img, (-1, 1, img_size, img_size))
# 实例化生成器,latent_size为隐码的维度
net_g = Generator(latent_size)
# 更新参数名称,以便在MindSpore中正确地保存和加载模型参数
net_g.update_parameters_name('generator')
判别器
如前所述,判别器 Discriminator
是一个二分类网络模型,输出判定该图像为真实图的概率。主要通过一系列的 Dense
层和 LeakyReLU
层对其进行处理,最后通过 Sigmoid
激活函数,使其返回 [0, 1] 的数据范围内,得到最终概率。注意实例化判别器之后需要修改参数的名称,不然静态图模式下会报错。
from mindspore import nn # 从mindspore库导入神经网络模块
import mindspore.ops as ops # 从mindspore库导入操作模块
# 假设训练图像的尺寸已经被定义
img_size = 28 # 例如,MNIST数据集中图像的尺寸是28x28
class Discriminator(nn.Cell):
def __init__(self, auto_prefix=True):
super().__init__(auto_prefix=auto_prefix) # 调用基类的构造函数
self.model = nn.SequentialCell() # 初始化模型为一个顺序容器
# 定义判别器的网络结构,逐步将输入数据下采样并变换到概率空间:
# 从图像数据到概率的转换过程:
# 第一层全连接层,将784维的图像数据映射到512维
self.model.append(nn.Dense(img_size * img_size, 512))
# 使用LeakyReLU激活函数,它允许负值通过一个小的斜率(默认为0.2)
self.model.append(nn.LeakyReLU())
# 第二层全连接层,将512维映射到256维
self.model.append(nn.Dense(512, 256))
self.model.append(nn.LeakyReLU())
# 第三层全连接层,将256维映射到1维
self.model.append(nn.Dense(256, 1))
# 使用Sigmoid激活函数将输出映射到[0, 1]区间,表示概率
self.model.append(nn.Sigmoid())
def construct(self, x):
# 构造函数,用于前向传播
# 将图像数据x从四维张量变换为二维张量,以符合全连接层的输入要求
x_flat = ops.reshape(x, (-1, img_size * img_size))
return self.model(x_flat) # 通过模型进行前向传播
# 实例化判别器
net_d = Discriminator()
# 更新参数名称,以便在MindSpore中正确地保存和加载模型参数
net_d.update_parameters_name('discriminator')
损失函数和优化器
定义了 Generator
和 Discriminator
后,损失函数使用MindSpore中二进制交叉熵损失函数BCELoss
;这里生成器和判别器都是使用Adam
优化器,但是需要构建两个不同名称的优化器,分别用于更新两个模型的参数,详情见下文代码。注意优化器的参数名称也需要修改。
# 设置学习率
lr = 0.0002
# 定义二元交叉熵损失函数,用于训练过程中的反向传播
# reduction='mean'表示将损失平均化,使得每个样本的权重相同
adversarial_loss = nn.BCELoss(reduction='mean')
# 创建判别器的优化器
# 使用Adam优化算法,这是一种基于梯度下降的优化算法,常用于训练深度学习模型
# net_d.trainable_params()获取判别器中所有的可训练参数
# learning_rate=lr设置优化器的学习率为上面定义的lr
# beta1和beta2是Adam算法的超参数,用于调整一阶矩估计和二阶矩估计
optimizer_d = nn.Adam(net_d.trainable_params(), learning_rate=lr, beta1=0.5, beta2=0.999)
# 创建生成器的优化器,参数设置同判别器的优化器
optimizer_g = nn.Adam(net_g.trainable_params(), learning_rate=lr, beta1=0.5, beta2=0.999)
# 更新生成器优化器参数的名称,以符合MindSpore的命名规范
# 这有助于在模型保存、加载或进行模型检查点管理时避免混淆
optimizer_g.update_parameters_name('optim_g')
# 更新判别器优化器参数的名称
optimizer_d.update_parameters_name('optim_d')
模型训练
训练分为两个主要部分。
第一部分是训练判别器。训练判别器的目的是最大程度地提高判别图像真伪的概率。按照原论文的方法,通过提高其随机梯度来更新判别器,最大化 𝑙𝑜𝑔𝐷(𝑥)+𝑙𝑜𝑔(1−𝐷(𝐺(𝑧))𝑙𝑜𝑔𝐷(𝑥)+𝑙𝑜𝑔(1−𝐷(𝐺(𝑧)) 的值。
第二部分是训练生成器。如论文所述,最小化 𝑙𝑜𝑔(1−𝐷(𝐺(𝑧)))𝑙𝑜𝑔(1−𝐷(𝐺(𝑧))) 来训练生成器,以产生更好的虚假图像。
在这两个部分中,分别获取训练过程中的损失,并在每轮迭代结束时进行测试,将隐码批量推送到生成器中,以直观地跟踪生成器 Generator
的训练效果。
import os # 导入os库,用于操作文件和目录
import time # 导入time库,用于时间相关的操作
import matplotlib.pyplot as plt # 导入matplotlib的pyplot模块,用于绘图和展示图像
import mindspore as ms # 导入mindspore库,用于MindSpore框架的功能
from mindspore import Tensor, save_checkpoint # 从mindspore库导入Tensor和save_checkpoint函数
# 设置训练的总周期数,即整个数据集将被遍历多少次
total_epoch = 12
# 设置训练时每个批次的样本数量
batch_size = 64
# 判断是否加载预训练模型的参数
# 如果pred_trained为True,则加载预训练的模型参数
pred_trained = False
# 如果加载预训练的生成器模型,需要提供生成器模型参数的路径
pred_trained_g = './result/checkpoints/Generator99.ckpt'
# 如果加载预训练的判别器模型,需要提供判别器模型参数的路径
pred_trained_d = './result/checkpoints/Discriminator99.ckpt'
# 设置模型训练过程中生成的检查点文件的保存路径
checkpoints_path = "./result/checkpoints"
# 设置训练过程中生成的图像的保存路径,用于可视化训练效果
image_path = "./result/images"
# 确保保存路径存在,如果不存在则创建它们
os.makedirs(checkpoints_path, exist_ok=True)
os.makedirs(image_path, exist_ok=True)
# 导入所需库
import os
import time
import matplotlib.pyplot as plt
import mindspore as ms
from mindspore import Tensor, save_checkpoint
# 定义生成器计算损失的函数
def generator_forward(test_noises):
fake_data = net_g(test_noises) # 生成器生成假数据
fake_out = net_d(fake_data) # 判别器对假数据进行判断
loss_g = adversarial_loss(fake_out, ops.ones_like(fake_out)) # 计算生成器的损失
return loss_g
# 定义判别器计算损失的函数
def discriminator_forward(real_data, test_noises):
fake_data = net_g(test_noises) # 生成器生成假数据给判别器
fake_out = net_d(fake_data) # 判别器对假数据进行判断
real_out = net_d(real_data) # 判别器对真实数据进行判断
real_loss = adversarial_loss(real_out, ops.ones_like(real_out)) # 计算真实数据的损失
fake_loss = adversarial_loss(fake_out, ops.zeros_like(fake_out)) # 计算假数据的损失
loss_d = real_loss + fake_loss # 判别器的总损失是真实损失和假损失之和
return loss_d
# 使用MindSpore的value_and_grad函数获取生成器和判别器的梯度
grad_g = ms.value_and_grad(generator_forward, None, net_g.trainable_params())
grad_d = ms.value_and_grad(discriminator_forward, None, net_d.trainable_params())
# 定义训练步骤的函数
def train_step(real_data, latent_code):
loss_d, grads_d = grad_d(real_data, latent_code) # 计算判别器的损失和梯度
optimizer_d(grads_d) # 使用优化器更新判别器的参数
loss_g, grads_g = grad_g(latent_code) # 计算生成器的损失和梯度
optimizer_g(grads_g) # 使用优化器更新生成器的参数
return loss_d, loss_g
# 定义保存生成图像的函数
def save_imgs(gen_imgs1, idx):
for i3 in range(gen_imgs1.shape[0]):
plt.subplot(5, 5, i3 + 1) # 创建子图
plt.imshow(gen_imgs1[i3, 0, :, :] / 2 + 0.5, cmap="gray") # 显示图像
plt.axis("off") # 关闭坐标轴
plt.savefig(os.path.join(image_path, f"test_{idx}.png")) # 保存图像
# 确保保存路径存在
os.makedirs(checkpoints_path, exist_ok=True)
os.makedirs(image_path, exist_ok=True)
# 设置网络为训练模式
net_g.set_train()
net_d.set_train()
# 初始化存储生成器和判别器损失的列表
losses_g, losses_d = [], []
# 训练循环
for epoch in range(total_epoch):
start_time = time.time() # 记录周期开始时间
for iter, data in enumerate(mnist_ds):
start_iter_time = time.time()
image, latent_code = data
# 归一化处理,将图像数据范围从[0, 255]转换到[-1, 1]
image = (image - 127.5) / 127.5
image = image.reshape(image.shape[0], 1, image.shape[1], image.shape[2])
d_loss, g_loss = train_step(image, latent_code) # 执行一个训练步骤
if (iter % 10 == 0):
# 每10个批次打印一次训练信息
print_training_info(epoch, iter, iter_size, d_loss, g_loss, start_iter_time)
# 记录损失
losses_d.append(d_loss.asnumpy())
losses_g.append(g_loss.asnumpy())
end_time = time.time() # 记录周期结束时间
print(f"Epoch {epoch + 1}/{total_epoch} finished in {end_time - start_time:.2f}s")
# 每个epoch结束后保存生成的图像
gen_imgs = net_g(test_noise)
save_imgs(gen_imgs.asnumpy(), epoch)
# 保存模型参数
if epoch % 1 == 0:
save_checkpoint(net_g, os.path.join(checkpoints_path, f"Generator{epoch}.ckpt"))
save_checkpoint(net_d, os.path.join(checkpoints_path, f"Discriminator{epoch}.ckpt"))
# 辅助函数,打印训练信息
def print_training_info(epoch, iter, iter_size, d_loss, g_loss, start_iter_time):
print(f"Epoch:[{epoch:>3d}/{total_epoch:>3d}], "
f"step:[{iter:>4d}/{iter_size:>4d}], "
f"loss_d:{d_loss:>4f}, "
f"loss_g:{g_loss:>4f}, "
f"time:{(end1 - start_iter_time):>3f}s, "
f"lr:{lr:>6f}")
效果展示
运行下面代码,描绘D
和G
损失与训练迭代的关系图:
import matplotlib.pyplot as plt
# 创建一个新的图形对象,并设置图形的大小为6x4英寸
plt.figure(figsize=(6, 4))
# 设置图形的标题
plt.title("Generator and Discriminator Loss During Training")
# 绘制生成器的损失曲线,标签为"G",线条颜色为蓝色
plt.plot(losses_g, label="G", color='blue')
# 绘制判别器的损失曲线,标签为"D",线条颜色为橙色
plt.plot(losses_d, label="D", color='orange')
# 设置x轴的显示范围,从-5到15
plt.xlim(-5, 15)
# 设置y轴的显示范围,从0到3.5
plt.ylim(0, 3.5)
# 设置x轴的标签为"iterations",表示迭代次数
plt.xlabel("iterations")
# 设置y轴的标签为"Loss",表示损失值
plt.ylabel("Loss")
# 显示图例,包含了两条曲线的标签"G"和"D"
plt.legend()
# 显示图形
plt.show()
可视化训练过程中通过隐向量生成的图像。
import cv2 # 导入OpenCV库,用于图像处理
import matplotlib.animation as animation # 导入matplotlib的animation模块,用于创建动画
# 初始化一个空列表,用于存储训练过程中生成的图像
image_list = []
# 循环遍历每个epoch生成的图像,并使用OpenCV的imread函数读取图像
for i in range(total_epoch):
# 读取图像,IMREAD_GRAYSCALE参数表示以灰度模式读取图像
image_list.append(cv2.imread(image_path + "/test_{}.png".format(i), cv2.IMREAD_GRAYSCALE))
# 初始化一个空列表,用于存储动画的每一帧
show_list = []
# 创建一个新的matplotlib图形对象,设置DPI为70
fig = plt.figure(dpi=70)
# 每隔5个epoch显示一张图像,以减少内存使用并使动画更加简洁
for epoch in range(0, len(image_list), 5):
plt.axis("off") # 关闭坐标轴
# 将当前epoch的图像添加到show_list中,作为动画的一帧
show_list.append([plt.imshow(image_list[epoch], cmap='gray')])
# 创建一个ArtistAnimation动画对象
# fig: 动画所在的图形对象
# show_list: 动画的每一帧
# interval: 每帧之间的时间间隔,单位为毫秒
# repeat_delay: 动画重复播放后等待的时间,单位为毫秒
# blit: 是否采用高效的方法更新图像
ani = animation.ArtistAnimation(fig, show_list, interval=1000, repeat_delay=1000, blit=True)
# 保存动画为GIF文件,使用Pillow库作为后端
ani.save('train_test.gif', writer='pillow', fps=1)
模型推理
下面我们通过加载生成器网络模型参数文件来生成图像,代码如下:
import mindspore as ms # 导入MindSpore库
import numpy as np # 导入NumPy库,用于数据处理
import matplotlib.pyplot as plt # 导入matplotlib的pyplot模块,用于绘图
# 假设net_g是已经定义并初始化的生成器网络
# 以下是加载预训练模型参数的代码,当前被注释掉了
# test_ckpt = './result/checkpoints/Generator199.ckpt' # 预训练模型的路径
# parameter = ms.load_checkpoint(test_ckpt) # 加载预训练模型的参数
# ms.load_param_into_net(net_g, parameter) # 将预训练参数加载到生成器网络中
# 准备生成图像的输入数据
# 生成25个样本,每个样本具有100维的随机正态分布隐码
test_data = ms.Tensor(np.random.normal(0, 1, (25, 100)).astype(np.float32))
# 使用生成器网络生成图像
# .transpose(0, 2, 3, 1)调整维度顺序以符合MindSpore的数据格式要求
# .asnumpy()将MindSpore Tensor转换为NumPy数组
images = net_g(test_data).transpose(0, 2, 3, 1).asnumpy()
# 创建图形展示生成的图像
fig = plt.figure(figsize=(3, 3), dpi=120) # 创建一个新的图形对象,并设置图形的大小和分辨率
# 循环遍历生成的图像,并在子图中展示
for i in range(25):
fig.add_subplot(5, 5, i + 1) # 添加子图,5行5列的网格中的第i+1个位置
plt.axis("off") # 关闭坐标轴
plt.imshow(images[i].squeeze(), cmap="gray") # 展示图像,使用灰度颜色图
# 显示图形
plt.show()
学习心得:
-
GAN的核心是通过生成器和判别器之间的对抗过程来生成高质量、逼真的图像。学习GAN让我理解了这种对抗性训练如何推动两个模型不断进步,生成器学习创建越来越真实的图像,而判别器则学习更好地区分真假。
-
GAN是一种无监督学习模型,它不需要成对的训练数据。这一点对于处理那些难以获取大量标注数据的问题非常有价值,例如艺术创作或模拟罕见事件。
-
通过构建和训练GAN,我更深入地理解了神经网络的架构,包括生成器和判别器的网络结构设计,以及它们如何通过层级结构转换数据。
-
学习GAN的过程中,我掌握了许多模型训练和优化的技巧,例如调整学习率、使用不同的优化器以及处理模式崩溃(mode collapse)问题。
-
GAN在多个领域都有广泛的应用,包括图像生成、风格迁移、数据增强等。了解这些应用扩展了我的视野,也为将来解决实际问题提供了新的思路。
-
通过实践编写GAN的代码,我提高了编程能力,尤其是在使用深度学习框架方面。这不仅增强了我的技术技能,也加深了我对模型工作机制的理解。
加油!!!