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

news2024/11/15 4:20:45

     

目录

数据集下载和加载

可视化

构建生成器

构建判别器

优化器和损失函数

前向计算

计算梯度和反向传播

模型训练

模型推理


数据集下载和加载


        使用 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.coloradmin.cn/o/1932294.html

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

相关文章

如何PR到别人仓库(指定分支,无废话)

如何PR到别人仓库&#xff08;指定分支&#xff09; 记录一下&#xff0c;之前都是直接master分支&#xff0c;现在记录如何pr到别人仓库的其他分支 首先进入别人仓库然后点击fork到自己仓库 步骤&#xff08;以博主自己一个例子为例&#xff09; &#xff08;1&#xff09;…

配置和保护SSH

使用SSH访问远程命令行 描述Secure Shell SSH&#xff08;Secure Shell&#xff09; 是一种网络协议&#xff0c;用于在不安全的网络上安全地进行系统管理和数据传输。它最初由 Tatu Ylnen 于1995年设计&#xff0c;并成为保护网络服务免受攻击的标准。SSH提供了多种功能&…

prometheus+grafana应用监控配置

配置Prometheus 官方地址&#xff1a;Download | Prometheus &#xff08;wegt下载压缩包&#xff0c;解压并重命名prometheus&#xff0c;文件放于/data/prometheus即可&#xff09; 配置 service方法(文件放于 /etc/systemd/system/prometheus.service)&#xff1a; [Unit…

Windows与Ubuntu安装ffmpeg

文章目录 前言ffmpeg的简介安装ffmpegWindows下载设置环境变量 Ubuntu 总结 前言 FFmpeg是一款非常强大的开源音视频处理工具&#xff0c;它包含了众多的音视频编解码库&#xff0c;可以用于音视频的采集、编解码、转码、流化、过滤和播放等复杂的处理。在Windows系统上安装FF…

Talk|清华大学袁天远:PreSight - 利用NeRF先验帮助自动驾驶场景在线感知

本期为TechBeat人工智能社区第605期线上Talk。 北京时间7月3日(周三)20:00&#xff0c;清华大学博士生—袁天远的Talk已经准时在TechBeat人工智能社区开播&#xff01; 他与大家分享的主题是: “PreSight - 利用NeRF先验帮助自动驾驶场景在线感知”&#xff0c;他向大家介绍了新…

OBD诊断(ISO15031) 08服务

文章目录 功能简介ISO 9141-2、ISO 14230-4和SAE J1850的诊断服务定义1、请求控制车载设备请求消息&#xff08;读取支持的TID&#xff09;2、请求控制车载设备响应消息&#xff08;报告支持的TID&#xff09;3、请求控制车载设备请求消息&#xff08;读取TID值&#xff09;4、…

QTableView仿Excel表头排序和筛选

一.效果 Excel的排序和筛选弹窗如下所示 功能非常强大。不仅支持内容排序和筛选,还支持颜色的排序和筛选,而且还支持设置多种过滤条件。本文只仿最常用的内容排序和内容单过滤条件的筛选,效果如下所示。 从效果图中可以看出,表头Section中的按钮有下列六种状态 enum Butt…

自制OJ在线判题网站

项目效果啊演示&#xff1a; 项目链接&#xff1a; Gitee:Oj: 自定义实现的OJ在线做题网站&#xff0c;自主实现代码沙箱 (gitee.com) GitHub:GitHub - SakanaYdy/Oj 仓库中含有完整的项目流程PDF文档&#xff0c;可以参考自定义OJ网站。 目前只支持Java语言&#xff0c;可以…

【zabbix6监控java-tomcat全流程】

目录 一、监控主机安装zabbix-server1、zabbix的安装2、配置数据库3、为zabbix server配置数据库4、启动服务,web界面安装 二、被监控主机安装tomcat1、安装JDK2、安装tomcat 三、zabbix的服务端安装zabbix-java-gateway四、被监控主机tomcat的配置五、web界面添加主机 一、监控…

动态规划题目:单词拆分/三角形最小路径和 - leetcode

动态规划思想 / 步骤 &#xff1a; 先将 当前要求 总结成一个 精炼的 小问题 &#xff0c; 然后 将 求解题目 转换为 求解N个 小问题 &#xff0c; 每个小问题的 求解过程相同 &#xff0c;但是 过程涉及 的 数据 是不同的 &#xff0c; 例如第三个 小问…

windows qt编译报错 无法打开包括文件: “EGL/egl.h”: No such file or directory

windows mingw32 qt creator QtAV 推荐ffmpeg依赖包 QT5.14.2 如果出现&#xff1a;无法打开包括文件: “EGL/egl.h”: No such file or directory 可能是Qt6的问题.在QT5上安装。 编译步骤&#xff1a; git clone https://github.com/wang-bin/QtAV.git cd QtAV &&…

ABAQUS应用10-Abaqus/Standard求解器定义接触时的有限滑移和小滑移

[toc] 0、背景 之前在做混塔参数化建模的过程中&#xff0c;Part之间的接触设置问题困扰过我很久。有一个老哥手动操作一遍以后发现需要定义小滑移接触。今天正好看见曹老师公众号讲解了这部分的设置&#xff0c;为防止丢失&#xff0c;收录在这里。同时欢迎大家关注曹老师公…

【Git学习 | 第2篇】在IDEA中使用Git

文章目录 在IDEA中使用Git1. IDEA中配置Git2. 获取Git仓库2.1 本地初始化仓库2.2 从远程仓库克隆 3. 本地仓库操作4. 远程仓库操作5. 分支操作 在IDEA中使用Git 1. IDEA中配置Git IDEA中使用Git&#xff0c;本质上使用的本地安装的Git软件配置步骤&#xff1a; 2. 获取Git仓库…

【Python与GUI开发】事件处理与打包分发

文章目录 前言 一、高级事件处理 1.自定义事件 2.拖放操作 3.复杂控件的事件处理 二、打包和分发 Tkinter 应用 1.PyInstaller 2.cx_Freeze 3.spec 文件 4.分发注意事项 三、实战示例&#xff1a;文件浏览器 总结 前言 在前面的讨论中&#xff0c;我们深入理解了 T…

Pikachu SQL注入训练实例

1 数字类型注入 打开Burp Suit工具&#xff0c;选择Proxy&#xff0c;之后点击Open Browser打开浏览器&#xff0c;在浏览器中输入http://localhost:8080/pikachu-master打开Pikachu漏洞练习平台。 选择“数字型注入”&#xff0c;之后点击下拉框随便选择一个ID&#xff0c;…

linux进程——父子进程层面的PID,fork的原理与理解

前言&#xff1a;本篇内容主要讲解进程中系统调用fork和父子进程的概念与原理&#xff0c; 想要系统学习linux进程的友友们只管看本篇文章是不行的。 还要学习一些linux进程的周边知识以及linux进程其他方面的知识&#xff0c;博主的linux专栏中已经加入了这些文章方便友友们进…

【Java数据结构】初始线性表之一:链表

为什么要有链表 上一节我们描述了顺序表&#xff1a;【Java数据结构】初识线性表之一&#xff1a;顺序表-CSDN博客 并且进行了简单模拟实现。通过源码知道&#xff0c;ArrayList底层使用数组来存储元素。 由于其底层是一段连续空间&#xff0c;当在ArrayList任意位置插入或者…

GESP CCF C++ 七级认证真题 2024年6月

第 1 题 下列C代码的输出结果是&#xff08; &#xff09;。 #include <iostream> #include <cmath> using namespace std; int main() { cout << sin(3.1415926 / 2); return 0; } A. 0 B. 1 C.0.5 D.0.7071 第 2 题 对于如下图的二叉树&#x…

「C++」类和对象(1)

欢迎来到海盗猫鸥的博客~~ 本篇我们将学习部分C中的类和对象相关知识沃~ (• ω •)&#xff89;算我一个&#xff5e; 目录 类的定义 类的定义及使用 访问限定符 类域 实例化 实例化概念&#xff1a; 对象大小&#xff1a; 内存对齐规则&#xff1a; 注意点&#xff…

卸载docker简单且ok的方法

杀死所有容器 docker kill $(docker ps -a -q) 删除所有容器 docker rm $(docker ps -a -q) 删除所有镜像 docker rmi $(docker images -q) 停止docker服务 systemctl stop docker 查看安装列表 yum list installed|grep docker 依次卸载已安装的docker yum -y remove docke…