CycleGAN及非监督条件图像生成技术简介

Posted 康x呀

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CycleGAN及非监督条件图像生成技术简介相关的知识,希望对你有一定的参考价值。

图像风格迁移技术

图像风格迁移:输入正常图像,输出风格化后的图像。
原始图像作为输入到深度学习模型生成期望的风格图像,在深度学习模型中已经学习了期望风格的特征等。
对于生成图像需要思考两个问题:
1.如何保证生成图像内容与原图一致?
2.如何保证生成图像风格与目标图一致?

逐层图像特征提取

保证内容一致性


将原图传入一个没有训练的神经网络之中,生成出一张带有原图内同的图像,那么如何保证内容的一致性呢?于是将生成的图片放入已经训练好的神经网络中进行传播,再将原始的图像放入神经网络中,对每一层都进行一个比较损失(感知损失)按照相关公式进行距离计算。

保证风格一致性


风格损失一致性:
将生成的图像与目标风格图分别传入已训练号的卷积网络中,然后对卷积网络中各层的特征图进行“风格损失”的欧几里得距离计算。

惩罚因子

总的损失函数如下:

如何调整图像画风?

目标:最小化总损失函数

非监督条件图像生成

方法一:直接转换

对于生成器生成Y领域的图片,然后将真实的Y领域的图片跟伪造的图片送入判别器去判别生成图片是不是Y领域的真实图片。
存在的问题:
Y领域例如梵高的画作本身就很稀少,训练它的话,判别器很好的去识别,但是对于生成器直接可能就记住了特征,因此可以顺利的欺骗过判别器,这样生成器就几乎没有任何学习。
解决办法
循环一致性:

上述生成器生成X-Y领域的图片后,然后再将Y领域的图片经过生成器重构到X领域的图片,这部分就是我们可作是我们所熟知的自动编码器。则这样就可以保证生成器记住的不只是梵高的画作,而是要生成出根据输入生成相应的图像。因此相应的提出了Cycle网络。
方法二:先映射到公共空间

这种的方法内容风格变化大,但是语义特征保留。

CycleGAN框架理解


对于这样一个循环结构,上面的循环是两个生成器生成的图片尽可能越接近越好,另外一个循环就是对于梵高画作,使用一个生成器生成一个普通的真实的图片,然后再由一个生成器生成回Y的这样一个过程,两者也是越接近越好,同样也可以将其送给判别器去判别是否像真实的x或者y,综上,此网络结构就是两组对抗网络组合在一起的网络结构。

StarGAN框架理解

多个领域互转:

StarGAN完成的任务就是对于不同领域互转,使用一个生成器去完成n个领域完成的任务。
网络结构:

判别器多了一项任务就是除了判别数据是否真实之外,还在判断数据属于哪一个领域(分类任务)。生成器的输入也分为两部分,输入数据和生成目标域的编码,两者合在一起送给生成器生成伪造的数据,然后放入生成器种进行重构,然后与原数据进行比对,越接近你越好。

StarGAN应用实例


上图对具体的标签进行编码从而生成不不同风格的图片,下图以表情标签进行编码进行学习从而可生成不同的表情的人脸。

公共映射空间

对于第二种方法我们提出公共映射空间这个概念

将数据映射到公共空间然后再解码到另外一个域的图像,核心就是去除图像内容风格,但是语义特征保留
语义特征:深度学习网络的后几层,为更抽象高级的特征

映射训练

那么如何进行训练呢?
训练自动编码器:

对于输入的人脸通过编码器映射到脸部特征这样一个公共映射空间,然后通过解码器重构成一个真实的图片,然后最小化重构损失进行一个反向传播进而训练编码器和解码器,对于输入的二次元图像同理。
训练判别器:

判别器去帮助解码器构造出更真实的图像,所以需要两个判别器分别去判断重构的图像是否真实。
但是存在一个问题就是:
图片相同的特征可能没有映射到隐空间的同一位置
举例说明:
“1010"在X种表示"黑眼睛”,但是"1010"“红头发”
因此解决上述问题提出一个叫共享编码器与解码器

共享编码器与解码器


对于编码和解码对应的两个神经网络,所谓共享就是强制几组参数相同或接近。

加入领域判别器

我们可以在隐变量层接入一个领域判别器,它是用来判断数据来自哪个编码器,当然这个网络也是一个对抗网络,编码器X和编码器Y需要欺骗领域判别器。对抗的结果就是编码器X与编码器Y输出相同的分布,即:同样的编码表示同样的特征.
具体图解表示方法如下:

ComboGAN

ComboGAN:核心就是加入循环一致性

XGAN

XGAN:加入语义一致性

对于输入图像经过编码器X生成出一组空间(向量)经过解码器Y然后返回编码器Y中进行一个生成,此时不再需要经过解码器X了,只需要生成另外一组的向量,这与前面的向量应该尽可能的相似。

非监督条件图像生成技术

1.融合自动编码器和对坑网络
2.使用训练后的网络中间层度量内容一致性与纹理一致性
3.循环一致性:a生成b,b重构a‘,a’生成b‘,b’重构a‘
4.语义一般指的是深度学习的最后几层的一些特征

复现CycleGAN论文

人脸——动漫脸转换

代码编写

初始化CycleGAN对象

// An highlighted block
    def __init__(self, img_h=64, img_w=64, lr=0.0004):
        """
        初始化CycleGAN对象
        :param img_h: 图像高度
        :param img_w: 图像宽度
        :param lr: 学习率
        """
        self.img_h = img_h
        self.img_w = img_w
        self.lr = lr
        self.d_dim = 1
        self.isTrain = tf.placeholder(dtype=tf.bool)
        self.img_c = 3
        # A--->B--->A'
        self.input_A = tf.placeholder(dtype=tf.float32, shape=[None, self.img_w, self.img_h, self.img_c])
        self.fake_B = self._init_generator(input=self.input_A, scope_name="generatorA2B", isTrain=self.isTrain)
        self.fake_rec_A = self._init_generator(input=self.fake_B, scope_name="generatorB2A", isTrain=self.isTrain)
        # B--->A---->B'
        self.input_B = tf.placeholder(dtype=tf.float32, shape=[None, self.img_w, self.img_h, self.img_c])
        self.fake_A = self._init_generator(input=self.input_B, scope_name="generatorB2A", isTrain=self.isTrain,
                                           reuse=True)
        self.fake_rec_B = self._init_generator(input=self.fake_A, scope_name="generatorA2B", isTrain=self.isTrain,
                                               reuse=True)
        # 判断真假A
        self.dis_fake_A = self._init_discriminator(input=self.fake_A, scope_name="discriminatorA", isTrain=self.isTrain)
        self.dis_real_A = self._init_discriminator(input=self.input_A, scope_name="discriminatorA",
                                                   isTrain=self.isTrain, reuse=True)
        # 判断真假B
        self.dis_fake_B = self._init_discriminator(input=self.fake_B, scope_name="discriminatorB", isTrain=self.isTrain)
        self.dis_real_B = self._init_discriminator(input=self.input_B, scope_name="discriminatorB",
                                                   isTrain=self.isTrain, reuse=True)
        # 初始化伪造数据A,B判断
        self.input_fake_A = tf.placeholder(dtype=tf.float32, shape=[None, self.img_w, self.img_h, self.img_c],
                                           name="fake_A_data")
        self.input_fake_B = tf.placeholder(dtype=tf.float32, shape=[None, self.img_w, self.img_h, self.img_c],
                                           name="fake_B_data")
        self.dis_fake_A_input = self._init_discriminator(self.input_fake_A, scope_name="discriminatorA",
                                                         isTrain=self.isTrain, reuse=True)
        self.dis_fake_B_input = self._init_discriminator(self.input_fake_B, scope_name="discriminatorB",
                                                         isTrain=self.isTrain, reuse=True)
        self._init_train_methods()

初始化判别器

// An highlighted block
    def _init_discriminator(self, input, scope_name="discriminator", isTrain=True, reuse=False):
        """
        初始化判别器op
        :param input: 输入数据op
        :param scope_name: 判别器变量命名空间
        :param isTrain: 是否处于训练状态
        :param reuse: 是否复用内部参数
        :return: 判断结果
        """
        with tf.variable_scope(scope_name,reuse=reuse):
            # input [none,64,64,3]
            conv1 = tf.layers.conv2d(input,32,[4,4],strides=(2,2),padding="same") #[none,32,32,32]
            bn1 = tf.layers.batch_normalization(conv1,training=isTrain)
            active1 = tf.nn.leaky_relu(bn1) #[none,32,32,32]
            #layer 2
            conv2 = tf.layers.conv2d(active1,64,[4,4],strides=(2,2),padding="same") #[none,16,16,64]
            bn2 = tf.layers.batch_normalization(conv2,training=isTrain)
            active2 = tf.nn.leaky_relu(bn2) #[none,16,16,64]
            # layer 3
            conv3 = tf.layers.conv2d(active2,128,[4,4],strides=(2,2),padding='same') #[none,8,8,128]
            bn3 = tf.layers.batch_normalization(conv3,training=isTrain)
            active3 = tf.nn.leaky_relu(bn3) #[none,8,8,128]
            #layer 4
            conv4 = tf.layers.conv2d(active3,256,[4,4],strides=(2,2),padding="same") #[none,4,4,256]
            bn4 = tf.layers.batch_normalization(conv4,training=isTrain)
            active4 = tf.nn.leaky_relu(bn4) #[none,4,4,256]
            # out layer
            out_logis = tf.layers.conv2d(active4,1,[4,4],strides=(1,1),padding="valid") #[none,1,1,1]
        return out_logis

初始化生成器

// An highlighted block
    def _init_generator(self, input, scope_name='generator', isTrain=True, reuse=False):
        """
        初始化生成器op
        :param input: 输入数据op
        :param scope_name: 生成器变量命名空间
        :param isTrain: 是否处于训练状态
        :param reuse: 是否复用内部参数
        :return: 生成数据op
        """
        with tf.variable_scope(scope_name,reuse=reuse):
            #input[none,64,64,3]
            conv1 = tf.layers.conv2d(input,64,[4,4],strides=(2,2),padding="same")# [none,32,32,64]
            bn1 = tf.layers.batch_normalization(conv1,training=isTrain)
            active1 = tf.nn.leaky_relu(bn1) #[none,32,32,64]
            #layer 2
            conv2 = tf.layers.conv2d(active1,128,[4,4],strides=(2,2),padding="same") #[none,16,16,128]
            bn2 = tf.layers.batch_normalization(conv2,training=isTrain)
            active2 = tf.nn.leaky_relu(bn2) #[none,16,15,128]
            # layer3
            conv3 = tf.layers.conv2d(active2,256,[4,4],strides=(2,2),padding="same") #[none,8,8,256]
            bn3 = tf.layers.batch_normalization(conv3,training=isTrain)
            active3 = tf.nn.leaky_relu(bn3) #[none,8,8,256]
            # deconv layer 1
            de_conv1 = tf.layers.conv2d_transpose(active3,128,[4,4],strides=(2,2),padding="same") #[none,16,16,128]
            de_bn1=tf.layers.batch_normalization(de_conv1,training=isTrain)
            de_active1 = tf.nn.leaky_relu(de_bn1) #[none,16,16,128]
            # deconv layer 2
            de_conv2 = tf.layers.conv2d_transpose(de_active1,64,[4,4],strides=(2,2),padding="same") #[none,32,32,64]
            de_bn2 = tf.layers.batch_normalization(de_conv2,training=isTrain)
            de_active2 = tf.nn.leaky_relu(de_bn2) #[none,32,32,64]
            # deconv layer 3
            de_conv3 = tf.layers.conv2d_transpose(de_active2,3,[4,4],strides=(2,2),padding="same") #[none,64,64,3]
            out = tf.nn.tanh(de_conv3)
        return out;

训练模型

// An highlighted block
    def train(self, data_path_A="data/Train_A/", data_path_B="data/faces/", batch_size=64, itrs=100000, save_time=500):
        """
        训练模型
        :param data_path_A: 数据A路径
        :param data_path_B: 数据B路径
        :param batch_size: 采样数据量
        :param itrs: 迭代训练次数
        :param save_time: 保存周期
        :return: None
        """
        start_time = time.time()
        test_A = dh.read_img2numpy(batch_size=18,img_h=64,img_w=64,path=data_path_A)
        test_B = dh.read_img2numpy(batch_size=18,img_w=64,img_h=64,path=data_path_B)
        for i in range(itrs):
            #读取训练图片
            batch_A = dh.read_img2numpy(batch_size=batch_size,img_h=64,img_w=64,path=data_path_A)
            batch_B = dh.read_img2numpy(batch_size=batch_size,img_w=64,img_h=64,path=data_path_B)
            batch_fake_A,batch_fake_B,g_loss_curr,_=self.sess.run([self.fake_A,self.fake_B,self.g_loss,self.G_trainer],feed_dict={
                self.input_A:batch_A,self.input_B:batch_B,self.isTrain:True
            })
            #训练判别器
            d_loss_curr,_ = self.sess.run([self.d_loss,self.D_trainer],
                                          feed_dict={self.input_A:batch_A,self.input_B:batch_B,self.input_fake_A:batch_fake_A,
                                                     self.input_fake_B:batch_fake_B,self.isTrain:True})
            if i %save_time ==0:
                #生成数据
                self.gen_data(data=test_A,a2b=True,save_path="out/CycleGAN/A2B/",name=str(i).zfill(6)+".png")
                self.gen_data(data=tes

以上是关于CycleGAN及非监督条件图像生成技术简介的主要内容,如果未能解决你的问题,请参考以下文章

[Pytorch系列-64]:生成对抗网络GAN - 图像生成开源项目pytorch-CycleGAN-and-pix2pix : 有监督图像生成pix2pix的基本原理

论文解读 用于弱监督表面缺陷分割的缺陷注意模板循环对抗网络 (Defect attention template generation cycleGAN for weakly supervised)

[Pytorch系列-75]:生成对抗网络GAN - 图像生成开源项目pytorch-CycleGAN-and-pix2pix - CycleGAN网络结构与代码实现详解

对抗生成网络GAN系列——CycleGAN简介及图片春冬变换案例

[Pytorch系列-69]:生成对抗网络GAN - 图像生成开源项目pytorch-CycleGAN-and-pix2pix - test.py代码详解

[Pytorch系列-63]:生成对抗网络GAN - 图像生成开源项目pytorch-CycleGAN-and-pix2pix - 代码总体架构与总体学习思路