昇思25天学习打卡营第13天|CycleGAN 图像风格迁移互换全流程解析

     

目录

数据集下载和加载

可视化

构建生成器

构建判别器

优化器和损失函数

前向计算

计算梯度和反向传播

模型训练

模型推理


数据集下载和加载


        使用 download 接口下载数据集,并将下载后的数据集自动解压到当前目录下。数据下载之前需要使用 pip install download 安装 download 包。使用 MindSpore 的 MindDataset 接口读取和解析数据集。

        代码如下:

%%capture captured_output  
# 实验环境已经预装了mindspore==2.2.14,如需更换mindspore版本,可更改下面mindspore的版本号  
!pip uninstall mindspore -y  
!pip install -i https://pypi.mirrors.ustc.edu.cn/simple mindspore==2.2.14  
# 查看当前 mindspore 版本  
!pip show mindspore  
from download import download  
url = "https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/models/application/CycleGAN_apple2orange.zip"  
download(url, ".", kind="zip", replace=True)  
from mindspore.dataset import MindDataset  
# 读取MindRecord格式数据  
name_mr = "./CycleGAN_apple2orange/apple2orange_train.mindrecord"  
data = MindDataset(dataset_files=name_mr)  
print("Datasize: ", data.get_dataset_size())  
batch_size = 1  
dataset = data.batch(batch_size)  
datasize = dataset.get_dataset_size()  

        分析:首先,对已安装的 MindSpore 库进行卸载尝试。紧接着,通过特定的镜像源安装指定版本(2.2.14)的 MindSpore 库。最后,查看当下所安装的 MindSpore 版本。

        从指定的 URL 下载一个 zip 压缩文件至当前目录,同时指定若存在同名文件则予以替换。

        从 MindSpore 的 dataset 模块导入 MindDataset 类,之后指定一个名为 name_mr 的路径用于读取 MindRecord 格式的数据,并将其存储于 data 变量内,最终打印出此数据集的规模大小。

        设置批量大小为 1,对原始数据集进行批量处理,将处理后的数据集存于 dataset 变量中,最后获取处理后的数据集的大小并存储在 datasize 变量里。

可视化


        通过 create_dict_iterator 函数将数据转换成字典迭代器,然后使用 matplotlib 模块可视化部分训练数据。从 dataset 中读取前 5 组图像数据,对其进行处理,并以特定的布局在一个图形中展示这些图像。

        代码如下:

import numpy as np  
import matplotlib.pyplot as plt  
mean = 0.5 * 255  
std = 0.5 * 255  
plt.figure(figsize=(12, 5), dpi=60)  
for i, data in enumerate(dataset.create_dict_iterator()):  if i < 5:  show_images_a = data["image_A"].asnumpy()  show_images_b = data["image_B"].asnumpy()  plt.subplot(2, 5, i+1)  show_images_a = (show_images_a[0] * std + mean).astype(np.uint8).transpose((1, 2, 0))  plt.imshow(show_images_a)  plt.axis("off")  plt.subplot(2, 5, i+6)  show_images_b = (show_images_b[0] * std + mean).astype(np.uint8).transpose((1, 2, 0))  plt.imshow(show_images_b)  plt.axis("off")  else:  break  
plt.show()  

        分析:首先,导入了 numpy 库并简称为 np ,同时引入了 matplotlib 库的 pyplot 模块以用于绘图。

        接着,定义了两个变量 mean 和 std ,它们将服务于后续的数据处理工作。

        之后,创建了一个新的图形,将其大小设定为 (12, 5) ,分辨率设为 60 。

        随后,运用 enumerate 函数对 dataset 的 create_dict_iterator 所生成的迭代器进行遍历。对于前面的 5 个数据项,如果索引 i 小于 5 ,就从数据中提取 image_A 和 image_B 并转换为 numpy 数组。而后对这些图像数据进行一系列处理,包含乘以 std 再加上 mean ,进行类型转换以及维度变换。使用 plt.subplot 在图形里创建子图,并于子图中展示处理后的图像,同时关闭坐标轴。一旦索引超过 4 ,就退出循环。

        最终,显示绘制好的图形。

        运行结果:

构建生成器


        首先,导入了 mindspore.nn 模块,并简称为 nn ;导入了 mindspore.ops 模块,并简称为 ops ;还从 mindspore.common.initializer 中引入了 Normal 以用于权重的初始化,并设定了一种权重初始化方式 weight_init 。

        定义了一个名为 ConvNormReLU 的类,该类继承自 nn.Cell 。此类别旨在构建涵盖卷积、归一化以及激活函数的层。在 __init__ 方法里,完成了各类参数的设定以及层的初始化操作,在 construct 方法中明确了前向传播的计算逻辑。

        定义了 ResidualBlock 类,用于构建残差块。它包含了两个 ConvNormReLU 层,并且能够选择是否运用 Dropout 。

        定义了 ResNetGenerator 类,这是一个基于残差网络的生成器。在 __init__ 方法中搭建了网络的各个层级,在 construct 方法中确定了前向传播的流程。

        再次强调,定义的 ResNetGenerator 类是一个基于残差网络的生成器,在 __init__ 方法中构建了网络的各层结构,在 construct 方法中定义了前向传播的流程。

        最后,实例化了两个 ResNetGenerator 对象,分别为 net_rg_a 和 net_rg_b ,并分别对它们的参数名称进行了更新。

        代码如下:

import mindspore.nn as nn  
import mindspore.ops as ops  
from mindspore.common.initializer import Normal  
weight_init = Normal(sigma=0.02)  
class ConvNormReLU(nn.Cell):  def __init__(self, input_channel, out_planes, kernel_size=4, stride=2, alpha=0.2, norm_mode='instance',  pad_mode='CONSTANT', use_relu=True, padding=None, transpose=False):  super(ConvNormReLU, self).__init__()  norm = nn.BatchNorm2d(out_planes)  if norm_mode == 'instance':  norm = nn.BatchNorm2d(out_planes, affine=False)  has_bias = (norm_mode == 'instance')  if padding is None:  padding = (kernel_size - 1) // 2  if pad_mode == 'CONSTANT':  if transpose:  conv = nn.Conv2dTranspose(input_channel, out_planes, kernel_size, stride, pad_mode='same',  has_bias=has_bias, weight_init=weight_init)  else:  conv = nn.Conv2d(input_channel, out_planes, kernel_size, stride, pad_mode='pad',  has_bias=has_bias, padding=padding, weight_init=weight_init)  layers = [conv, norm]  else:  paddings = ((0, 0), (0, 0), (padding, padding), (padding, padding))  pad = nn.Pad(paddings=paddings, mode=pad_mode)  if transpose:  conv = nn.Conv2dTranspose(input_channel, out_planes, kernel_size, stride, pad_mode='pad',  has_bias=has_bias, weight_init=weight_init)  else:  conv = nn.Conv2d(input_channel, out_planes, kernel_size, stride, pad_mode='pad',  has_bias=has_bias, weight_init=weight_init)  layers = [pad, conv, norm]  if use_relu:  relu = nn.ReLU()  if alpha > 0:  relu = nn.LeakyReLU(alpha)  layers.append(relu)  self.features = nn.SequentialCell(layers)  def construct(self, x):  output = self.features(x)  return output  
class ResidualBlock(nn.Cell):  def __init__(self, dim, norm_mode='instance', dropout=False, pad_mode="CONSTANT"):  super(ResidualBlock, self).__init__()  self.conv1 = ConvNormReLU(dim, dim, 3, 1, 0, norm_mode, pad_mode)  self.conv2 = ConvNormReLU(dim, dim, 3, 1, 0, norm_mode, pad_mode, use_relu=False)  self.dropout = dropout  if dropout:  self.dropout = nn.Dropout(p=0.5)  def construct(self, x):  out = self.conv1(x)  if self.dropout:  out = self.dropout(out)  out = self.conv2(out)  return x + out  
class ResNetGenerator(nn.Cell):  def __init__(self, input_channel=3, output_channel=64, n_layers=9, alpha=0.2, norm_mode='instance', dropout=False,  pad_mode="CONSTANT"):  super(ResNetGenerator, self).__init__()  self.conv_in = ConvNormReLU(input_channel, output_channel, 7, 1, alpha, norm_mode, pad_mode=pad_mode)  self.down_1 = ConvNormReLU(output_channel, output_channel * 2, 3, 2, alpha, norm_mode)  self.down_2 = ConvNormReLU(output_channel * 2, output_channel * 4, 3, 2, alpha, norm_mode)  layers = [ResidualBlock(output_channel * 4, norm_mode, dropout=dropout, pad_mode=pad_mode)] * n_layers  self.residuals = nn.SequentialCell(layers)  self.up_2 = ConvNormReLU(output_channel * 4, output_channel * 2, 3, 2, alpha, norm_mode, transpose=True)  self.up_1 = ConvNormReLU(output_channel * 2, output_channel, 3, 2, alpha, norm_mode, transpose=True)  if pad_mode == "CONSTANT":  self.conv_out = nn.Conv2d(output_channel, 3, kernel_size=7, stride=1, pad_mode='pad',  padding=3, weight_init=weight_init)  else:  pad = nn.Pad(paddings=((0, 0), (0, 0), (3, 3), (3, 3)), mode=pad_mode)  conv = nn.Conv2d(output_channel, 3, kernel_size=7, stride=1, pad_mode='pad', weight_init=weight_init)  self.conv_out = nn.SequentialCell([pad, conv])  def construct(self, x):  x = self.conv_in(x)  x = self.down_1(x)  x = self.down_2(x)  x = self.residuals(x)  x = self.up_2(x)  x = self.up_1(x)  output = self.conv_out(x)  return ops.tanh(output)  
# 实例化生成器  
net_rg_a = ResNetGenerator()  
net_rg_a.update_parameters_name('net_rg_a.')  
net_rg_b = ResNetGenerator()  
net_rg_b.update_parameters_name('net_rg_b.')  

构建判别器


        判别器其实是一个二分类网络模型,输出判定该图像为真实图的概率。网络模型使用的是 Patch 大小为 70x70 的 PatchGANs 模型。通过一系列的 Conv2d 、 BatchNorm2d 和 LeakyReLU 层对其进行处理,最后通过 Sigmoid 激活函数得到最终概率。

        代码如下:

# 定义判别器  
class Discriminator(nn.Cell):  def __init__(self, input_channel=3, output_channel=64, n_layers=3, alpha=0.2, norm_mode='instance'):  super(Discriminator, self).__init__()  kernel_size = 4  layers = [nn.Conv2d(input_channel, output_channel, kernel_size, 2, pad_mode='pad', padding=1, weight_init=weight_init),  nn.LeakyReLU(alpha)]  nf_mult = output_channel  for i in range(1, n_layers):  nf_mult_prev = nf_mult  nf_mult = min(2 ** i, 8) * output_channel  layers.append(ConvNormReLU(nf_mult_prev, nf_mult, kernel_size, 2, alpha, norm_mode, padding=1))  nf_mult_prev = nf_mult  nf_mult = min(2 ** n_layers, 8) * output_channel  layers.append(ConvNormReLU(nf_mult_prev, nf_mult, kernel_size, 1, alpha, norm_mode, padding=1))  layers.append(nn.Conv2d(nf_mult, 1, kernel_size, 1, pad_mode='pad', padding=1, weight_init=weight_init))  self.features = nn.SequentialCell(layers)  def construct(self, x):  output = self.features(x)  return output  # 判别器初始化  
net_d_a = Discriminator()  
net_d_a.update_parameters_name('net_d_a.')  net_d_b = Discriminator()  
net_d_b.update_parameters_name('net_d_b.') 

        分析:定义了一个名为 Discriminator 的类,它继承自 nn.Cell 。在 __init__ 方法中,首先设置了一些参数,如输入通道数、输出通道数、层数等。然后初始化了一些层,包括卷积层和激活函数层,并通过一个循环逐步构建更多的卷积和归一化激活层。最后将这些层组合成一个顺序模型 nn.SequentialCell 并存储在 self.features 中。

        construct 方法定义了前向传播的计算逻辑,即输入数据通过 self.features 进行处理并返回输出。

        这部分代码实例化了两个 Discriminator 对象 net_d_a 和 net_d_b ,并分别更新了它们的参数名称。

优化器和损失函数


        创建了四个优化器对象。optimizer_rg_a 和 optimizer_rg_b 用于优化生成器 net_rg_a 和 net_rg_b 的可训练参数,optimizer_d_a 和 optimizer_d_b 用于优化判别器 net_d_a 和 net_d_b 的可训练参数。这里使用的优化算法是 Adam 算法,学习率均为 0.0002,beta1 值均为 0.5 。

        定义了两个损失函数,loss_fn 是均方误差损失函数(MSELoss),采用均值约简方式;l1_loss 是平均绝对误差损失函数(L1Loss)。

        定义了一个名为 gan_loss 的函数,用于计算 GAN 网络的损失。首先将目标值 target 扩展为与预测值 predict 形状相同且元素全为 1 乘以 target 的张量,然后使用之前定义的 loss_fn 计算预测值和扩展后的目标值之间的损失,并返回该损失值。

        代码如下:

# 构建生成器,判别器优化器  
optimizer_rg_a = nn.Adam(net_rg_a.trainable_params(), learning_rate=0.0002, beta1=0.5)  
optimizer_rg_b = nn.Adam(net_rg_b.trainable_params(), learning_rate=0.0002, beta1=0.5)  optimizer_d_a = nn.Adam(net_d_a.trainable_params(), learning_rate=0.0002, beta1=0.5)  
optimizer_d_b = nn.Adam(net_d_b.trainable_params(), learning_rate=0.0002, beta1=0.5)  # GAN网络损失函数,这里最后一层不使用sigmoid函数  
loss_fn = nn.MSELoss(reduction='mean')  
l1_loss = nn.L1Loss("mean")  def gan_loss(predict, target):  target = ops.ones_like(predict) * target  loss = loss_fn(predict, target)  return loss  

前向计算


        为了减少模型振荡[1],这里遵循 Shrivastava 等人的策略[2],使用生成器生成图像的历史数据而不是生成器生成的最新图像数据来更新鉴别器。这里创建 image_pool 函数,保留了一个图像缓冲区,用于存储生成器生成前的50个图像。搭建模型前向计算损失的过程,过程如下代码。

        代码如下:

import mindspore as ms  # 前向计算  def generator(img_a, img_b):  fake_a = net_rg_b(img_b)  fake_b = net_rg_a(img_a)  rec_a = net_rg_b(fake_b)  rec_b = net_rg_a(fake_a)  identity_a = net_rg_b(img_a)  identity_b = net_rg_a(img_b)  return fake_a, fake_b, rec_a, rec_b, identity_a, identity_b  lambda_a = 10.0  
lambda_b = 10.0  
lambda_idt = 0.5  def generator_forward(img_a, img_b):  true = Tensor(True, dtype.bool_)  fake_a, fake_b, rec_a, rec_b, identity_a, identity_b = generator(img_a, img_b)  loss_g_a = gan_loss(net_d_b(fake_b), true)  loss_g_b = gan_loss(net_d_a(fake_a), true)  loss_c_a = l1_loss(rec_a, img_a) * lambda_a  loss_c_b = l1_loss(rec_b, img_b) * lambda_b  loss_idt_a = l1_loss(identity_a, img_a) * lambda_a * lambda_idt  loss_idt_b = l1_loss(identity_b, img_b) * lambda_b * lambda_idt  loss_g = loss_g_a + loss_g_b + loss_c_a + loss_c_b + loss_idt_a + loss_idt_b  return fake_a, fake_b, loss_g, loss_g_a, loss_g_b, loss_c_a, loss_c_b, loss_idt_a, loss_idt_b  def generator_forward_grad(img_a, img_b):  _, _, loss_g, _, _, _, _, _, _ = generator_forward(img_a, img_b)  return loss_g  def discriminator_forward(img_a, img_b, fake_a, fake_b):  false = Tensor(False, dtype.bool_)  true = Tensor(True, dtype.bool_)  d_fake_a = net_d_a(fake_a)  d_img_a = net_d_a(img_a)  d_fake_b = net_d_b(fake_b)  d_img_b = net_d_b(img_b)  loss_d_a = gan_loss(d_fake_a, false) + gan_loss(d_img_a, true)  loss_d_b = gan_loss(d_fake_b, false) + gan_loss(d_img_b, true)  loss_d = (loss_d_a + loss_d_b) * 0.5  return loss_d  def discriminator_forward_a(img_a, fake_a):  false = Tensor(False, dtype.bool_)  true = Tensor(True, dtype.bool_)  d_fake_a = net_d_a(fake_a)  d_img_a = net_d_a(img_a)  loss_d_a = gan_loss(d_fake_a, false) + gan_loss(d_img_a, true)  return loss_d_a  def discriminator_forward_b(img_b, fake_b):  false = Tensor(False, dtype.bool_)  true = Tensor(True, dtype.bool_)  d_fake_b = net_d_b(fake_b)  d_img_b = net_d_b(img_b)  loss_d_b = gan_loss(d_fake_b, false) + gan_loss(d_img_b, true)  return loss_d_b  # 保留了一个图像缓冲区,用来存储之前创建的50个图像  
pool_size = 50  
def image_pool(images):  num_imgs = 0  image1 = []  if isinstance(images, Tensor):  images = images.asnumpy()  return_images = []  for image in images:  if num_imgs < pool_size:  num_imgs = num_imgs + 1  image1.append(image)  return_images.append(image)  else:  if random.uniform(0, 1) > 0.5:  random_id = random.randint(0, pool_size - 1)  tmp = image1[random_id].copy()  image1[random_id] = image  return_images.append(tmp)  else:  return_images.append(image)  output = Tensor(return_images, ms.float32)  if output.ndim != 4:  raise ValueError("img should be 4d, but get shape {}".format(output.shape))  return output  

        分析:首先导入了 mindspore 库并简称为 ms 。

        定义了 generator 函数,用于执行生成器的前向计算。它接受两个图像输入 img_a 和 img_b ,通过生成器网络计算得到一系列的输出,包括生成的假图像、重建图像和恒等映射图像,并返回这些结果。

        定义了一些用于计算损失的权重系数。

        generator_forward 函数在 generator 函数的基础上计算生成器的各种损失,并将损失组合得到总的生成器损失 loss_g ,同时返回相关的输出和损失值。

        generator_forward_grad 函数获取 generator_forward 计算得到的生成器总损失 loss_g 并返回。

        discriminator_forward 函数计算判别器的损失。

        discriminator_forward 函数计算判别器的损失。

        discriminator_forward_b 函数计算与图像 img_b 和生成的假图像 fake_b 相关的判别器损失。

        image_pool 函数实现了一个图像缓冲区,用于存储一定数量的图像,并根据随机条件进行图像的替换和返回。

计算梯度和反向传播


        其中梯度计算也是分开不同的模型来进行的,详情见如下代码:

        代码如下:

from mindspore import value_and_grad  # 实例化求梯度的方法  
grad_g_a = value_and_grad(generator_forward_grad, None, net_rg_a.trainable_params())  
grad_g_b = value_and_grad(generator_forward_grad, None, net_rg_b.trainable_params())  grad_d_a = value_and_grad(discriminator_forward_a, None, net_d_a.trainable_params())  
grad_d_b = value_and_grad(discriminator_forward_b, None, net_d_b.trainable_params())  # 计算生成器的梯度,反向传播更新参数  
def train_step_g(img_a, img_b):  net_d_a.set_grad(False)  net_d_b.set_grad(False)  fake_a, fake_b, lg, lga, lgb, lca, lcb, lia, lib = generator_forward(img_a, img_b)  _, grads_g_a = grad_g_a(img_a, img_b)  _, grads_g_b = grad_g_b(img_a, img_b)  optimizer_rg_a(grads_g_a)  optimizer_rg_b(grads_g_b)  return fake_a, fake_b, lg, lga, lgb, lca, lcb, lia, lib  # 计算判别器的梯度,反向传播更新参数  
def train_step_d(img_a, img_b, fake_a, fake_b):  net_d_a.set_grad(True)  net_d_b.set_grad(True)  loss_d_a, grads_d_a = grad_d_a(img_a, fake_a)  loss_d_b, grads_d_b = grad_d_b(img_b, fake_b)  loss_d = (loss_d_a + loss_d_b) * 0.5  optimizer_d_a(grads_d_a)  optimizer_d_b(grads_d_b)  return loss_d  

        分析:从 mindspore 库中导入 value_and_grad 函数,用于计算函数的输出值和梯度。这里为生成器和判别器的相关函数创建了求梯度的实例。在 train_step_g 函数中,首先设置判别器 net_d_a 和 net_d_b 的梯度计算为 False ,即暂时不计算判别器的梯度。在 train_step_g 函数中,首先设置判别器 net_d_a 和 net_d_b 的梯度计算为 False ,即暂时不计算判别器的梯度。通过之前创建的梯度计算实例获取生成器的梯度,并使用相应的优化器 optimizer_rg_a 和 optimizer_rg_b 进行参数更新。在 train_step_d 函数中,设置判别器 net_d_a 和 net_d_b 的梯度计算为 True ,准备计算判别器的梯度。获取判别器的损失和梯度。计算总的判别器损失,并使用相应的优化器 optimizer_d_a 和 optimizer_d_b 进行参数更新。

        总的来说,这段代码定义了用于训练生成器和判别器的函数,通过计算梯度并使用优化器来更新网络的参数。

模型训练


        训练分为两个主要部分:训练判别器和训练生成器,在前文的判别器损失函数中,论文采用了最小二乘损失代替负对数似然目标。

        下面定义了生成器和判别器的训练过程:

        代码如下:

import os  
import time  
import random  
import numpy as np  
from PIL import Image  
from mindspore import Tensor, save_checkpoint  
from mindspore import dtype  # 由于时间原因,epochs设置为1,可根据需求进行调整  
epochs = 1  
save_step_num = 80  
save_checkpoint_epochs = 1  
save_ckpt_dir = './train_ckpt_outputs/'  print('Start training!')  for epoch in range(epochs):  g_loss = []  d_loss = []  start_time_e = time.time()  for step, data in enumerate(dataset.create_dict_iterator()):  start_time_s = time.time()  img_a = data["image_A"]  img_b = data["image_B"]  res_g = train_step_g(img_a, img_b)  fake_a = res_g[0]  fake_b = res_g[1]  res_d = train_step_d(img_a, img_b, image_pool(fake_a), image_pool(fake_b))  loss_d = float(res_d.asnumpy())  step_time = time.time() - start_time_s  res = []  for item in res_g[2:]:  res.append(float(item.asnumpy()))  g_loss.append(res[0])  d_loss.append(loss_d)  if step % save_step_num == 0:  print(f"Epoch:[{int(epoch + 1):>3d}/{int(epochs):>3d}], "  f"step:[{int(step):>4d}/{int(datasize):>4d}], "  f"time:{step_time:>3f}s,\n"  f"loss_g:{res[0]:.2f}, loss_d:{loss_d:.2f}, "  f"loss_g_a: {res[1]:.2f}, loss_g_b: {res[2]:.2f}, "  f"loss_c_a: {res[3]:.2f}, loss_c_b: {res[4]:.2f}, "  f"loss_idt_a: {res[5]:.2f}, loss_idt_b: {res[6]:.2f}")  epoch_cost = time.time() - start_time_e  per_step_time = epoch_cost / datasize  mean_loss_d, mean_loss_g = sum(d_loss) / datasize, sum(g_loss) / datasize  print(f"Epoch:[{int(epoch + 1):>3d}/{int(epochs):>3d}], "  f"epoch time:{epoch_cost:.2f}s, per step time:{per_step_time:.2f}, "  f"mean_g_loss:{mean_loss_g:.2f}, mean_d_loss:{mean_loss_d :.2f}")  if epoch % save_checkpoint_epochs == 0:  os.makedirs(save_ckpt_dir, exist_ok=True)  save_checkpoint(net_rg_a, os.path.join(save_ckpt_dir, f"g_a_{epoch}.ckpt"))  save_checkpoint(net_rg_b, os.path.join(save_ckpt_dir, f"g_b_{epoch}.ckpt"))  save_checkpoint(net_d_a, os.path.join(save_ckpt_dir, f"d_a_{epoch}.ckpt"))  save_checkpoint(net_d_b, os.path.join(save_ckpt_dir, f"d_b_{epoch}.ckpt"))  print('End of training!')  

        分析:首先导入了所需的库和模块。

        设置了训练的轮数 epochs 、保存检查点的步长 save_step_num 、保存检查点的轮数间隔 save_checkpoint_epochs 以及保存检查点的目录 save_ckpt_dir 。

        打印出开始训练的提示信息。

        开始进行训练轮数的循环。初始化生成器损失列表 g_loss 和判别器损失列表 d_loss ,并记录每一轮训练开始的时间。

        在每一轮中,通过数据集的迭代器获取数据,并执行生成器的训练步骤,获取训练结果和生成的假图像。

        执行判别器的训练步骤,获取判别器的损失,并计算每一步的训练时间。

        处理生成器的训练结果,将相关损失值添加到对应的损失列表中。

        如果当前步数是保存步长的整数倍,打印出当前的训练信息,包括轮数、步数、训练时间以及各种损失值。

        计算每一轮的训练总时间、每步平均时间,以及判别器和生成器损失的平均值。

        打印出每一轮训练结束后的总结信息,包括轮数、训练总时间、每步平均时间以及平均损失值。

        如果当前轮数是保存检查点轮数间隔的整数倍,创建保存目录,并保存生成器和判别器的模型检查点。

        最后打印训练结束的提示信息。

        运行结果:

Start training!  
Epoch:[  1/  1], step:[   0/1019], time:140.084908s,  
loss_g:20.59, loss_d:1.09, loss_g_a: 1.03, loss_g_b: 1.07, loss_c_a: 5.08, loss_c_b: 7.28, loss_idt_a: 2.55, loss_idt_b: 3.59  
Epoch:[  1/  1], step:[  80/1019], time:0.459862s,  
loss_g:9.79, loss_d:0.35, loss_g_a: 0.60, loss_g_b: 0.32, loss_c_a: 2.05, loss_c_b: 3.97, loss_idt_a: 1.03, loss_idt_b: 1.80  
Epoch:[  1/  1], step:[ 160/1019], time:0.450440s,  
loss_g:10.24, loss_d:0.38, loss_g_a: 0.54, loss_g_b: 0.65, loss_c_a: 3.35, loss_c_b: 2.68, loss_idt_a: 1.68, loss_idt_b: 1.33  
Epoch:[  1/  1], step:[ 240/1019], time:0.450548s,  
loss_g:10.80, loss_d:0.36, loss_g_a: 0.32, loss_g_b: 0.43, loss_c_a: 3.88, loss_c_b: 3.42, loss_idt_a: 1.33, loss_idt_b: 1.42  
Epoch:[  1/  1], step:[ 320/1019], time:0.501738s,  
loss_g:7.80, loss_d:0.30, loss_g_a: 0.30, loss_g_b: 0.41, loss_c_a: 1.57, loss_c_b: 3.54, loss_idt_a: 0.48, loss_idt_b: 1.50  
Epoch:[  1/  1], step:[ 400/1019], time:0.459365s,  
loss_g:6.60, loss_d:0.70, loss_g_a: 0.29, loss_g_b: 0.28, loss_c_a: 1.60, loss_c_b: 2.48, loss_idt_a: 0.88, loss_idt_b: 1.06  
Epoch:[  1/  1], step:[ 480/1019], time:0.448666s,  
loss_g:4.93, loss_d:0.62, loss_g_a: 0.16, loss_g_b: 0.59, loss_c_a: 1.27, loss_c_b: 1.73, loss_idt_a: 0.46, loss_idt_b: 0.72  
Epoch:[  1/  1], step:[ 560/1019], time:0.469116s,  
loss_g:5.46, loss_d:0.43, loss_g_a: 0.37, loss_g_b: 0.59, loss_c_a: 1.59, loss_c_b: 1.38, loss_idt_a: 0.94, loss_idt_b: 0.60  
Epoch:[  1/  1], step:[ 640/1019], time:0.453548s,  
loss_g:5.32, loss_d:0.34, loss_g_a: 0.44, loss_g_b: 0.40, loss_c_a: 1.52, loss_c_b: 1.57, loss_idt_a: 0.62, loss_idt_b: 0.77  
Epoch:[  1/  1], step:[ 720/1019], time:0.460250s,  
loss_g:5.53, loss_d:0.46, loss_g_a: 0.49, loss_g_b: 0.20, loss_c_a: 1.64, loss_c_b: 1.90, loss_idt_a: 0.64, loss_idt_b: 0.66  
Epoch:[  1/  1], step:[ 800/1019], time:0.460121s,  
loss_g:4.58, loss_d:0.29, loss_g_a: 0.37, loss_g_b: 0.69, loss_c_a: 1.34, loss_c_b: 1.16, loss_idt_a: 0.60, loss_idt_b: 0.42  
Epoch:[  1/  1], step:[ 880/1019], time:0.453644s,  
loss_g:6.31, loss_d:0.44, loss_g_a: 0.37, loss_g_b: 0.18, loss_c_a: 2.37, loss_c_b: 1.63, loss_idt_a: 1.09, loss_idt_b: 0.67  
Epoch:[  1/  1], step:[ 960/1019], time:0.449156s,  
loss_g:3.81, loss_d:0.26, loss_g_a: 0.42, loss_g_b: 0.35, loss_c_a: 0.73, loss_c_b: 1.38, loss_idt_a: 0.33, loss_idt_b: 0.60  
Epoch:[  1/  1], epoch time:609.15s, per step time:0.60, mean_g_loss:7.00, mean_d_loss:0.45  
End of training!  
CPU times: user 19min 48s, sys: 5min 15s, total: 25min 3s  
Wall time: 10min 10s  

模型推理


        下面我们通过加载生成器网络模型参数文件来对原图进行风格迁移,结果中第一行为原图,第二行为对应生成的结果图。

        代码如下:

%%time  
import os  
from PIL import Image  
import mindspore.dataset as ds  
import mindspore.dataset.vision as vision  
from mindspore import load_checkpoint, load_param_into_net  # 加载权重文件  
def load_ckpt(net, ckpt_dir):  param_GA = load_checkpoint(ckpt_dir)  load_param_into_net(net, param_GA)  g_a_ckpt = './CycleGAN_apple2orange/ckpt/g_a.ckpt'  
g_b_ckpt = './CycleGAN_apple2orange/ckpt/g_b.ckpt'  load_ckpt(net_rg_a, g_a_ckpt)  
load_ckpt(net_rg_b, g_b_ckpt)  # 图片推理  
fig = plt.figure(figsize=(11, 2.5), dpi=100)  
def eval_data(dir_path, net, a):  def read_img():  for dir in os.listdir(dir_path):  path = os.path.join(dir_path, dir)  img = Image.open(path).convert('RGB')  yield img, dir  dataset = ds.GeneratorDataset(read_img, column_names=["image", "image_name"])  trans = [vision.Resize((256, 256)), vision.Normalize(mean=[0.5 * 255] * 3, std=[0.5 * 255] * 3), vision.HWC2CHW()]  dataset = dataset.map(operations=trans, input_columns=["image"])  dataset = dataset.batch(1)  for i, data in enumerate(dataset.create_dict_iterator()):  img = data["image"]  fake = net(img)  fake = (fake[0] * 0.5 * 255 + 0.5 * 255).astype(np.uint8).transpose((1, 2, 0))  img = (img[0] * 0.5 * 255 + 0.5 * 255).astype(np.uint8).transpose((1, 2, 0))  fig.add_subplot(2, 8, i+1+a)  plt.axis("off")  plt.imshow(img.asnumpy())  fig.add_subplot(2, 8, i+9+a)  plt.axis("off")  plt.imshow(fake.asnumpy())  eval_data('./CycleGAN_apple2orange/predict/apple', net_rg_a, 0)  
eval_data('./CycleGAN_apple2orange/predict/orange', net_rg_b, 4)  
plt.show()

        分析:首先,设置了代码运行的计时环境,并导入了所需的库和模块。

        定义了一个用于加载模型检查点权重的函数 load_ckpt 。

        指定了要加载的生成器 net_rg_a 和 net_rg_b 的检查点文件路径。

        调用 load_ckpt 函数加载相应的权重到生成器模型中。

        创建了一个用于绘制推理结果的图形,并定义了一个用于进行图片推理的函数 eval_data 。

        在 eval_data 函数内部定义了一个用于读取指定目录下图片的生成器函数 read_img 。

        通过读取图片生成数据集,并对图片进行预处理操作(如调整大小、归一化和格式转换),然后将数据集进行批处理。

        对数据集进行迭代,通过生成器模型进行推理得到生成的假图片,并对原始图片和生成的假图片进行数据处理。

        将原始图片和生成的假图片添加到图形的子图中进行展示。

        分别对苹果和橙子的图片目录进行推理,并展示结果。

        总的来说,这段代码主要实现了加载生成器模型的权重,并对指定目录下的苹果和橙子图片进行推理,将原始图片和生成的图片进行展示。

        运行结果:

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

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

相关文章

el-table表格操作列错行处理

解决方法&#xff1a; <style>::v-deep .el-table th.el-table__cell > .cell {white-space: nowrap !important;} </style>

Template execution failed: ReferenceError: name is not defined

问题 我们使用了html-webpack-plugin&#xff08;webpack&#xff09;进行编译html&#xff0c;导致的错误。 排查结果 连接地址 html-webpack-plugin版本低(2.30.1)&#xff0c;html模板里面不能有符号&#xff0c;注释都不行 // var reg new RegExp((^|&)${name}([^&…

测试——基础篇

内容纲要: 软件测试的生命周期 如何描述一个bug 如何定义bug的级别 bug的生命周期 如何开始第一次测试 测试的执行和bug管理 产生争执怎么办 1. 软件测试的生命周期 需求分析→测试计划→ 测试设计、测试开发→ 测试执行→ 测试评估 需求分析:需求是否正确,是否可行测试计划:…

Qt基础 | Qt全局定义 | qglobal头文件中的数据类型、函数、宏定义

文章目录 一、数据类型定义二、函数三、宏定义 QtGlobal头文件包含了 Qt 类库的一些全局定义 &#xff0c;包括基本数据类型、函数和宏&#xff0c;一般的Qt类的头文件都会包含该文件。 详细内容可参考&#xff1a;https://doc.qt.io/qt-5/qtglobal.html 一、数据类型定义 为了…

《Python机器学习项目实战》书籍介绍

文章目录 书籍介绍主要内容书籍目录 书籍介绍 《Python机器学习项目实战》带领大家在构建实际项目的过程中&#xff0c;掌握关键的机器学习概念&#xff01;使用机器学习&#xff0c;我们可完成客户行为分析、价格趋势预测、风险评估等任务。要想掌握机器学习&#xff0c;需要…

2024年大数据高频面试题(中篇)

文章目录 Kafka为什么要用消息队列为什么选择了kafkakafka的组件与作用(架构)kafka为什么要分区Kafka生产者分区策略kafka的数据可靠性怎么保证ack应答机制(可问:造成数据重复和丢失的相关问题)副本数据同步策略ISRkafka的副本机制kafka的消费分区分配策略Range分区分配策略…

网络准入控制设备是什么?有哪些?网络准入设备臻品优选

小李&#xff1a;“小张&#xff0c;最近公司网络频繁遭遇外部攻击&#xff0c;我们得加强一下网络安全了。” 小张&#xff1a;“是啊&#xff0c;我听说实施网络准入控制是个不错的选择。但具体什么是网络准入控制设备&#xff1f;我们有哪些选择呢&#xff1f;” 小李微笑…

数据结构历年考研真题对应知识点(哈夫曼树和哈夫曼编码)

目录 5.5.1哈夫曼树和哈夫曼编码 1.哈夫曼树的定义 2.哈夫曼树的构造 【分析哈夫曼树的路径上权值序列的合法性&#xff08;2010&#xff09;】 【哈夫曼树的性质&#xff08;2010、2019&#xff09;】 3.哈夫曼编码 【根据哈夫曼编码对编码序列进行译码&#xff08;201…

乘积量化pq:将高维向量压缩 97%

向量相似性搜索在处理大规模数据集时&#xff0c;往往面临着内存消耗的挑战。例如&#xff0c;即使是一个包含100万个密集向量的小数据集&#xff0c;其索引也可能需要数GB的内存。随着数据集规模的增长&#xff0c;尤其是高维数据&#xff0c;内存使用量会迅速增加&#xff0c…

达梦 ./disql SYSDBA/SYSDBA报错[-70028]:创建SOCKET连接失败. 解决方法

原因 达梦命令./disql SYSDBA/SYSDBA默认访问端口5236&#xff0c;如果初始化实例的时候修改了端口&#xff0c;需要指定端口访问 解决 ./disql SYSDBA/SYSDBA192.168.10.123:5237

数据结构(5.2_1)——二叉树的基本定义和术语

二叉树的基本概念 二叉树是n(n>0)个结点的有限集合: 或者为空二叉树&#xff0c;即n0&#xff1b;或者由一个根结点和两个互不相交的被称为根的左子树和右子树组成。左子树和右子树又分别是一颗二叉树。 特点:每个结点至多只有两颗字数&#xff1b;左子树不能颠倒(二叉树…

09 深度推荐模型演化中的“平衡与不平衡“规律

你好&#xff0c;我是大壮。08 讲我们介绍了深度推荐算法中的范式方法&#xff0c;并简单讲解了组合范式推荐方法&#xff0c;其中还提到了多层感知器&#xff08;MLP&#xff09;。因此&#xff0c;这一讲我们就以 MLP 组件为基础&#xff0c;讲解深度学习范式的其他组合推荐方…

pico+unity3d手部动画

在 Unity 开发中&#xff0c;输入系统的选择和运用对于实现丰富的交互体验至关重要。本文将深入探讨 Unity 中的 Input System 和 XR Input Subsystem 这两种不同的输入系统&#xff0c;并详细介绍它们在控制手部动画方面的应用。 一、Input System 和 XR Input Subsystem 的区…

每日练习,不要放弃

目录 题目1.下面叙述错误的是 ( )2.java如何返回request范围内存在的对象&#xff1f;3.以下代码将打印出4.下列类定义中哪些是合法的抽象类的定义&#xff1f;&#xff08;&#xff09;5.以下代码段执行后的输出结果为6.以下代码运行输出的是总结 题目 选自牛客网 1.下面叙述…

【node-RED 4.0.2】连接操作 Oracle 数据库实现 增 删 改 查【新版,使用新插件:@hylink/node-red-oracle】

总览 上节课&#xff0c;我们说到&#xff0c;在 node-red 上链接 oracle 数据库 我们使用的插件是 node-red-contrib-agur-connector。 其实后来我发现&#xff0c;有一个插件更简便&#xff0c;并且也更好用&#xff1a;hylink/node-red-oracle &#xff01;&#xff01;&am…

Linux--网络基础

计算机网络背景 计算机网络背景是一个复杂而丰富的领域&#xff0c;涵盖了从计算机单机模式到网络互联的演变过程&#xff0c;以及网络技术的不断发展和创新。 计算机单机模式和独立发展 在早期&#xff0c;计算机主要以单机模式存在&#xff0c;即每台计算机都是独立的&…

传知代码-揭秘AI如何揪出图片中的“李鬼”(论文复现)

代码以及视频讲解 本文所涉及所有资源均在传知代码平台可获取 文字篡改图像的“照妖镜”&#xff1a;揭秘AI如何揪出图片中的“李鬼” 在数字化时代&#xff0c;我们时常被各种图像信息所包围。然而&#xff0c;这些图像中有时隐藏着不为人知的秘密——被篡改的文字或图像。这…

C++ | Leetcode C++题解之第238题除自身以外数组的乘积

题目&#xff1a; 题解&#xff1a; class Solution { public:vector<int> productExceptSelf(vector<int>& nums) {int length nums.size();// L 和 R 分别表示左右两侧的乘积列表vector<int> L(length, 0), R(length, 0);vector<int> answer(l…

188数码管轮询扫描

前言 最近用到了188数码管&#xff0c;总结一下。 188数码管&#xff0c;用5个IO&#xff0c;在不借助外部驱动芯片的情况下&#xff0c;可以点亮20个灯。188数码管广泛应用于电子烟、充电器、充电宝、DVD、高级音响、工业设备控制面板、医疗器械等多个领域&#xff0c;满足不…