昇思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://www.mfbz.cn/a/802336.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LabVIEW设备检修信息管理系统

开发了基于LabVIEW设计平台开发的设备检修信息管理系统。该系统应用于各种设备的检修基地&#xff0c;通过与基地管理信息系统的连接和数据交换&#xff0c;实现了本地检修工位数据的远程自动化管理&#xff0c;提高了设备的检修效率和安全性。 项目背景 现代设备运维过程中信…

QT小细节

QT小细节 1 QTextToSpeech1.1 cmake1.2 qmake QT6 6.7.2 1 QTextToSpeech 从下图可以看到&#xff0c;分别使用qmake或者cmake编译情况下的&#xff0c;QTextToSpeech的使用方法 QTextToSpeech官方链接&#xff0c;也可以直接在QT Creator的帮助中搜索 1.1 cmake 将上图中的…

jmeter之变量随机参数化以及解决多线程不会随机变化

参考链接&#xff1a; https://www.cnblogs.com/Testing1105/p/12743475.html jmeter 使用random函数多线程运行时数据不会随机变化&#xff1f;_jmeter 线程组循环执行时 变量不变-CSDN博客 1、如下图所示&#xff0c;需要对请求参数 autor 和phone进行随机参数化 2、目前有…

FullCalendar日历组件集成实战(20)

背景 有一些应用系统或应用功能&#xff0c;如日程管理、任务管理需要使用到日历组件。虽然Element Plus也提供了日历组件&#xff0c;但功能比较简单&#xff0c;用来做数据展现勉强可用。但如果需要进行复杂的数据展示&#xff0c;以及互动操作如通过点击添加事件&#xff0…

【Java--数据结构】二叉树

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 树结构 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合 注意&#xff1a;树形结构中&#xff0c;子…

昇思MindSpore学习开始

昇思MindSpore是一个全场景深度学习框架&#xff0c;旨在实现易开发、高效执行、全场景统一部署三大目标。 其中&#xff0c;易开发表现为API友好、调试难度低&#xff1b;高效执行包括计算效率、数据预处理效率和分布式训练效率&#xff1b;全场景则指框架同时支持云、边缘以…

二叉树、B树/B-树

二叉树 在中文语境中,节点结点傻傻分不清楚,故后文以 node 代表 "结点",root node 代表根节点,child node 代表 “子节点” 二叉树是诸多树状结构的始祖,至于为什么不是三叉树,四叉树,或许是因为计算机只能数到二吧,哈哈,开个玩笑。二叉树很简单,每个 no…

在android11 上实现平行视界效果

前言&#xff1a; 平行视界是谷歌为了解决大屏横屏设备 适配为手机等竖屏设备开发的APP &#xff0c; 在这类APP显示时 在横屏设备上不方便用户观看。 android 13 上平行视界的效果如下&#xff1a; 正文: 在android13前 &#xff0c;各家有各自的解决方案&#xff0c;下面提…

[计算机网络] VPN技术

VPN技术 1. 概述 虚拟专用网络&#xff08;VPN&#xff09;技术利用互联网服务提供商&#xff08;ISP&#xff09;和网络服务提供商&#xff08;NSP&#xff09;的网络基础设备&#xff0c;在公用网络中建立专用的数据通信通道。VPN的主要优点包括节约成本和提供安全保障。 优…

心理健康服务小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;学生管理&#xff0c;最新资讯管理&#xff0c;心理产品管理&#xff0c;产品分类管理&#xff0c;音乐理疗管理&#xff0c;试题管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;心理产品音…

学习大数据DAY17 PLSQL基础语法6和Git的基本操作

目录 包 存储过程调试功能 作业 阶段复习作业 Git课程目录 什么是版本控制 没有版本控制的缺点 常见的版本工具 版本控制分类 1. 本地版本控制 2. 集中版本控制 3. 分布式版本控制 Git与SVN主要区别 Git软件安装及配置 Windows系统安装Git 安装Tortoise Git(乌龟…

git和gitee的基本操作

目录 git常见命令 1.初始化工作区(在某一文件路径下) 2.查看当前工作区的代码文件状态 3.将工作区的代码文件提交到暂存区 4.将暂存区的代码文件提交到本地仓库 5.工作区和暂存区文件差异化比较 6.暂存区和本地仓库的差异化比较 7.工作区和本地仓库差异化比较 8.版本回…

自适应键盘,自带隐藏键盘的输入框(UITextField)

引言 在iOS开发中&#xff0c;输入框占据着举足轻重的地位。与安卓不同&#xff0c;iOS输入框经常面临键盘遮挡的问题&#xff0c;或者无法方便地取消键盘。为了解决这些问题&#xff0c;有许多针对iOS键盘管理的库&#xff0c;如IQKeyboardManager、TPKeyboardAvoiding和Keyb…

实习随笔【实现Json格式化与latex渲染】

【写在前面】在实习中&#xff0c;遇到了如下需求&#xff1a; 待格式化数据大概长这样&#xff0c;里面存在Json乱码以及由$$包裹的公式 目标格式&#xff1a; 一、Json格式化 我们这里的任务主要分为两部分&#xff1a; 解析一个可能包含嵌套的 JSON 字符串格式化 JSON 对象…

SAP ABAP性能优化分析工具

SAP系统提供了许多性能调优的工具&#xff0c;重点介绍下最常用几种SM50, ST05, SAT等工具&#xff1a; 1.工具概况 1.1 SM50 / SM66 - 工作进程监视器 通过这两个T-code, 可以查看当前SAP AS实例上面的工作进程&#xff0c;当某一工作进程长时间处于running的状态时&#…

支持前端路由权限和后端接口权限的企业管理系统模版

一、技术栈 前端&#xff1a;iview-admin vue 后端&#xff1a;springboot shiro 二、基于角色的权限控制 1、路由权限 即不同角色的路由访问控制 2、菜单权限 即不同角色的菜单列表展示 3、按钮权限 即不同角色的按钮展示 4、接口权限 即不同角色的接口访问控制 三…

C++——类和对象(下)

文章目录 一、再探构造函数——初始化列表二、 类型转换三、static成员静态成员变量静态成员函数 四、 友元友元函数友元类 五、内部类六、匿名对象 一、再探构造函数——初始化列表 之前我们实现构造函数时&#xff0c;初始化成员变量主要使⽤函数体内赋值&#xff0c;构造函…

16_网络IPC2-寻址

进程标识 字节序 采用大小模式对数据进行存放的主要区别在于在存放的字节顺序&#xff0c;大端方式将高位存放在低地址&#xff0c;小端方式将高位存放在高地址。 采用大端方式进行数据存放符合人类的正常思维&#xff0c;而采用小端方式进行数据存放利于计算机处理。到目前…

QT使用QPainter绘制多边形维度图

多边形统计维度图是一种用于展示多个维度的数据的图表。它通过将各个维度表示为图表中的多边形的边&#xff0c;根据数据的大小和比例来确定各个维度的长度。 一、简述 本示例实现六边形战力统计维度图&#xff0c;一种将六个维度的战力统计以六边形图形展示的方法。六个维度是…

WebAssembly与JavaScript的交互(1)

前一阵子利用Balazor开发了一个NuGet站点&#xff0c;对WebAssembly进行了初步的了解&#xff0c;觉得挺有意思。在接下来的一系列文章中&#xff0c;我们将通过实例演示的方式介绍WebAssembly的一些基本概念和编程模式。首先我们先来说说什么是WebAssembly&#xff0c;它主要帮…