- 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
- 🍖 原作者:K同学啊
由于ACGAN的原理在上一篇文章中已经很详细的解释过了,这次我们直接上代码
一、代码解读
import argparse
import os
import numpy as np
import torchvision.transforms as transforms
from torchvision.utils import save_image
from torch.utils.data import DataLoader
from torchvision import datasets
from torch.autograd import Variable
import torch.nn as nn
import torch
import sys
sys.argv = ['run.py']
os.makedirs("images1", exist_ok=True)
parser = argparse.ArgumentParser()
parser.add_argument("--n_epochs", type=int, default=100, help="训练的总轮数")
parser.add_argument("--batch_size", type=int, default=128, help="每个批次的大小")
parser.add_argument("--lr", type=float, default=0.0002, help="Adam优化器的学习率")
parser.add_argument("--b1", type=float, default=0.5, help="Adam优化器的一阶动量衰减")
parser.add_argument("--b2", type=float, default=0.999, help="Adam优化器的二阶动量衰减")
parser.add_argument("--n_cpu", type=int, default=8, help="用于批次生成的CPU线程数")
parser.add_argument("--latent_dim", type=int, default=100, help="潜在空间的维度")
parser.add_argument("--n_classes", type=int, default=10, help="数据集的类别数")
parser.add_argument("--img_size", type=int, default=32, help="每个图像的尺寸")
parser.add_argument("--channels", type=int, default=1, help="图像通道数")
parser.add_argument("--sample_interval", type=int, default=200, help="图像采样间隔")
opt = parser.parse_args()
print(opt)
cuda = True if torch.cuda.is_available() else False
这段代码是一个Python脚本的示例,它使用了argparse
库来解析命令行参数,并设置了与图像生成相关的各种参数。代码的主要部分包括参数解析、检查GPU支持、以及创建一个目录来存储生成的图像。
- 导入必要的库和模块:
argparse
:用于解析命令行参数。os
:用于操作系统相关的功能,如创建目录。numpy
:用于数学计算。torchvision.transforms
:用于图像预处理。torchvision.utils
:用于保存图像。torch.utils.data
:用于数据加载。torchvision.datasets
:用于加载数据集。torch.autograd
:用于自动求导。torch.nn
:用于定义神经网络。torch
:用于深度学习。sys
:用于访问系统相关的参数和函数。
- 创建用于存储生成图像的目录:
os.makedirs("images1", exist_ok=True)
:如果目录"images1"不存在,则创建它。如果目录已经存在,则不会引发错误。
- 解析命令行参数:
- 使用
argparse.ArgumentParser
创建一个参数解析器。 - 添加多个参数,包括训练的总轮数、每个批次的大小、Adam优化器的学习率、一阶和二阶动量衰减、用于批次生成的CPU线程数、潜在空间的维度、数据集的类别数、每个图像的尺寸、图像通道数和图像采样间隔。
- 使用
parser.parse_args()
解析命令行参数,并将解析后的参数存储在opt
变量中。 - 打印解析后的参数。
- 使用
- 检查是否支持GPU加速:
cuda = True if torch.cuda.is_available() else False
:检查是否有可用的GPU,并设置cuda
变量为True
或False
。
这段代码为后续的深度学习模型训练和图像生成准备环境,包括设置参数和检查GPU支持。在实际应用中,这段代码可能需要根据具体任务和数据集进行调整。
def weights_init_normal(m):
classname = m.__class__.__name__
if classname.find("Conv") != -1:
torch.nn.init.normal_(m.weight.data, 0.0, 0.02)
elif classname.find("BatchNorm2d") != -1:
torch.nn.init.normal_(m.weight.data, 1.0, 0.02)
torch.nn.init.constant_(m.bias.data, 0.0)
# 生成器网络类
class Generator(nn.Module):
def __init__(self):
super(Generator, self).__init__()
# 为类别标签创建嵌入层
self.label_emb = nn.Embedding(opt.n_classes, opt.latent_dim)
# 计算上采样前的初始大小
self.init_size = opt.img_size // 4 # Initial size before upsampling
# 第一层线性层
self.l1 = nn.Sequential(nn.Linear(opt.latent_dim, 128 * self.init_size ** 2))
# 卷积层块
self.conv_blocks = nn.Sequential(
nn.BatchNorm2d(128),
nn.Upsample(scale_factor=2),
nn.Conv2d(128, 128, 3, stride=1, padding=1),
nn.BatchNorm2d(128, 0.8),
nn.LeakyReLU(0.2, inplace=True),
nn.Upsample(scale_factor=2),
nn.Conv2d(128, 64, 3, stride=1, padding=1),
nn.BatchNorm2d(64, 0.8),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(64, opt.channels, 3, stride=1, padding=1),
nn.Tanh(),
)
def forward(self, noise, labels):
# 将标签嵌入到噪声中
gen_input = torch.mul(self.label_emb(labels), noise)
# 通过第一层线性层
out = self.l1(gen_input)
# 重新整形为合适的形状
out = out.view(out.shape[0], 128, self.init_size, self.init_size)
# 通过卷积层块生成图像
img = self.conv_blocks(out)
return img
这段代码定义了一个生成器网络类Generator
,它是用于生成图像的深度学习模型。生成器网络是生成对抗网络(GAN)的一部分,它学习如何从随机噪声生成真实的图像。代码中定义了网络的结构和前向传播过程。
- 权重初始化函数:
weights_init_normal(m)
:这是一个自定义的权重初始化函数,用于在生成器网络中初始化各种层的权重。classname = m.__class__.__name__
:获取当前层的类名。if classname.find("Conv") != -1:
:如果层是卷积层,则进行相应的权重初始化。elif classname.find("BatchNorm2d") != -1:
:如果层是批量归一化层,则进行相应的权重和偏置初始化。torch.nn.init.normal_(m.weight.data, 0.0, 0.02)
:对权重进行正态分布初始化。torch.nn.init.constant_(m.bias.data, 0.0)
:对偏置进行常数初始化。
- 生成器网络类:
class Generator(nn.Module)
:定义了一个名为Generator
的类,它继承自nn.Module
,这是所有PyTorch神经网络类的基础。def __init__(self)
:这是生成器网络的构造函数。super(Generator, self).__init__()
:调用父类的构造函数,初始化网络的基本结构。self.label_emb = nn.Embedding(opt.n_classes, opt.latent_dim)
:创建一个嵌入层,用于将类别标签嵌入到潜在空间中。self.init_size = opt.img_size // 4
:计算上采样前的初始大小,通常为输入图像尺寸的四分之一。self.l1 = nn.Sequential(...)
:定义了一个包含线性层和重排层的序列,用于将潜在空间向量转换为适合后续卷积层的形状。self.conv_blocks = nn.Sequential(...)
:定义了一个包含多个卷积层、批量归一化层和Leaky ReLU激活函数的序列,用于生成最终的图像。
- 前向传播过程:
def forward(self, noise, labels)
:定义了生成器的前向传播函数。gen_input = torch.mul(self.label_emb(labels), noise)
:将标签嵌入到噪声中,生成用于后续处理的输入。out = self.l1(gen_input)
:通过第一层线性层。out = out.view(out.shape[0], 128, self.init_size, self.init_size)
:重新整形为合适的形状。img = self.conv_blocks(out)
:通过卷积层块生成图像。return img
:返回生成的图像。
这段代码定义了一个生成器网络,它可以接受噪声和类别标签作为输入,并生成相应的图像。在实际应用中,这个生成器网络可能需要根据具体任务和数据集进行调整。
class Discriminator(nn.Module):
def __init__(self):
super(Discriminator, self).__init__()
# 定义判别器块的函数
def discriminator_block(in_filters, out_filters, bn=True):
"""返回每个判别器块的层"""
block = [nn.Conv2d(in_filters, out_filters, 3, 2, 1), nn.LeakyReLU(0.2, inplace=True), nn.Dropout2d(0.25)]
if bn:
block.append(nn.BatchNorm2d(out_filters, 0.8))
return block
# 判别器的卷积层块
self.conv_blocks = nn.Sequential(
*discriminator_block(opt.channels, 16, bn=False),
*discriminator_block(16, 32),
*discriminator_block(32, 64),
*discriminator_block(64, 128),
)
# 下采样后图像的高度和宽度
ds_size = opt.img_size // 2 ** 4
# 输出层
self.adv_layer = nn.Sequential(nn.Linear(128 * ds_size ** 2, 1), nn.Sigmoid())
self.aux_layer = nn.Sequential(nn.Linear(128 * ds_size ** 2, opt.n_classes), nn.Softmax())
def forward(self, img):
out = self.conv_blocks(img)
out = out.view(out.shape[0], -1)
validity = self.adv_layer(out)
label = self.aux_layer(out)
return validity, label
# 损失函数
adversarial_loss = torch.nn.BCELoss()
auxiliary_loss = torch.nn.CrossEntropyLoss()
这段代码定义了一个判别器网络类Discriminator
,它是用于区分真实图像和生成图像的深度学习模型。判别器网络是生成对抗网络(GAN)的另一部分,它的目标是区分输入的图像是否是真实图像。代码中定义了网络的结构和前向传播过程。
- 权重初始化函数:
weights_init_normal(m)
:这是一个自定义的权重初始化函数,用于在判别器网络中初始化各种层的权重。classname = m.__class__.__name__
:获取当前层的类名。if classname.find("Conv") != -1:
:如果层是卷积层,则进行相应的权重初始化。elif classname.find("BatchNorm2d") != -1:
:如果层是批量归一化层,则进行相应的权重和偏置初始化。torch.nn.init.normal_(m.weight.data, 0.0, 0.02)
:对权重进行正态分布初始化。torch.nn.init.constant_(m.bias.data, 0.0)
:对偏置进行常数初始化。
- 判别器网络类:
class Discriminator(nn.Module)
:定义了一个名为Discriminator
的类,它继承自nn.Module
。def __init__(self)
:这是判别器网络的构造函数。super(Discriminator, self).__init__()
:调用父类的构造函数,初始化网络的基本结构。self.conv_blocks = nn.Sequential(...)
:定义了一个包含多个卷积层、Leaky ReLU激活函数和批量归一化层的序列,用于判别输入的图像。self.adv_layer = nn.Sequential(...)
:定义了一个包含线性层和Sigmoid激活函数的序列,用于输出判别结果。self.aux_layer = nn.Sequential(...)
:定义了一个包含线性层和Softmax激活函数的序列,用于输出类别标签。
- 前向传播过程:
def forward(self, img)
:定义了判别器的前向传播函数。out = self.conv_blocks(img)
:通过卷积层块处理输入的图像。out = out.view(out.shape[0], -1)
:重新整形为适合后续层的形状。validity = self.adv_layer(out)
:通过判别层输出判别结果。label = self.aux_layer(out)
:通过判别层输出类别标签。return validity, label
:返回判别结果和类别标签。
- 损失函数:
adversarial_loss = torch.nn.BCELoss()
:定义了一个二元交叉熵损失函数,用于判别器中的对抗损失。auxiliary_loss = torch.nn.CrossEntropyLoss()
:定义了一个交叉熵损失函数,用于判别器中的辅助损失。
这段代码定义了一个判别器网络,它可以接受图像作为输入,并输出判别结果和类别标签。在实际应用中,这个判别器网络可能需要根据具体任务和数据集进行调整。
generator = Generator()
discriminator = Discriminator()
if cuda:
generator.cuda()
discriminator.cuda()
adversarial_loss.cuda()
auxiliary_loss.cuda()
# 初始化权重
generator.apply(weights_init_normal)
discriminator.apply(weights_init_normal)
# 配置数据加载器
os.makedirs("data/mnist", exist_ok=True)
dataloader = torch.utils.data.DataLoader(
datasets.MNIST(
"data/mnist",
train=True,
download=True,
transform=transforms.Compose(
[transforms.Resize(opt.img_size), transforms.ToTensor(), transforms.Normalize([0.5], [0.5])]
),
),
batch_size=opt.batch_size,
shuffle=True,
)
# 优化器
optimizer_G = torch.optim.Adam(generator.parameters(), lr=opt.lr, betas=(opt.b1, opt.b2))
optimizer_D = torch.optim.Adam(discriminator.parameters(), lr=opt.lr, betas=(opt.b1, opt.b2))
FloatTensor = torch.cuda.FloatTensor if cuda else torch.FloatTensor
LongTensor = torch.cuda.LongTensor if cuda else torch.LongTensor
# 保存生成图像的函数
def sample_image(n_row, batches_done):
"""保存从0到n_classes的生成数字的图像网格"""
# 采样噪声
z = Variable(FloatTensor(np.random.normal(0, 1, (n_row ** 2, opt.latent_dim))))
# 为n行生成标签从0到n_classes
labels = np.array([num for _ in range(n_row) for num in range(n_row)])
labels = Variable(LongTensor(labels))
gen_imgs = generator(z, labels)
save_image(gen_imgs.data, "images1/%d.png" % batches_done, nrow=n_row, normalize=True)
这段代码是生成对抗网络(GAN)的训练过程的一部分,它包括生成器、判别器、数据加载器、优化器以及一个保存生成图像的函数。代码首先定义了生成器和判别器,然后设置GPU支持,初始化权重,配置数据加载器,创建优化器,并定义了一个保存生成图像的函数。
- 创建生成器和判别器:
generator = Generator()
:创建一个Generator
实例。discriminator = Discriminator()
:创建一个Discriminator
实例。
- 设置GPU支持:
if cuda:
:如果启用了GPU,则将生成器和判别器移动到GPU上,并相应地调整损失函数。generator.cuda()
:将生成器移动到GPU。discriminator.cuda()
:将判别器移动到GPU。adversarial_loss.cuda()
:将对抗损失函数移动到GPU。auxiliary_loss.cuda()
:将辅助损失函数移动到GPU。
- 初始化权重:
generator.apply(weights_init_normal)
:使用weights_init_normal
函数初始化生成器的权重。discriminator.apply(weights_init_normal)
:使用weights_init_normal
函数初始化判别器的权重。
- 配置数据加载器:
os.makedirs("data/mnist", exist_ok=True)
:创建一个名为"data/mnist"的目录,如果目录已存在,则不引发错误。dataloader = torch.utils.data.DataLoader(...)
:创建一个MNIST
数据集的DataLoader
实例,用于加载训练数据。
- 创建优化器:
optimizer_G = torch.optim.Adam(generator.parameters(), lr=opt.lr, betas=(opt.b1, opt.b2))
:为生成器创建一个Adam优化器实例。optimizer_D = torch.optim.Adam(discriminator.parameters(), lr=opt.lr, betas=(opt.b1, opt.b2))
:为判别器创建一个Adam优化器实例。
- 定义FloatTensor和LongTensor:
FloatTensor = torch.cuda.FloatTensor if cuda else torch.FloatTensor
:根据是否使用GPU,定义一个浮点数张量类型。LongTensor = torch.cuda.LongTensor if cuda else torch.LongTensor
:根据是否使用GPU,定义一个长整数张量类型。
- 保存生成图像的函数:
def sample_image(n_row, batches_done)
:定义了一个函数,用于保存生成的图像。z = Variable(FloatTensor(np.random.normal(0, 1, (n_row ** 2, opt.latent_dim))))
:生成一个随机噪声向量。labels = np.array([num for _ in range(n_row) for num in range(n_row)])
:为图像生成对应的标签。labels = Variable(LongTensor(labels))
:将标签转换为张量。gen_imgs = generator(z, labels)
:使用生成器生成图像。save_image(gen_imgs.data, "images1/%d.png" % batches_done, nrow=n_row, normalize=True)
:保存生成的图像。
这段代码是GAN训练过程中的一部分,它定义了生成器和判别器,并设置了GPU支持、初始化权重、配置数据加载器、创建优化器,并定义了一个保存生成图像的函数。在实际应用中,这段代码可能需要根据具体任务和数据集进行调整。
for epoch in range(opt.n_epochs):
for i, (imgs, labels) in enumerate(dataloader):
batch_size = imgs.shape[0]
# 真实数据的标签
valid = Variable(FloatTensor(batch_size, 1).fill_(1.0), requires_grad=False)
# 生成数据的标签
fake = Variable(FloatTensor(batch_size, 1).fill_(0.0), requires_grad=False)
# 配置输入
real_imgs = Variable(imgs.type(FloatTensor))
labels = Variable(labels.type(LongTensor))
# -----------------
# 训练生成器
# -----------------
optimizer_G.zero_grad()
# 采样噪声和标签作为生成器的输入
z = Variable(FloatTensor(np.random.normal(0, 1, (batch_size, opt.latent_dim))))
gen_labels = Variable(LongTensor(np.random.randint(0, opt.n_classes, batch_size)))
# 生成一批图像
gen_imgs = generator(z, gen_labels)
# 损失度量生成器的欺骗判别器的能力
validity, pred_label = discriminator(gen_imgs)
g_loss = 0.5 * (adversarial_loss(validity, valid) + auxiliary_loss(pred_label, gen_labels))
g_loss.backward()
optimizer_G.step()
# ---------------------
# 训练判别器
# ---------------------
optimizer_D.zero_grad()
# 真实图像的损失
real_pred, real_aux = discriminator(real_imgs)
d_real_loss = (adversarial_loss(real_pred, valid) + auxiliary_loss(real_aux, labels)) / 2
# 生成图像的损失
fake_pred, fake_aux = discriminator(gen_imgs.detach())
d_fake_loss = (adversarial_loss(fake_pred, fake) + auxiliary_loss(fake_aux, gen_labels)) / 2
# 判别器的总损失
d_loss = (d_real_loss + d_fake_loss) / 2
# 计算判别器的准确率
pred = np.concatenate([real_aux.data.cpu().numpy(), fake_aux.data.cpu().numpy()], axis=0)
gt = np.concatenate([labels.data.cpu().numpy(), gen_labels.data.cpu().numpy()], axis=0)
d_acc = np.mean(np.argmax(pred, axis=1) == gt)
d_loss.backward()
optimizer_D.step()
batches_done = epoch * len(dataloader) + i
if batches_done % opt.sample_interval == 0:
save_image(gen_imgs.data[:25], "images1/%d.png" % batches_done, nrow=5, normalize=True)
print(
"[Epoch %d/%d] [Batch %d/%d] [D loss: %f, acc: %d%%] [G loss: %f]"
% (epoch, opt.n_epochs, i, len(dataloader), d_loss.item(), 100 * d_acc, g_loss.item())
)
这段代码是生成对抗网络(GAN)的训练过程,其中包含了多个循环,用于迭代地训练生成器和判别器。代码首先定义了生成器和判别器的优化器,然后遍历数据集,在每个批次上更新网络的权重。
- 循环遍历训练轮数:
for epoch in range(opt.n_epochs):
:循环遍历训练的轮数,opt.n_epochs
是命令行参数中定义的总训练轮数。
- 循环遍历数据集的每个批次:
for i, (imgs, labels) in enumerate(dataloader):
:循环遍历数据加载器中的每个批次,imgs
是真实图像,labels
是图像对应的标签。
- 设置批次大小:
batch_size = imgs.shape[0]
:获取当前批次的图像数量,即批次大小。
- 定义真实数据和生成数据的标签:
valid = Variable(FloatTensor(batch_size, 1).fill_(1.0), requires_grad=False)
:真实数据的标签,值为1。fake = Variable(FloatTensor(batch_size, 1).fill_(0.0), requires_grad=False)
:生成数据的标签,值为0。
- 准备真实图像和标签:
real_imgs = Variable(imgs.type(FloatTensor))
:将真实图像转换为浮点数张量。labels = Variable(labels.type(LongTensor))
:将标签转换为长整数张量。
- 更新生成器:
optimizer_G.zero_grad()
:清除生成器优化器的梯度。z = Variable(FloatTensor(np.random.normal(0, 1, (batch_size, opt.latent_dim))))
:生成一个随机噪声向量。gen_labels = Variable(LongTensor(np.random.randint(0, opt.n_classes, batch_size)))
:为图像生成对应的标签。gen_imgs = generator(z, gen_labels)
:使用生成器生成图像。validity, pred_label = discriminator(gen_imgs)
:使用判别器评估生成图像的真实性。g_loss = 0.5 * (adversarial_loss(validity, valid) + auxiliary_loss(pred_label, gen_labels))
:计算生成器的损失。g_loss.backward()
:计算生成器损失的梯度。optimizer_G.step()
:使用梯度更新生成器的权重。
- 更新判别器:
optimizer_D.zero_grad()
:清除判别器优化器的梯度。real_pred, real_aux = discriminator(real_imgs)
:使用判别器评估真实图像的真实性。d_real_loss = (adversarial_loss(real_pred, valid) + auxiliary_loss(real_aux, labels)) / 2
:计算判别器对真实图像的损失。fake_pred, fake_aux = discriminator(gen_imgs.detach())
:使用判别器评估生成图像的真实性,但不对生成图像求梯度。d_fake_loss = (adversarial_loss(fake_pred, fake) + auxiliary_loss(fake_aux, gen_labels)) / 2
:计算判别器对生成图像的损失。d_loss = (d_real_loss + d_fake_loss) / 2
:计算判别器的总损失。d_acc = np.mean(np.argmax(pred, axis=1) == gt)
:计算判别器的分类准确率。d_loss.backward()
:计算判别器损失的梯度。optimizer_D.step()
:使用梯度更新判别器的权重。
- 保存生成图像:
if batches_done % opt.sample_interval == 0:
:检查当前批次完成的次数是否是opt.sample_interval
的倍数。如果满足条件,则执行以下代码。save_image(gen_imgs.data[:25], "images1/%d.png" % batches_done, nrow=5, normalize=True)
:gen_imgs.data[:25]
:从生成的图像中选择前25个图像用于保存。"images1/%d.png" % batches_done
:创建一个文件名,包含当前批次完成的次数。nrow=5
:将图像以5行每行的形式排列。normalize=True
:将图像的像素值从[-1, 1]范围转换为[0, 1]范围,以便于显示。
- 打印训练状态:
print(...)
:打印当前的训练状态。[Epoch %d/%d]
:当前训练轮数和总训练轮数。[Batch %d/%d]
:当前批次和总批次。[D loss: %f, acc: %d%%]
:判别器的损失和分类准确率。[G loss: %f]
:生成器的损失。%d
:整数格式化占位符。%f
:浮点数格式化占位符。epoch, opt.n_epochs, i, len(dataloader), d_loss.item(), 100 * d_acc, g_loss.item()
:实际要打印的值。
二、训练结果
Namespace(b1=0.5, b2=0.999, batch_size=128, channels=1, img_size=32, latent_dim=100, lr=0.0002, n_classes=10, n_cpu=8, n_epochs=100, sample_interval=200)
[Epoch 0/100] [Batch 468/469] [D loss: 1.186150, acc: 76%] [G loss: 1.278507]
[Epoch 1/100] [Batch 468/469] [D loss: 1.089216, acc: 91%] [G loss: 1.147808]
[Epoch 2/100] [Batch 468/469] [D loss: 1.077708, acc: 92%] [G loss: 1.162803]
[Epoch 3/100] [Batch 468/469] [D loss: 1.066667, acc: 94%] [G loss: 1.165272]
[Epoch 4/100] [Batch 468/469] [D loss: 1.071887, acc: 93%] [G loss: 1.119173]
[Epoch 5/100] [Batch 468/469] [D loss: 1.101875, acc: 93%] [G loss: 1.112627]
[Epoch 6/100] [Batch 468/469] [D loss: 1.085075, acc: 93%] [G loss: 1.184606]
[Epoch 7/100] [Batch 468/469] [D loss: 1.094645, acc: 96%] [G loss: 1.119984]
[Epoch 8/100] [Batch 468/469] [D loss: 1.086777, acc: 96%] [G loss: 1.113284]
[Epoch 9/100] [Batch 468/469] [D loss: 1.092487, acc: 96%] [G loss: 1.108529]
[Epoch 10/100] [Batch 468/469] [D loss: 1.115485, acc: 96%] [G loss: 1.087031]
[Epoch 11/100] [Batch 468/469] [D loss: 1.081023, acc: 96%] [G loss: 1.093937]
[Epoch 12/100] [Batch 468/469] [D loss: 1.095930, acc: 99%] [G loss: 1.099756]
[Epoch 13/100] [Batch 468/469] [D loss: 1.091788, acc: 97%] [G loss: 1.096789]
[Epoch 14/100] [Batch 468/469] [D loss: 1.099218, acc: 96%] [G loss: 1.109792]
[Epoch 15/100] [Batch 468/469] [D loss: 1.056762, acc: 98%] [G loss: 1.126807]
[Epoch 16/100] [Batch 468/469] [D loss: 1.088305, acc: 96%] [G loss: 1.103586]
[Epoch 17/100] [Batch 468/469] [D loss: 1.102751, acc: 97%] [G loss: 1.113332]
[Epoch 18/100] [Batch 468/469] [D loss: 1.082917, acc: 96%] [G loss: 1.093896]
[Epoch 19/100] [Batch 468/469] [D loss: 1.129379, acc: 98%] [G loss: 1.066369]
[Epoch 20/100] [Batch 468/469] [D loss: 1.088398, acc: 96%] [G loss: 1.124479]
[Epoch 21/100] [Batch 468/469] [D loss: 1.075817, acc: 97%] [G loss: 1.062265]
[Epoch 22/100] [Batch 468/469] [D loss: 1.092206, acc: 97%] [G loss: 1.080536]
[Epoch 23/100] [Batch 468/469] [D loss: 1.083714, acc: 99%] [G loss: 1.120035]
[Epoch 24/100] [Batch 468/469] [D loss: 1.065568, acc: 96%] [G loss: 1.092086]
[Epoch 25/100] [Batch 468/469] [D loss: 1.065326, acc: 98%] [G loss: 1.079625]
[Epoch 26/100] [Batch 468/469] [D loss: 1.083590, acc: 94%] [G loss: 1.066061]
[Epoch 27/100] [Batch 468/469] [D loss: 1.093708, acc: 98%] [G loss: 1.066264]
[Epoch 28/100] [Batch 468/469] [D loss: 1.098876, acc: 96%] [G loss: 1.046513]
[Epoch 29/100] [Batch 468/469] [D loss: 1.065514, acc: 98%] [G loss: 1.138119]
[Epoch 30/100] [Batch 468/469] [D loss: 1.082466, acc: 98%] [G loss: 1.091829]
[Epoch 31/100] [Batch 468/469] [D loss: 1.115253, acc: 98%] [G loss: 1.075734]
[Epoch 32/100] [Batch 468/469] [D loss: 1.083843, acc: 97%] [G loss: 1.094761]
[Epoch 33/100] [Batch 468/469] [D loss: 1.077276, acc: 98%] [G loss: 1.061793]
[Epoch 34/100] [Batch 468/469] [D loss: 1.051831, acc: 98%] [G loss: 1.061515]
[Epoch 35/100] [Batch 468/469] [D loss: 1.049085, acc: 97%] [G loss: 1.091401]
[Epoch 36/100] [Batch 468/469] [D loss: 1.107189, acc: 97%] [G loss: 1.104072]
[Epoch 37/100] [Batch 468/469] [D loss: 1.085251, acc: 98%] [G loss: 1.014494]
[Epoch 38/100] [Batch 468/469] [D loss: 1.055244, acc: 98%] [G loss: 1.128277]
[Epoch 39/100] [Batch 468/469] [D loss: 1.074688, acc: 97%] [G loss: 1.211257]
[Epoch 40/100] [Batch 468/469] [D loss: 1.020138, acc: 98%] [G loss: 1.099206]
[Epoch 41/100] [Batch 468/469] [D loss: 1.028032, acc: 98%] [G loss: 1.139912]
[Epoch 42/100] [Batch 468/469] [D loss: 1.094773, acc: 97%] [G loss: 1.123720]
[Epoch 43/100] [Batch 468/469] [D loss: 1.063132, acc: 98%] [G loss: 1.151915]
[Epoch 44/100] [Batch 468/469] [D loss: 1.044639, acc: 98%] [G loss: 1.127079]
[Epoch 45/100] [Batch 468/469] [D loss: 1.047207, acc: 97%] [G loss: 0.941165]
[Epoch 46/100] [Batch 468/469] [D loss: 1.153255, acc: 98%] [G loss: 1.064194]
[Epoch 47/100] [Batch 468/469] [D loss: 1.041365, acc: 97%] [G loss: 1.129444]
[Epoch 48/100] [Batch 468/469] [D loss: 1.117032, acc: 97%] [G loss: 1.110979]
[Epoch 49/100] [Batch 468/469] [D loss: 1.091151, acc: 97%] [G loss: 1.079596]
[Epoch 50/100] [Batch 468/469] [D loss: 1.045083, acc: 97%] [G loss: 1.115737]
[Epoch 51/100] [Batch 468/469] [D loss: 1.078201, acc: 98%] [G loss: 1.042152]
[Epoch 52/100] [Batch 468/469] [D loss: 0.993151, acc: 97%] [G loss: 1.135464]
[Epoch 53/100] [Batch 468/469] [D loss: 1.158597, acc: 97%] [G loss: 1.045841]
[Epoch 54/100] [Batch 468/469] [D loss: 1.077160, acc: 98%] [G loss: 1.180580]
[Epoch 55/100] [Batch 468/469] [D loss: 1.081528, acc: 98%] [G loss: 1.082192]
[Epoch 56/100] [Batch 468/469] [D loss: 1.051033, acc: 96%] [G loss: 1.181082]
[Epoch 57/100] [Batch 468/469] [D loss: 1.123516, acc: 94%] [G loss: 1.107539]
[Epoch 58/100] [Batch 468/469] [D loss: 1.066718, acc: 94%] [G loss: 1.013356]
[Epoch 59/100] [Batch 468/469] [D loss: 1.103453, acc: 98%] [G loss: 1.317525]
[Epoch 60/100] [Batch 468/469] [D loss: 1.007531, acc: 95%] [G loss: 0.980590]
[Epoch 61/100] [Batch 468/469] [D loss: 1.025996, acc: 96%] [G loss: 1.193597]
[Epoch 62/100] [Batch 468/469] [D loss: 1.042114, acc: 96%] [G loss: 1.175320]
[Epoch 63/100] [Batch 468/469] [D loss: 0.943264, acc: 98%] [G loss: 1.230064]
[Epoch 64/100] [Batch 468/469] [D loss: 1.079207, acc: 98%] [G loss: 1.161710]
[Epoch 65/100] [Batch 468/469] [D loss: 1.043449, acc: 98%] [G loss: 0.989506]
[Epoch 66/100] [Batch 468/469] [D loss: 1.066983, acc: 98%] [G loss: 1.354617]
[Epoch 67/100] [Batch 468/469] [D loss: 1.089749, acc: 98%] [G loss: 1.055511]
[Epoch 68/100] [Batch 468/469] [D loss: 1.203126, acc: 95%] [G loss: 1.052433]
[Epoch 69/100] [Batch 468/469] [D loss: 1.083899, acc: 98%] [G loss: 1.340578]
[Epoch 70/100] [Batch 468/469] [D loss: 1.103002, acc: 99%] [G loss: 1.214381]
[Epoch 71/100] [Batch 468/469] [D loss: 0.968297, acc: 99%] [G loss: 1.174415]
[Epoch 72/100] [Batch 468/469] [D loss: 1.024843, acc: 97%] [G loss: 1.083050]
[Epoch 73/100] [Batch 468/469] [D loss: 1.199783, acc: 98%] [G loss: 1.148810]
[Epoch 74/100] [Batch 468/469] [D loss: 1.085408, acc: 97%] [G loss: 1.163345]
[Epoch 75/100] [Batch 468/469] [D loss: 1.087018, acc: 97%] [G loss: 1.109405]
[Epoch 76/100] [Batch 468/469] [D loss: 1.010831, acc: 98%] [G loss: 1.325916]
[Epoch 77/100] [Batch 468/469] [D loss: 1.092120, acc: 96%] [G loss: 1.058283]
[Epoch 78/100] [Batch 468/469] [D loss: 1.082102, acc: 97%] [G loss: 1.159579]
[Epoch 79/100] [Batch 468/469] [D loss: 1.078881, acc: 96%] [G loss: 1.023906]
[Epoch 80/100] [Batch 468/469] [D loss: 1.134373, acc: 98%] [G loss: 1.261664]
[Epoch 81/100] [Batch 468/469] [D loss: 1.049532, acc: 98%] [G loss: 1.188655]
[Epoch 82/100] [Batch 468/469] [D loss: 1.060618, acc: 98%] [G loss: 1.166402]
[Epoch 83/100] [Batch 468/469] [D loss: 1.020341, acc: 96%] [G loss: 1.256302]
[Epoch 84/100] [Batch 468/469] [D loss: 1.116723, acc: 97%] [G loss: 1.056611]
[Epoch 85/100] [Batch 468/469] [D loss: 1.055123, acc: 97%] [G loss: 1.047174]
[Epoch 86/100] [Batch 468/469] [D loss: 1.036949, acc: 99%] [G loss: 1.130135]
[Epoch 87/100] [Batch 468/469] [D loss: 1.035721, acc: 97%] [G loss: 1.163391]
[Epoch 88/100] [Batch 468/469] [D loss: 1.082056, acc: 98%] [G loss: 1.516514]
[Epoch 89/100] [Batch 468/469] [D loss: 1.036912, acc: 98%] [G loss: 1.305870]
[Epoch 90/100] [Batch 468/469] [D loss: 1.161742, acc: 96%] [G loss: 1.124942]
[Epoch 91/100] [Batch 468/469] [D loss: 1.059311, acc: 95%] [G loss: 1.285748]
[Epoch 92/100] [Batch 468/469] [D loss: 1.060407, acc: 97%] [G loss: 1.189411]
[Epoch 93/100] [Batch 468/469] [D loss: 0.923837, acc: 97%] [G loss: 1.381471]
[Epoch 94/100] [Batch 468/469] [D loss: 0.947491, acc: 98%] [G loss: 1.243434]
[Epoch 95/100] [Batch 468/469] [D loss: 1.011180, acc: 97%] [G loss: 1.212379]
[Epoch 96/100] [Batch 468/469] [D loss: 1.023346, acc: 98%] [G loss: 1.094416]
[Epoch 97/100] [Batch 468/469] [D loss: 1.115852, acc: 96%] [G loss: 1.148246]
[Epoch 98/100] [Batch 468/469] [D loss: 1.025092, acc: 97%] [G loss: 1.255130]
[Epoch 99/100] [Batch 468/469] [D loss: 1.059351, acc: 97%] [G loss: 1.173633]
图片输出