[{"data":1,"prerenderedAt":569},["ShallowReactive",2],{"content-query-0xP2rI6McZ":3},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"cover":11,"type":12,"category":13,"body":14,"_type":563,"_id":564,"_source":565,"_file":566,"_stem":567,"_extension":568},"/technology-blogs/zh/2110","zh",false,"","应用案例 | 实现图像到图像转换—Pix2Pix","cGAN的生成器与传统GAN的生成器在原理上有一些区别，cGAN的生成器是将输入图片作为指导信息，由输入图像不断尝试生成用于迷惑判别器的“假”图像，由输入图像转换输出为相应“假”图像的本质是从像素到另一个像素的映射，而传统GAN的生成器是基于一个给定的随机噪声生成图像，输出图像通过其他约束条件控制生成，这是cGAN和GAN的在图像翻译任务中的差异。","2023-02-09","https://obs-mindspore-file.obs.cn-north-4.myhuaweicloud.com/file/2023/02/17/7592f0983fcc4c30acec916776cc05d4.png","technology-blogs","实践",{"type":15,"children":16,"toc":545},"root",[17,25,49,54,59,67,72,77,82,87,96,101,109,114,121,126,133,138,147,152,162,170,175,182,190,195,203,208,215,220,228,233,241,248,256,267,277,282,290,300,308,317,325,330,339,344,352,361,366,374,382,388,396,404,409,417,425,432,437,445,450,458,463,468,475,480,487,492,500,505,512,520,525,538],{"type":18,"tag":19,"props":20,"children":22},"element","h1",{"id":21},"应用案例-实现图像到图像转换pix2pix",[23],{"type":24,"value":8},"text",{"type":18,"tag":26,"props":27,"children":28},"p",{},[29,35,37,42,44],{"type":18,"tag":30,"props":31,"children":32},"strong",{},[33],{"type":24,"value":34},"作者",{"type":24,"value":36},"：",{"type":18,"tag":30,"props":38,"children":39},{},[40],{"type":24,"value":41},"高晓亮",{"type":24,"value":43}," ｜**学校：**",{"type":18,"tag":30,"props":45,"children":46},{},[47],{"type":24,"value":48},"兰州大学",{"type":18,"tag":26,"props":50,"children":51},{},[52],{"type":24,"value":53},"Pix2Pix是基于条件生成对抗网络（cGAN, Condition Generative Adversarial Networks ）实现的一种深度学习图像转换模型，该模型是由Phillip Isola等作者在2017年CVPR上提出的,可以实现语义/标签到真实图片、灰度图到彩色图、航空图到地图、白天到黑夜、线稿图到实物图的转换。Pix2Pix是将cGAN应用于有监督的图像到图像翻译的经典之作，其包括两个模型：生成器和判别器。",{"type":18,"tag":26,"props":55,"children":56},{},[57],{"type":24,"value":58},"传统上，尽管此类任务的目标都是相同的从像素预测像素，但每项都是用单独的专用机器来处理的。而Pix2Pix使用的网络作为一个通用框架，使用相同的架构和目标，只在不同的数据上进行训练，即可得到令人满意的结果，鉴于此许多人已经使用此网络发布了他们自己的艺术作品。",{"type":18,"tag":26,"props":60,"children":61},{},[62],{"type":18,"tag":30,"props":63,"children":64},{},[65],{"type":24,"value":66},"基础原理",{"type":18,"tag":26,"props":68,"children":69},{},[70],{"type":24,"value":71},"cGAN的生成器与传统GAN的生成器在原理上有一些区别，cGAN的生成器是将输入图片作为指导信息，由输入图像不断尝试生成用于迷惑判别器的“假”图像，由输入图像转换输出为相应“假”图像的本质是从像素到另一个像素的映射，而传统GAN的生成器是基于一个给定的随机噪声生成图像，输出图像通过其他约束条件控制生成，这是cGAN和GAN的在图像翻译任务中的差异。Pix2Pix中判别器的任务是判断从生成器输出的图像是真实的训练图像还是生成的“假”图像。在生成器与判别器的不断博弈过程中，模型会达到一个平衡点，生成器输出的图像与真实训练数据使得判别器刚好具有50%的概率判断正确。",{"type":18,"tag":26,"props":73,"children":74},{},[75],{"type":24,"value":76},"在教程开始前，首先定义一些在整个过程中需要用到的符号：",{"type":18,"tag":26,"props":78,"children":79},{},[80],{"type":24,"value":81},"x：代表观测图像的数据。",{"type":18,"tag":26,"props":83,"children":84},{},[85],{"type":24,"value":86},"z：代表随机噪声的数据。",{"type":18,"tag":26,"props":88,"children":89},{},[90],{"type":18,"tag":91,"props":92,"children":95},"img",{"alt":93,"src":94},"cke_741.png","https://fileserver.developer.huaweicloud.com/FileServer/getFile/cmtybbs/e64/154/b38/90a1d5d431e64154b387b3660e356ff5.20230216084225.24560486864741108449818173008446:50540216011332:2400:F67EDCCEE354033A31E5F27D92CA5D94842EF2E44A53AA5097DDBF1CBF28FE68.png",[],{"type":18,"tag":26,"props":97,"children":98},{},[99],{"type":24,"value":100},"为了对比cGAN和GAN的不同，我们将GAN的目标也进行了说明：",{"type":18,"tag":26,"props":102,"children":103},{},[104],{"type":18,"tag":91,"props":105,"children":108},{"alt":106,"src":107},"image.png","https://fileserver.developer.huaweicloud.com/FileServer/getFile/cmtybbs/e64/154/b38/90a1d5d431e64154b387b3660e356ff5.20230216084244.59240708538216935107598253634324:50540216011332:2400:89498335CFE7F0FE8FA9A5E05E75BFA89861AF8A729EFC56E38A1F30D8BB1BDB.png",[],{"type":18,"tag":26,"props":110,"children":111},{},[112],{"type":24,"value":113},"从公式可以看出，GAN直接由随机噪声z生成“假”图像，不借助观测图像x的任何信息。过去的经验告诉我们，GAN与传统损失混合使用是有好处的，判别器的任务不变，依旧是区分真实图像与“假”图像，但是生成器的任务不仅要欺骗判别器，还要在传统损失的基础上接近训练数据。假设cGAN与L1正则化混合使用，那么有:",{"type":18,"tag":26,"props":115,"children":116},{},[117],{"type":18,"tag":91,"props":118,"children":120},{"alt":106,"src":119},"https://fileserver.developer.huaweicloud.com/FileServer/getFile/cmtybbs/e64/154/b38/90a1d5d431e64154b387b3660e356ff5.20230216084343.55121125195913505714010850279499:50540216011332:2400:A2CE3C3337360D384B5FD20BD7329D525DDDE520F69005B08408BF83DF4F0012.png",[],{"type":18,"tag":26,"props":122,"children":123},{},[124],{"type":24,"value":125},"进而得到最终目标：",{"type":18,"tag":26,"props":127,"children":128},{},[129],{"type":18,"tag":91,"props":130,"children":132},{"alt":106,"src":131},"https://fileserver.developer.huaweicloud.com/FileServer/getFile/cmtybbs/e64/154/b38/90a1d5d431e64154b387b3660e356ff5.20230216084435.02718555401576258265543624521427:50540216011332:2400:1A8E522C41DF9A9D03760949297C8FCF02C1E1F1D9CE4A2714D6A50B9C71CE61.png",[],{"type":18,"tag":26,"props":134,"children":135},{},[136],{"type":24,"value":137},"图像转换问题本质上其实就是像素到像素的映射问题，Pix2Pix使用完全一样的网络结构和目标函数，仅更换不同的训练数据集就能分别实现以上的任务。本任务将借助昇思MindSpore框架来实现Pix2Pix的应用。",{"type":18,"tag":139,"props":140,"children":142},"h3",{"id":141},"配置环境文件",[143],{"type":18,"tag":30,"props":144,"children":145},{},[146],{"type":24,"value":141},{"type":18,"tag":26,"props":148,"children":149},{},[150],{"type":24,"value":151},"本教程我们在GPU环境下，使用图模式运行实验。",{"type":18,"tag":153,"props":154,"children":156},"pre",{"code":155},"from mindspore import context\n\n#选择执行模式为图模式；指定训练使用的平台为\"GPU\"，如需使用昇腾硬件可将其替换为\"Ascend\"\ncontext.set_context(mode=context.GRAPH_MODE, device_target=\"GPU\")\n",[157],{"type":18,"tag":158,"props":159,"children":160},"code",{"__ignoreMap":7},[161],{"type":24,"value":155},{"type":18,"tag":139,"props":163,"children":165},{"id":164},"准备数据",[166],{"type":18,"tag":30,"props":167,"children":168},{},[169],{"type":24,"value":164},{"type":18,"tag":26,"props":171,"children":172},{},[173],{"type":24,"value":174},"在本教程中，我们将使用指定数据集，该数据集为六种数据集，分别为外墙（facades）、市景（cityscapes）、地图（maps）、昼夜图（night2day）、鞋子图片及对应线条图（edges2shoes）、手包图片及对应线条图（edges2handbags）。 每类数据集均保存于/datasets文件夹下，如/datasets/maps等，具体结构及图片数量如下。",{"type":18,"tag":26,"props":176,"children":177},{},[178],{"type":18,"tag":91,"props":179,"children":181},{"alt":106,"src":180},"https://fileserver.developer.huaweicloud.com/FileServer/getFile/cmtybbs/e64/154/b38/90a1d5d431e64154b387b3660e356ff5.20230216084521.43481518282281478863851636111141:50540216011332:2400:942881DC3E86EDCBE8DEE3EDB215B9A93FA90AD5D106A27B69E705A03D60FFF2.png",[],{"type":18,"tag":139,"props":183,"children":185},{"id":184},"数据处理",[186],{"type":18,"tag":30,"props":187,"children":188},{},[189],{"type":24,"value":184},{"type":18,"tag":26,"props":191,"children":192},{},[193],{"type":24,"value":194},"以facades数据集为例，首先定义一些配置参数：",{"type":18,"tag":153,"props":196,"children":198},{"code":197},"train_data_dir = \"/home/pix2pix/end/datasets/facades/train/\"                    # 训练集的文件路径\nepoch_num = 200                                                                 # 训练迭代次数\nbatch_size = 1                                                                  # 训练中使用的批量大小\ndataset_size = 400                                                              # 训练数据集大小\nval_data_dir = \"/home/pix2pix/end/datasets/facades/val/\"                        # 推理数据集的文件路径\nckpt = \"results/ckpt/Generator_200.ckpt\"                                        # 推理用ckpt文件的文件路径\ndevice_target = \"GPU\"                                                           # GPU或Ascend\n# 以上参数需要根据不同数据集单独设置\n",[199],{"type":18,"tag":158,"props":200,"children":201},{"__ignoreMap":7},[202],{"type":24,"value":197},{"type":18,"tag":26,"props":204,"children":205},{},[206],{"type":24,"value":207},"各个数据集的训练轮次数epoch_num、输入图像批次batch_size等不同，需根据不同数据集相应设置。论文参考值展示如下：",{"type":18,"tag":26,"props":209,"children":210},{},[211],{"type":18,"tag":91,"props":212,"children":214},{"alt":106,"src":213},"https://fileserver.developer.huaweicloud.com/FileServer/getFile/cmtybbs/e64/154/b38/90a1d5d431e64154b387b3660e356ff5.20230216084607.05564413091721328748464992417523:50540216011332:2400:744106B4EC92F25BAAC2477B470686D920B288D558B62D835C79FCFD4E3E4BEE.png",[],{"type":18,"tag":26,"props":216,"children":217},{},[218],{"type":24,"value":219},"定义Pix2PixDataset和create_train_dataset函数对训练数据进行处理和增强操作。",{"type":18,"tag":153,"props":221,"children":223},{"code":222},"import os\nimport numpy as np\nfrom PIL import Image\n\nimport mindspore\nfrom mindspore import dataset as ds\nimport mindspore.dataset.vision.c_transforms as transforms\n\n\ndef get_params(load_size, train_pic_size):\n    \"\"\"从图像中获取参数\"\"\"\n\n    new_h = new_w = load_size\n\n    x = np.random.randint(0, np.maximum(0, new_w - train_pic_size))\n    y = np.random.randint(0, np.maximum(0, new_h - train_pic_size))\n\n    return x, y\n\ndef crop(img, pos, load_size, train_pic_size):\n    \"\"\"裁剪图像\"\"\"\n\n    ow = oh = load_size\n    x1, y1 = pos\n    tw = th = train_pic_size\n    if ow > tw or oh > th:\n        img = img.crop((x1, y1, x1 + tw, y1 + th))\n        return img\n    return img\n\ndef sync_random_horizontal_flip(input_images, target_images):\n    \"\"\"随机翻转输入图像和目标图像\"\"\"\n\n    seed = np.random.randint(0, 2000000000)\n    mindspore.set_seed(seed)\n    op = transforms.RandomHorizontalFlip(prob=0.5)\n    out_input = op(input_images)\n    mindspore.set_seed(seed)\n    op = transforms.RandomHorizontalFlip(prob=0.5)\n    out_target = op(target_images)\n    return out_input, out_target\n\nclass Pix2PixDataset():\n    \"\"\"定义训练数据集过程\"\"\"\n\n    def __init__(self, root_dir, config):\n        self.root_dir = root_dir\n        self.list_files = os.listdir(self.root_dir)\n        self.load_size = config.load_size\n        self.train_pic_size = config.train_pic_size\n\n    def __len__(self):\n        return len(self.list_files)\n\n    def __getitem__(self, index):\n        img_file = self.list_files[index]\n        img_path = os.path.join(self.root_dir, img_file)\n        ab = Image.open(img_path).convert('RGB')\n        w, h = ab.size\n        w2 = int(w / 2)\n\n        a = ab.crop((w2, 0, w, h))\n        b = ab.crop((0, 0, w2, h))\n\n        a = a.resize((self.load_size, self.load_size))\n        b = b.resize((self.load_size, self.load_size))\n\n        transform_params = get_params(self.load_size, self.train_pic_size)\n        a_crop = crop(a, transform_params, self.load_size, self.train_pic_size)\n        b_crop = crop(b, transform_params, self.load_size, self.train_pic_size)\n\n        return a_crop, b_crop\n\ndef create_train_dataset(dataset, batch_size):\n    \"\"\"创建训练集\"\"\"\n\n    mean = [0.5 * 255] * 3\n    std = [0.5 * 255] * 3\n\n    trans = [\n        transforms.Normalize(mean=mean, std=std),\n        transforms.HWC2CHW()\n    ]\n\n    train_ds = ds.GeneratorDataset(dataset, column_names=[\"input_images\", \"target_images\"], shuffle=False)\n\n    train_ds = train_ds.map(operations=[sync_random_horizontal_flip], input_columns=[\"input_images\", \"target_images\"])\n\n    train_ds = train_ds.map(operations=trans, input_columns=[\"input_images\"])\n    train_ds = train_ds.map(operations=trans, input_columns=[\"target_images\"])\n\n    train_ds = train_ds.batch(batch_size=batch_size, drop_remainder=True)\n\n    return train_ds\n",[224],{"type":18,"tag":158,"props":225,"children":226},{"__ignoreMap":7},[227],{"type":24,"value":222},{"type":18,"tag":26,"props":229,"children":230},{},[231],{"type":24,"value":232},"调用Pix2PixDataset和create_train_dataset读取训练集，通过create_dict_iterator函数将数据转换成字典迭代器，然后使用matplotlib模块可视化部分训练数据。",{"type":18,"tag":153,"props":234,"children":236},{"code":235},"import matplotlib.pyplot as plt\n\nfrom src.process_datasets.dataset import Pix2PixDataset, create_train_dataset\nfrom src.config.pix2pix_config import parse_args\n\nconfig = parse_args()\ndataset = Pix2PixDataset(root_dir='/home/ma-user/work/fixpix2pix/datasets/facades/train/', config=config) # 以facades数据集为例\nds = create_train_dataset(dataset, batch_size=30) # 选择一批30张图片\ndata_iter = next(ds.create_dict_iterator(output_numpy=True))\n# 可视化部分训练数据\nplt.figure(figsize=(10, 3), dpi=140)\nfor i, image in enumerate(data_iter['input_images'][:30], 1):\n    plt.subplot(3, 10, i)\n    plt.axis(\"off\")\n    plt.imshow(image.transpose(1, 2, 0))\nplt.show()\n",[237],{"type":18,"tag":158,"props":238,"children":239},{"__ignoreMap":7},[240],{"type":24,"value":235},{"type":18,"tag":26,"props":242,"children":243},{},[244],{"type":18,"tag":91,"props":245,"children":247},{"alt":106,"src":246},"https://fileserver.developer.huaweicloud.com/FileServer/getFile/cmtybbs/e64/154/b38/90a1d5d431e64154b387b3660e356ff5.20230216090015.79379816870468213152049315905616:50540216011332:2400:B0DF282BD8A12D09A2578F6EE801A3CDD940CCC23BBF91CE9461358E96000EC8.png",[],{"type":18,"tag":26,"props":249,"children":250},{},[251],{"type":18,"tag":30,"props":252,"children":253},{},[254],{"type":24,"value":255},"创建网络",{"type":18,"tag":26,"props":257,"children":258},{},[259,261,265],{"type":24,"value":260},"当处理完数据后，就可以来进行网络的搭建了。网络搭建将逐一详细讨论生成器、判别器和损失函数。生成器G用到的是U-Net结构，输入的轮廓图x编码再解码成真是图片，判别器D用到的是作者自己提出来的条件判别器PatchGAN，判别器D的作用是在轮廓图x的条件下，对于生成的图片",{"type":18,"tag":91,"props":262,"children":264},{"alt":106,"src":263},"https://fileserver.developer.huaweicloud.com/FileServer/getFile/cmtybbs/e64/154/b38/90a1d5d431e64154b387b3660e356ff5.20230216090256.61816163397357212064365102406966:50540216011332:2400:4663B65A5902A50CB1EBD979E77304EC8E8D9C7D7C0938355D671081718C6283.png",[],{"type":24,"value":266},"判断为假，对于真实判断为真。",{"type":18,"tag":268,"props":269,"children":271},"h2",{"id":270},"生成器g结构",[272],{"type":18,"tag":30,"props":273,"children":274},{},[275],{"type":24,"value":276},"生成器G结构",{"type":18,"tag":26,"props":278,"children":279},{},[280],{"type":24,"value":281},"U-Net是德国Freiburg大学模式识别和图像处理组提出的一种全卷积结构。它分为两个部分，其中左侧是由卷积和降采样操作组成的压缩路径，右侧是由卷积和上采样组成的扩张路径，扩张的每个网络块的输入由上一层上采样的特征和压缩路径部分的特征拼接而成。网络模型整体是一个U形的结构，因此被叫做U-Net。和常见的先降采样到低维度，再升采样到原始分辨率的编解码结构的网络相比，U-Net的区别是加入skip-connection，对应的feature maps和decode之后的同样大小的feature maps按通道拼一起，用来保留不同分辨率下像素级的细节信息。",{"type":18,"tag":26,"props":283,"children":284},{},[285],{"type":18,"tag":91,"props":286,"children":289},{"alt":287,"src":288},"640.png","https://fileserver.developer.huaweicloud.com/FileServer/getFile/cmtybbs/e64/154/b38/90a1d5d431e64154b387b3660e356ff5.20230216090228.74786449580432110069612442923998:50540216011332:2400:F905988F023B1ED5F4E132BF5D527B0BFFA601A648687F065CE57484ECF86D0B.png",[],{"type":18,"tag":291,"props":292,"children":294},"h4",{"id":293},"定义unet-skip-connection-block",[295],{"type":18,"tag":30,"props":296,"children":297},{},[298],{"type":24,"value":299},"定义UNet Skip Connection Block",{"type":18,"tag":153,"props":301,"children":303},{"code":302},"import mindspore.nn as nn\nimport mindspore.ops as ops\n\n\nclass UNetSkipConnectionBlock(nn.Cell):\n    \"\"\"UNet子模块\"\"\"\n\n    def __init__(self, outer_nc, inner_nc, in_planes=None, dropout=False,\n                 submodule=None, outermost=False, innermost=False, alpha=0.2, norm_mode='batch'):\n        super(UNetSkipConnectionBlock, self).__init__()\n        downnorm = nn.BatchNorm2d(inner_nc)\n        upnorm = nn.BatchNorm2d(outer_nc)\n        use_bias = False\n        if norm_mode == 'instance':\n            downnorm = nn.BatchNorm2d(inner_nc, affine=False)\n            upnorm = nn.BatchNorm2d(outer_nc, affine=False)\n            use_bias = True\n        if in_planes is None:\n            in_planes = outer_nc\n        downconv = nn.Conv2d(in_planes, inner_nc, kernel_size=4,\n                             stride=2, padding=1, has_bias=use_bias, pad_mode='pad')\n        downrelu = nn.LeakyReLU(alpha)\n        uprelu = nn.ReLU()\n\n        if outermost:\n            upconv = nn.Conv2dTranspose(inner_nc * 2, outer_nc,\n                                        kernel_size=4, stride=2,\n                                        padding=1, pad_mode='pad')\n            down = [downconv]\n            up = [uprelu, upconv, nn.Tanh()]\n            model = down + [submodule] + up\n        elif innermost:\n            upconv = nn.Conv2dTranspose(inner_nc, outer_nc,\n                                        kernel_size=4, stride=2,\n                                        padding=1, has_bias=use_bias, pad_mode='pad')\n            down = [downrelu, downconv]\n            up = [uprelu, upconv, upnorm]\n            model = down + up\n        else:\n            upconv = nn.Conv2dTranspose(inner_nc * 2, outer_nc,\n                                        kernel_size=4, stride=2,\n                                        padding=1, has_bias=use_bias, pad_mode='pad')\n            down = [downrelu, downconv, downnorm]\n            up = [uprelu, upconv, upnorm]\n\n            model = down + [submodule] + up\n            if dropout:\n                model.append(nn.Dropout(0.5))\n\n        self.model = nn.SequentialCell(model)\n        self.skip_connections = not outermost\n        self.concat = ops.Concat(axis=1)\n\n    def construct(self, x):\n        out = self.model(x)\n        if self.skip_connections:\n            out = self.concat((out, x))\n        return out\n",[304],{"type":18,"tag":158,"props":305,"children":306},{"__ignoreMap":7},[307],{"type":24,"value":302},{"type":18,"tag":291,"props":309,"children":311},{"id":310},"基于unet的生成器",[312],{"type":18,"tag":30,"props":313,"children":314},{},[315],{"type":24,"value":316},"基于UNet的生成器",{"type":18,"tag":153,"props":318,"children":320},{"code":319},"import mindspore.nn as nn\n\nfrom src.models.unet_block import UNetSkipConnectionBlock\n\n\nclass UNetGenerator(nn.Cell):\n    \"\"\"基于UNet的生成器\"\"\"\n\n    def __init__(self, in_planes, out_planes, ngf=64, n_layers=8, norm_mode='bn', dropout=False):\n        super(UNetGenerator, self).__init__()\n        # construct unet structure\n        unet_block = UNetSkipConnectionBlock(ngf * 8, ngf * 8, in_planes=None, submodule=None,\n                                             norm_mode=norm_mode, innermost=True)\n        for _ in range(n_layers - 5):\n            unet_block = UNetSkipConnectionBlock(ngf * 8, ngf * 8, in_planes=None, submodule=unet_block,\n                                                 norm_mode=norm_mode, dropout=dropout)\n        # gradually reduce the number of filters from ngf * 8 to ngf\n        unet_block = UNetSkipConnectionBlock(ngf * 4, ngf * 8, in_planes=None, submodule=unet_block,\n                                             norm_mode=norm_mode)\n        unet_block = UNetSkipConnectionBlock(ngf * 2, ngf * 4, in_planes=None, submodule=unet_block,\n                                             norm_mode=norm_mode)\n        unet_block = UNetSkipConnectionBlock(ngf, ngf * 2, in_planes=None, submodule=unet_block,\n                                             norm_mode=norm_mode)\n        self.model = UNetSkipConnectionBlock(out_planes, ngf, in_planes=in_planes, submodule=unet_block,\n                                             outermost=True, norm_mode=norm_mode)\n\n    def construct(self, x):\n        return self.model(x)\n",[321],{"type":18,"tag":158,"props":322,"children":323},{"__ignoreMap":7},[324],{"type":24,"value":319},{"type":18,"tag":26,"props":326,"children":327},{},[328],{"type":24,"value":329},"原始cGAN的输入是条件x和噪声z两种信息，这里的生成器只使用了条件信息，因此不能生成多样性的结果。因此Pix2Pix在训练和测试时都使用了dropout，这样可以生成多样性的结果。",{"type":18,"tag":139,"props":331,"children":333},{"id":332},"基于patchgan的判别器",[334],{"type":18,"tag":30,"props":335,"children":336},{},[337],{"type":24,"value":338},"基于PatchGAN的判别器",{"type":18,"tag":26,"props":340,"children":341},{},[342],{"type":24,"value":343},"判别器使用的PatchGAN结构，可看做卷积。生成的矩阵中的每个点代表原图的一小块区域（patch）。通过矩阵中的各个值来判断原图中对应每个Patch的真假。",{"type":18,"tag":153,"props":345,"children":347},{"code":346},"import mindspore.nn as nn\nfrom mindspore.ops import Concat\n\n\nclass ConvNormRelu(nn.Cell):\n    \"\"\"操作融合的卷积块\"\"\"\n\n    def __init__(self,\n                 in_planes,\n                 out_planes,\n                 config,\n                 kernel_size=4,\n                 stride=2,\n                 alpha=0.2,\n                 norm_mode='batch',\n                 pad_mode='CONSTANT',\n                 use_relu=True,\n                 padding=None):\n        super(ConvNormRelu, self).__init__()\n        norm = nn.BatchNorm2d(out_planes)\n        if norm_mode == 'instance':\n            norm = nn.BatchNorm2d(out_planes, affine=False)\n        has_bias = (norm_mode == 'instance')\n        if not padding:\n            padding = (kernel_size - 1) // 2\n        if config.pad_mode == 'REFLECT':\n            pad_mode = \"REFLECT\"\n        elif config.pad_mode == \"SYMMETRIC\":\n            pad_mode = \"SYMMETRIC\"\n        if pad_mode == 'CONSTANT':\n            conv = nn.Conv2d(in_planes, out_planes, kernel_size, stride, pad_mode='pad',\n                             has_bias=has_bias, padding=padding)\n            layers = [conv, norm]\n        else:\n            paddings = ((0, 0), (0, 0), (padding, padding), (padding, padding))\n            pad = nn.Pad(paddings=paddings, mode=pad_mode)\n            conv = nn.Conv2d(in_planes, out_planes, kernel_size, stride, pad_mode='pad', has_bias=has_bias)\n            layers = [pad, conv, norm]\n        if use_relu:\n            relu = nn.ReLU()\n            if alpha > 0:\n                relu = nn.LeakyReLU(alpha)\n            layers.append(relu)\n        self.features = nn.SequentialCell(layers)\n\n    def construct(self, x):\n        output = self.features(x)\n        return output\n\nclass Discriminator(nn.Cell):\n    \"\"\"判别器模型\"\"\"\n\n    def __init__(self, config, in_planes=3, ndf=64, n_layers=3, alpha=0.2, norm_mode='batch'):\n        super(Discriminator, self).__init__()\n        kernel_size = 4\n        layers = [\n            nn.Conv2d(in_planes, ndf, kernel_size, 2, pad_mode='pad', padding=1),\n            nn.LeakyReLU(config.alpha)\n        ]\n        nf_mult = ndf\n        for i in range(1, n_layers):\n            nf_mult_prev = nf_mult\n            nf_mult = min(2 ** i, 8) * ndf\n            layers.append(ConvNormRelu(nf_mult_prev, nf_mult, config, kernel_size, 2, alpha, norm_mode, padding=1))\n        nf_mult_prev = nf_mult\n        nf_mult = min(2 ** n_layers, 8) * ndf\n        layers.append(ConvNormRelu(nf_mult_prev, nf_mult, config, kernel_size, 1, alpha, norm_mode, padding=1))\n        layers.append(nn.Conv2d(nf_mult, 1, kernel_size, 1, pad_mode='pad', padding=1))\n\n        self.features = nn.SequentialCell(layers)\n        self.concat = Concat(axis=1)\n\n    def construct(self, x, y):\n        x_y = self.concat((x, y))\n        output = self.features(x_y)\n        return output\n",[348],{"type":18,"tag":158,"props":349,"children":350},{"__ignoreMap":7},[351],{"type":24,"value":346},{"type":18,"tag":139,"props":353,"children":355},{"id":354},"pix2pix的生成器和判别器初始化",[356],{"type":18,"tag":30,"props":357,"children":358},{},[359],{"type":24,"value":360},"Pix2Pix的生成器和判别器初始化",{"type":18,"tag":26,"props":362,"children":363},{},[364],{"type":24,"value":365},"实例化Pix2Pix生成器和判别器。",{"type":18,"tag":153,"props":367,"children":369},{"code":368},"import mindspore.nn as nn\nfrom mindspore.common import initializer as init\n\nfrom src.models.generator import UNetGenerator\nfrom src.models.discriminator import Discriminator\nfrom src.config.pix2pix_config import parse_args\n\ndef get_generator(config):\n    \"\"\"通过参数返回一个生成器\"\"\"\n\n    net_generator = UNetGenerator(in_planes=config.g_in_planes, out_planes=config.g_out_planes,\n                                  ngf=config.g_ngf, n_layers=config.g_layers)\n    for _, cell in net_generator.cells_and_names():\n        if isinstance(cell, (nn.Conv2d, nn.Conv2dTranspose)):\n            if config.init_type == 'normal':\n                cell.weight.set_data(init.initializer(init.Normal(config.init_gain), cell.weight.shape))\n            elif config.init_type == 'xavier':\n                cell.weight.set_data(init.initializer(init.XavierUniform(config.init_gain), cell.weight.shape))\n            elif config.init_type == 'constant':\n                cell.weight.set_data(init.initializer(0.001, cell.weight.shape))\n            else:\n                raise NotImplementedError('initialization method [%s] is not implemented' % config.init_type)\n        elif isinstance(cell, nn.BatchNorm2d):\n            cell.gamma.set_data(init.initializer('ones', cell.gamma.shape))\n            cell.beta.set_data(init.initializer('zeros', cell.beta.shape))\n    return net_generator\n\ndef get_discriminator(config):\n    \"\"\"通过参数返回一个判别器\"\"\"\n\n    net_discriminator = Discriminator(config, in_planes=config.d_in_planes, ndf=config.d_ndf,\n                                      alpha=config.alpha, n_layers=config.d_layers)\n    for _, cell in net_discriminator.cells_and_names():\n        if isinstance(cell, (nn.Conv2d, nn.Conv2dTranspose)):\n            if config.init_type == 'normal':\n                cell.weight.set_data(init.initializer(init.Normal(config.init_gain), cell.weight.shape))\n            elif config.init_type == 'xavier':\n                cell.weight.set_data(init.initializer(init.XavierUniform(config.init_gain), cell.weight.shape))\n            elif config.init_type == 'constant':\n                cell.weight.set_data(init.initializer(0.001, cell.weight.shape))\n            else:\n                raise NotImplementedError('initialization method [%s] is not implemented' % config.init_type)\n        elif isinstance(cell, nn.BatchNorm2d):\n            cell.gamma.set_data(init.initializer('ones', cell.gamma.shape))\n            cell.beta.set_data(init.initializer('zeros', cell.beta.shape))\n    return net_discriminator\n\nclass Pix2Pix(nn.Cell):\n    \"\"\"Pix2Pix模型网络\"\"\"\n    def __init__(self, discriminator, generator):\n        super(Pix2Pix, self).__init__(auto_prefix=True)\n        self.net_discriminator = discriminator\n        self.net_generator = generator\n\n    def construct(self, reala):\n        fakeb = self.net_generator(reala)\n        return fakeb\n\nconfig = parse_args()\nnet_generator = get_generator(config)\nnet_discriminator = get_discriminator(config)\npix2pix = Pix2Pix(generator=net_generator, discriminator=net_discriminator)\n",[370],{"type":18,"tag":158,"props":371,"children":372},{"__ignoreMap":7},[373],{"type":24,"value":368},{"type":18,"tag":139,"props":375,"children":377},{"id":376},"连接网络和损失函数",[378],{"type":18,"tag":30,"props":379,"children":380},{},[381],{"type":24,"value":376},{"type":18,"tag":139,"props":383,"children":385},{"id":384},"昇思mindspore将损失函数优化器等操作都封装到了cell中因为gan结构上的特殊性其损失是判别器和生成器的多输出形式这就导致它和一般的分类网络不同所以我们需要自定义withlosscell类将网络和loss连接起来",[386],{"type":24,"value":387},"昇思MindSpore将损失函数、优化器等操作都封装到了Cell中，因为GAN结构上的特殊性，其损失是判别器和生成器的多输出形式，这就导致它和一般的分类网络不同。所以我们需要自定义WithLossCell类，将网络和Loss连接起来。",{"type":18,"tag":153,"props":389,"children":391},{"code":390},"import mindspore.nn as nn\nimport mindspore.ops as ops\nfrom mindspore.ops import functional as opsf\nimport mindspore.ops.operations as opsp\nfrom mindspore.parallel._utils import (_get_device_num, _get_gradients_mean, _get_parallel_mode)\nfrom mindspore.context import ParallelMode\nfrom mindspore.nn.wrap.grad_reducer import DistributedGradReducer\nfrom mindspore.nn.loss.loss import LossBase\n\n\nclass SigmoidCrossEntropyWithLogits(LossBase):\n    \"\"\"计算预测值与真实值之间的sigmoid交叉熵\"\"\"\n\n    def __init__(self):\n        super(SigmoidCrossEntropyWithLogits, self).__init__()\n        self.cross_entropy = opsp.SigmoidCrossEntropyWithLogits()\n\n    def construct(self, data, label):\n        x = self.cross_entropy(data, label)\n        return self.get_loss(x)\n\nclass LossD(LossBase):\n    \"\"\"定义鉴别器损失\"\"\"\n\n    def __init__(self, config, reduction=\"mean\"):\n        super(LossD, self).__init__(reduction)\n        self.sig = SigmoidCrossEntropyWithLogits()\n        self.ones = ops.OnesLike()\n        self.zeros = ops.ZerosLike()\n        self.lambda_dis = config.lambda_dis\n\n    def construct(self, pred1, pred0):\n        loss = self.sig(pred1, self.ones(pred1)) + self.sig(pred0, self.zeros(pred0))\n        dis_loss = loss * self.lambda_dis\n        return dis_loss\n\n\nclass WithLossCellD(nn.Cell):\n    \"\"\"定义判别器损失封装连接网络\"\"\"\n\n    def __init__(self, backbone, loss_fn):\n        super(WithLossCellD, self).__init__(auto_prefix=True)\n        self.net_discriminator = backbone.net_discriminator\n        self.net_generator = backbone.net_generator\n        self._loss_fn = loss_fn\n\n    def construct(self, reala, realb):\n        fakeb = self.net_generator(reala)\n        pred1 = self.net_discriminator(reala, realb)\n        pred0 = self.net_discriminator(reala, fakeb)\n        return self._loss_fn(pred1, pred0)\n\n\nclass LossG(LossBase):\n    \"\"\"定义生成器损失\"\"\"\n\n    def __init__(self, config, reduction=\"mean\"):\n        super(LossG, self).__init__(reduction)\n        self.sig = SigmoidCrossEntropyWithLogits()\n        self.l1_loss = nn.L1Loss()\n        self.ones = ops.OnesLike()\n        self.lambda_gan = config.lambda_gan\n        self.lambda_l1 = config.lambda_l1\n\n    def construct(self, fakeb, realb, pred0):\n        loss_1 = self.sig(pred0, self.ones(pred0))\n        loss_2 = self.l1_loss(fakeb, realb)\n        loss = loss_1 * self.lambda_gan + loss_2 * self.lambda_l1\n        return loss\n\n\nclass WithLossCellG(nn.Cell):\n    \"\"\"定义生成器损失封装连接网络\"\"\"\n\n    def __init__(self, backbone, loss_fn):\n        super(WithLossCellG, self).__init__(auto_prefix=True)\n        self.net_discriminator = backbone.net_discriminator\n        self.net_generator = backbone.net_generator\n        self._loss_fn = loss_fn\n\n    def construct(self, reala, realb):\n        fakeb = self.net_generator(reala)\n        pred0 = self.net_discriminator(reala, fakeb)\n        return self._loss_fn(fakeb, realb, pred0)\n\n\nclass TrainOneStepCell(nn.Cell):\n    \"\"\"定义训练网络封装类\"\"\"\n\n    def __init__(self, loss_netd, loss_netg, optimizerd, optimizerg, sens=1, auto_prefix=True):\n        super(TrainOneStepCell, self).__init__(auto_prefix=auto_prefix)\n        self.loss_net_d = loss_netd\n        self.loss_net_d.set_grad()\n        self.loss_net_d.add_flags(defer_inline=True)\n\n        self.loss_net_g = loss_netg\n        self.loss_net_g.set_grad()\n        self.loss_net_g.add_flags(defer_inline=True)\n\n        self.weights_g = optimizerg.parameters\n        self.optimizerg = optimizerg\n        self.weights_d = optimizerd.parameters\n        self.optimizerd = optimizerd\n\n        self.grad = ops.GradOperation(get_by_list=True, sens_param=True)\n        self.sens = sens\n\n        # 并行处理\n        self.reducer_flag = False\n        self.grad_reducer_g = opsf.identity\n        self.grad_reducer_d = opsf.identity\n        self.parallel_mode = _get_parallel_mode()\n        if self.parallel_mode in (ParallelMode.DATA_PARALLEL, ParallelMode.HYBRID_PARALLEL):\n            self.reducer_flag = True\n        if self.reducer_flag:\n            mean = _get_gradients_mean()\n            degree = _get_device_num()\n            self.grad_reducer_g = DistributedGradReducer(self.weights_g, mean, degree)\n            self.grad_reducer_d = DistributedGradReducer(self.weights_d, mean, degree)\n\n    def set_sens(self, value):\n        self.sens = value\n\n    def construct(self, reala, realb):\n        d_loss = self.loss_net_d(reala, realb)\n        g_loss = self.loss_net_g(reala, realb)\n        d_sens = ops.Fill()(ops.DType()(d_loss), ops.Shape()(d_loss), self.sens)\n        d_grads = self.grad(self.loss_net_d, self.weights_d)(reala, realb, d_sens)\n        d_res = ops.depend(d_loss, self.optimizerd(d_grads))\n        g_sens = ops.Fill()(ops.DType()(g_loss), ops.Shape()(g_loss), self.sens)\n        g_grads = self.grad(self.loss_net_g, self.weights_g)(reala, realb, g_sens)\n        g_res = ops.depend(g_loss, self.optimizerg(g_grads))\n        return d_res, g_res\n",[392],{"type":18,"tag":158,"props":393,"children":394},{"__ignoreMap":7},[395],{"type":24,"value":390},{"type":18,"tag":26,"props":397,"children":398},{},[399],{"type":18,"tag":30,"props":400,"children":401},{},[402],{"type":24,"value":403},"训练",{"type":18,"tag":26,"props":405,"children":406},{},[407],{"type":24,"value":408},"训练分为两个主要部分：训练判别器和训练生成器。训练判别器的目的是最大程度地提高判别图像真伪的概率。训练生成器是希望能产生更好的虚假图像。在这两个部分中，分别获取训练过程中的损失，并在每个周期结束时进行统计。下面进行训练。",{"type":18,"tag":26,"props":410,"children":411},{},[412],{"type":18,"tag":30,"props":413,"children":414},{},[415],{"type":24,"value":416},"训练过程",{"type":18,"tag":153,"props":418,"children":420},{"code":419},"# 导入各模块\nimport os\nimport datetime\n\nimport mindspore.nn as nn\nfrom mindspore import context\nfrom mindspore import Tensor\nfrom mindspore.train.serialization import save_checkpoint\n\nfrom src.models.loss import WithLossCellD, LossD, WithLossCellG, LossG, TrainOneStepCell\nfrom src.models.pix2pix import Pix2Pix, get_generator, get_discriminator\nfrom src.process_datasets.dataset import Pix2PixDataset, create_train_dataset\nfrom src.utils.tools import get_lr\nfrom src.config.pix2pix_config import parse_args\n\n# 配置信息\narg = parse_args()\ncontext.set_context(mode=context.GRAPH_MODE)\n\n# 预处理数据以进行训练\ndataset = Pix2PixDataset(root_dir=arg.train_data_dir, config=arg)\nds = create_train_dataset(dataset, batch_size=arg.batch_size)\nsteps_per_epoch = ds.get_dataset_size()\nprint(\"ds:\", ds.get_dataset_size())\nprint(\"ds:\", ds.get_col_names())\nprint(\"ds.shape:\", ds.output_shapes())\n\n# 网络\nnet_generator = get_generator(arg)\nnet_discriminator = get_discriminator(arg)\npix2pix = Pix2Pix(generator=net_generator, discriminator=net_discriminator)\n\n# 损失\nd_loss_fn = LossD(arg)\ng_loss_fn = LossG(arg)\nd_loss_net = WithLossCellD(backbone=pix2pix, loss_fn=d_loss_fn)\ng_loss_net = WithLossCellG(backbone=pix2pix, loss_fn=g_loss_fn)\n\n# 优化器\nd_opt = nn.Adam(pix2pix.net_discriminator.trainable_params(), learning_rate=get_lr(arg),\n                beta1=arg.beta1, beta2=arg.beta2, loss_scale=1)\ng_opt = nn.Adam(pix2pix.net_generator.trainable_params(), learning_rate=get_lr(arg),\n                beta1=arg.beta1, beta2=arg.beta2, loss_scale=1)\n\n# 训练网络\ntrain_net = TrainOneStepCell(loss_netd=d_loss_net, loss_netg=g_loss_net, optimizerd=d_opt, optimizerg=g_opt, sens=1)\ntrain_net.set_train()\n\n# 迭代训练\nif not os.path.isdir(arg.ckpt_dir):\n    os.makedirs(arg.ckpt_dir)\ng_losses = []\nd_losses = []\ndata_loader = ds.create_dict_iterator(output_numpy=True, num_epochs=arg.epoch_num)\nfor epoch in range(arg.epoch_num):\n    for i, data in enumerate(data_loader):\n        start_time = datetime.datetime.now()\n        input_image = Tensor(data[\"input_images\"])\n        target_image = Tensor(data[\"target_images\"])\n        dis_loss, gen_loss = train_net(input_image, target_image)\n        end_time = datetime.datetime.now()\n        delta = (end_time - start_time).microseconds\n        if i % 100 == 0:\n            print(\"ms per step :\", delta / 1000, \"epoch: \",\n                  epoch + 1, \"/\", arg.epoch_num, \"step: \",\n                  i, \"/\", steps_per_epoch, \"Dloss: \", dis_loss, \"Gloss: \", gen_loss)\n\n        d_losses.append(dis_loss.asnumpy())\n        g_losses.append(gen_loss.asnumpy())\n    if (epoch + 1) == arg.epoch_num:\n        save_checkpoint(net_generator, os.path.join(arg.ckpt_dir, \"Generator_200.ckpt\"))\n",[421],{"type":18,"tag":158,"props":422,"children":423},{"__ignoreMap":7},[424],{"type":24,"value":419},{"type":18,"tag":26,"props":426,"children":427},{},[428],{"type":18,"tag":91,"props":429,"children":431},{"alt":106,"src":430},"https://fileserver.developer.huaweicloud.com/FileServer/getFile/cmtybbs/e64/154/b38/90a1d5d431e64154b387b3660e356ff5.20230216090931.08235607332096566313793648204626:50540216011332:2400:7659370EECF6B0E50D6276481B8691D9B863155B34ADE4E3CDE1298353861666.png",[],{"type":18,"tag":26,"props":433,"children":434},{},[435],{"type":24,"value":436},"循环训练网络，每次迭代，就收集生成器和判别器的损失，以便于后面绘制训练过程中损失函数的图像。",{"type":18,"tag":139,"props":438,"children":440},{"id":439},"结果展示",[441],{"type":18,"tag":30,"props":442,"children":443},{},[444],{"type":24,"value":439},{"type":18,"tag":26,"props":446,"children":447},{},[448],{"type":24,"value":449},"定义描绘损失函数与训练迭代的关系以及生成器生成图片函数：",{"type":18,"tag":153,"props":451,"children":453},{"code":452},"import numpy as np\nfrom PIL import Image as image\nimport matplotlib.pyplot as plt\nfrom mindspore import Tensor\n\n\ndef save_losses(g_losses, f_losses, idx, config):\n    \"\"\"绘制迭代的损失信息并保存\"\"\"\n\n    plt.figure(figsize=(10, 5))\n    plt.title(\"Generator and Discriminator Loss During Training\")\n    plt.plot(g_losses, label=\"G\")\n    plt.plot(f_losses, label=\"D\")\n    plt.xlabel(\"iterations\")\n    plt.ylabel(\"Losses\")\n    plt.legend()\n    plt.savefig(config.loss_show_dir + \"/{}.png\".format(idx))\n\n\ndef save_image(img, img_path):\n    \"\"\"图像保存\"\"\"\n\n    if isinstance(img, Tensor):\n        img = decode_image(img)\n    elif not isinstance(img, np.ndarray):\n        raise ValueError(\"img should be Tensor or numpy array, but get {}\".format(type(img)))\n\n    img_pil = image.fromarray(img)\n    img_pil.save(img_path + \".jpg\")\n",[454],{"type":18,"tag":158,"props":455,"children":456},{"__ignoreMap":7},[457],{"type":24,"value":452},{"type":18,"tag":26,"props":459,"children":460},{},[461],{"type":24,"value":462},"训练时同时调用函数以保存产生图片至指定文件，如：",{"type":18,"tag":26,"props":464,"children":465},{},[466],{"type":24,"value":467},"save_image(fake_image, config.train_fakeimg_dir + str(epoch + 1)) save_losses(g_losses, d_losses, epoch + 1)",{"type":18,"tag":26,"props":469,"children":470},{},[471],{"type":18,"tag":91,"props":472,"children":474},{"alt":106,"src":473},"https://fileserver.developer.huaweicloud.com/FileServer/getFile/cmtybbs/e64/154/b38/90a1d5d431e64154b387b3660e356ff5.20230216091051.91969938297245151631958929302734:50540216011332:2400:C0D1299D0A38B26975234F64162CD0860FC1D123E64711F8CB98AA0B116AA0BB.png",[],{"type":18,"tag":26,"props":476,"children":477},{},[478],{"type":24,"value":479},"展示随着迭代轮次增大，各数据集不同轮次的推理图片效果如下。",{"type":18,"tag":26,"props":481,"children":482},{},[483],{"type":18,"tag":91,"props":484,"children":486},{"alt":106,"src":485},"https://fileserver.developer.huaweicloud.com/FileServer/getFile/cmtybbs/e64/154/b38/90a1d5d431e64154b387b3660e356ff5.20230216091132.48954645726387658883417703523126:50540216011332:2400:88F26D80668C649FA3E6330369152C0FA05055B6DCA4342A0AA352D38C7D4E04.png",[],{"type":18,"tag":26,"props":488,"children":489},{},[490],{"type":24,"value":491},"从上面的图像可以看出，随着训练次数的增多，图像质量也越来越好。如当num_epochs达到论文参考值时，生成的转化图片较为合理，下面我们通过加载生成器网络模型参数文件ckpt来生成图像，代码如下。",{"type":18,"tag":153,"props":493,"children":495},{"code":494},"from mindspore.train.serialization import load_checkpoint\nfrom mindspore.train.serialization import load_param_into_net\n\nfrom src.process_datasets.dataset import create_val_dataset, Pix2PixDatasetVal\nfrom src.config.pix2pix_config import parse_args\n\nconfig = parse_args()\ndataset_val = Pix2PixDatasetVal(root_dir=config.val_data_dir)\nds_val = create_val_dataset(dataset_val, config.val_pic_size)\n\nckpt_url = config.ckpt\nparam_g = load_checkpoint(ckpt_url)\nload_param_into_net(net_generator, param_g)\ndata_loader_val = ds_val.create_dict_iterator(output_numpy=True, num_epochs=config.epoch_num)\nfor i, data in enumerate(data_loader_val):\n    input_image = Tensor(data[\"input_images\"])\n    fake_image = net_generator(input_image)\n",[496],{"type":18,"tag":158,"props":497,"children":498},{"__ignoreMap":7},[499],{"type":24,"value":494},{"type":18,"tag":26,"props":501,"children":502},{},[503],{"type":24,"value":504},"各数据集分别推理的效果如下",{"type":18,"tag":26,"props":506,"children":507},{},[508],{"type":18,"tag":91,"props":509,"children":511},{"alt":106,"src":510},"https://fileserver.developer.huaweicloud.com/FileServer/getFile/cmtybbs/e64/154/b38/90a1d5d431e64154b387b3660e356ff5.20230216091210.66453225649501456654025530810990:50540216011332:2400:CFD8C6DA819D820E4C708D13F86BDAA8857B7817FF1132D26C5B42BB10A36AEF.png",[],{"type":18,"tag":268,"props":513,"children":515},{"id":514},"引用",[516],{"type":18,"tag":30,"props":517,"children":518},{},[519],{"type":24,"value":514},{"type":18,"tag":26,"props":521,"children":522},{},[523],{"type":24,"value":524},"[1] Phillip Isola,Jun-Yan Zhu,Tinghui Zhou,Alexei A. Efros. Image-to-Image Translation with Conditional Adversarial Networks.[J]. CoRR,2016,abs/1611.07004.",{"type":18,"tag":26,"props":526,"children":527},{},[528,530],{"type":24,"value":529},"更多昇思MindSpore应用案例请访问官网开发者案例：",{"type":18,"tag":531,"props":532,"children":536},"a",{"href":533,"rel":534},"https://www.mindspore.cn/resources/cases",[535],"nofollow",[537],{"type":24,"value":533},{"type":18,"tag":26,"props":539,"children":540},{},[541],{"type":18,"tag":91,"props":542,"children":544},{"alt":7,"src":543},"https://obs-mindspore-file.obs.cn-north-4.myhuaweicloud.com/file/2023/02/17/998227d725fa46b39544393275b9cb3c.png",[],{"title":7,"searchDepth":546,"depth":546,"links":547},4,[548,550,551,552,562],{"id":141,"depth":549,"text":141},3,{"id":164,"depth":549,"text":164},{"id":184,"depth":549,"text":184},{"id":270,"depth":553,"text":276,"children":554},2,[555,556,557,558,559,560,561],{"id":293,"depth":546,"text":299},{"id":310,"depth":546,"text":316},{"id":332,"depth":549,"text":338},{"id":354,"depth":549,"text":360},{"id":376,"depth":549,"text":376},{"id":384,"depth":549,"text":387},{"id":439,"depth":549,"text":439},{"id":514,"depth":553,"text":514},"markdown","content:technology-blogs:zh:2110.md","content","technology-blogs/zh/2110.md","technology-blogs/zh/2110","md",1776506120285]