DCGAN是一种深度学习模型,针对复杂分布的无监督学习有很好的发展前景。
这种模型实现的方法是通过两个模块(生成器和判别器)互相为难对方来提升模型的准确率。通俗来说就是老师和学生的关系,老师给学生出题,学生进行答题,一开始是简单的题目学生越做越熟练,准确率越来越高,老师发现难不住学生了,就开始提高教学的深度,给予更难做的题目,学生一开始被难住,但再通过逐步做题提升自己的能力,使自己在这个难度下又孰能生巧,老师和学生就在这种情况下相持,学生就是模型的准确度,在此期间不断提升。在这里面老师也担当了提高样本数的作用,好的模型一定是要经过大量的样本训练的。
下面开始下载数据集:
from download import download
url = "https://download.mindspore.cn/dataset/Faces/faces.zip"
path = download(url, "./faces", kind="zip")
对数据集进行清洗和增强:
batch_size = 128# 批量大小
image_size = 64# 训练图像空间大小
nc = 3# 图像彩色通道数
nz = 100# 隐向量的长度
ngf = 64# 特征图在生成器中的大小
ndf = 64# 特征图在判别器中的大小
num_epochs = 10# 训练周期数
lr = 0.0002# 学习率
beta1 = 0.5# Adam优化器的beta1超参数import numpy as np
import mindspore.dataset as ds
import mindspore.dataset.vision as vision
defcreate_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"))
]
# 数据映射操作
dataset = dataset.project('image')
dataset = dataset.map(transforms, 'image')
# 批量操作
dataset = dataset.batch(batch_size)
return dataset
dataset = create_dataset_imagenet('./faces')
下面抽样几个数据集中的图片原图看看:
![](https://i-blog.csdnimg.cn/blog_migrate/2391fa4dbd32f41805dfc7d5123fceab.png)
![](https://i-blog.csdnimg.cn/blog_migrate/e4429e9aac65076097862c506bc71b65.png)
import matplotlib.pyplot as plt
defplot_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)
图片展示如下:
![](https://i-blog.csdnimg.cn/blog_migrate/e8df174ee971d939d242c4309a9c6a14.png)
![](https://i-blog.csdnimg.cn/blog_migrate/0504c923d306a6994c69dc3e4f50907e.png)
数据集中的图片清晰度还是可以的。
下面开始模型的构建与训练,按照DCGAN论文中的描述,所有模型权重均应从mean为0,sigma为0.02的正态分布中随机初始化。生成器的功能应该是生成与原图像1:1大小的假图像来给判别器,这里使用conv2dTranspose卷积转置,再用batchnorm2d和relu激活层,最后再通过tanh返回到[-1,1]的图像数据。
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)
classGenerator(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()
)
defconstruct(self, x):return self.generator(x)
generator = Generator()
判别器是一个二分类网络模型,输出判定该图像为真实图的概率。通过一系列的Conv2d,BatchNorm2d和leakyRelu层对其进行处理,最后通过sigmoid激活函数得到最终概率。
classDiscriminator(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()
defconstruct(self, x):
out = self.discriminator(x)
out = out.reshape(out.shape[0], -1)
return self.adv_layer(out)
discriminator = Discriminator()
开始模型的训练准备:
选择二进制交叉熵损失函数:
# 定义损失函数
adversarial_loss = nn.BCELoss(reduction='mean')
为判别器和生成器都进行优化:
# 为生成器和判别器设置优化器
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.')
开始训练:
defgenerator_forward(real_imgs, valid):# 将噪声采样为发生器的输入
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
defdiscriminator_forward(real_imgs, gen_imgs, valid, fake):# 衡量鉴别器从生成的样本中对真实样本进行分类的能力
real_loss = adversarial_loss(discriminator(real_imgs), valid)
fake_loss = adversarial_loss(discriminator(gen_imgs), fake)
d_loss = (real_loss + fake_loss) / 2return 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.jitdeftrain_step(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)
g_loss = ops.depend(g_loss, optimizer_G(g_grads))
d_loss, d_grads = grad_discriminator_fn(imgs, gen_imgs, valid, fake)
d_loss = ops.depend(d_loss, optimizer_D(d_grads))
return g_loss, d_loss, gen_imgs
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 == 0or 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")
运行结果如下图:
![](https://i-blog.csdnimg.cn/blog_migrate/ae5060decde8cf41873cbd63a7ae2a83.png)
![](https://i-blog.csdnimg.cn/blog_migrate/980d220a26b412c89b77ce449f9cce03.png)
![](https://i-blog.csdnimg.cn/blog_migrate/86c6ace1f28c6b15ec178e6784367b54.png)
判别器的损失值一直都处于较小的值,生成器的值不是很稳定,感觉是学习率调大了?
下面运行一下判别器和生成器的损失与训练迭代图:
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()
![](https://i-blog.csdnimg.cn/blog_migrate/e781c96be7ba1915fae0257ae1017de0.png)
用代码看一下训练期间生成的假图像如何:
import matplotlib.pyplot as plt
import matplotlib.animation as animation
defshowGif(image_list):
show_list = []
fig = plt.figure(figsize=(8, 3), dpi=120)
for epoch in range(len(image_list)):
images = []
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)
ani.save('./dcgan.gif', writer='pillow', fps=1)
showGif(image_list)
确实模糊,不过大致的样子还是能认出来
最后加大力度,增加训练周期,看看(假)图的效果:
# 从文件中获取模型参数并加载到网络中
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()
![](https://i-blog.csdnimg.cn/blog_migrate/fd547e5ea85483e2dd9797b5501a0b3b.png)
![](https://i-blog.csdnimg.cn/blog_migrate/129e3949597acff9e4f9e0b7cf3cd310.png)
额,效果不如人意,可能是训练次数还不够多