如何利用CycleGAN实现男女性别转换

介绍

CycleGAN网络具有很强大的风格迁移功能。能够实现非常深层次的风格转换。比如男性图片女性化或者女性图片男性化。

先上效果图:



下面简单谈一谈实现原理。

网络结构

在这里插入图片描述

网络结构如图所示,通过两个循环使用的生成器来进行风格迁移。由此实现了非常神奇的效果。

下面结合代码来详细解释一下网络结构。训练生成对抗网络的深度学习框架为Pytorch

1. 残差模块定义

class ResidualBlock(nn.Module):
    def __init__(self, in_features):
        super(ResidualBlock, self).__init__()

        # 残差模块不改变shape
        conv_block = [  nn.ReflectionPad2d(1),  # 构建残差模块的时候使用映射填充的形式
                        nn.Conv2d(in_features, in_features, 3),
                        nn.InstanceNorm2d(in_features),     # 不使用BatchNorm而是使用InstanceNorm
                        nn.ReLU(inplace=True),
                        nn.ReflectionPad2d(1),
                        nn.Conv2d(in_features, in_features, 3),
                        nn.InstanceNorm2d(in_features)  ]

        self.conv_block = nn.Sequential(*conv_block)

    def forward(self, x):
        return x + self.conv_block(x)

残差模块的定义没有太多需要说明的地方,就是有一点需要注意的是。我们在风格迁移中,不再使用BatchNorm而是使用InstanceNorm。



BN是将每一个batch的每一个通道的每一组图片求mean和var, IN是将单独一个图片的一个通道的数据求mean和var。 区别就是一个是对batch求,一个是对一个图片求。风格迁移中,为了保证风格,通常都对每一个图片单独处理。 CycleGAN网络中,每一个batch只有一张 图片,所以使用InstanceNorm。

2. 定义生成器

class Generator(nn.Module):
    def __init__(self, input_nc, output_nc, n_residual_blocks=9):
        """
        定义生成网络
        参数:
            input_nc                    --输入通道数
            output_nc                   --输出通道数
            n_residual_blocks           --残差模块数量
        """
        super(Generator, self).__init__()

        # 初始化卷积模块
        # 因为使用ReflectionPad扩充
        # 所以输入是3*256*256
        # 输出是64*256*256
        model = [   nn.ReflectionPad2d(3),
                    nn.Conv2d(input_nc, 64, 7),
                    nn.InstanceNorm2d(64),
                    nn.ReLU(inplace=True) ]

        # 进行下采样
        # 第一个range:输入是64*256*256,输出是128*128*128
        # 第二个range:输入是128*128*128,输出是256*64*64

        in_features = 64
        out_features = in_features*2
        for _ in range(2):
            model += [  nn.Conv2d(in_features, out_features, 3, stride=2, padding=1),
                        nn.InstanceNorm2d(out_features),
                        nn.ReLU(inplace=True) ]
            in_features = out_features
            out_features = in_features*2

        # 使用残差模块
        # 输入输出都是256*64*64
        for _ in range(n_residual_blocks): # 默认添加9个残差模块
            model += [ResidualBlock(in_features)]

        # 进行上采样
        # 第一个range:输入是256*64*64,输出是128*128*128
        # 第二个range:输入是128*128*128,输出是64*256*256       
        out_features = in_features//2
        for _ in range(2):
            model += [  nn.ConvTranspose2d(in_features, out_features, 3, stride=2, padding=1, output_padding=1),
                        nn.InstanceNorm2d(out_features),
                        nn.ReLU(inplace=True) ]
            in_features = out_features
            out_features = in_features//2

        # 最后输出层
        # 输入是64*256*256
        # 输出是3*256*256
        model += [  nn.ReflectionPad2d(3),
                    nn.Conv2d(64, output_nc, 7),
                    nn.Tanh() ]

        self.model = nn.Sequential(*model)

    def forward(self, x):
        return self.model(x)

生成器的结构就是最初那幅图中的右侧的样子。进行下采样之后接一个残差模块,再之后进行上采样。生成器期望可以学到比较复杂的特征构造方法,所以网络结构更深,更复杂。判别器结构相对来说要简单很多。

3. 判别器

class Discriminator(nn.Module):
    def __init__(self, input_nc):
        super(Discriminator, self).__init__()

        # 构建卷积分类器
        # 输入为3*256*256
        # 输出为64*128*128
        model = [   nn.Conv2d(input_nc, 64, 4, stride=2, padding=1),
                    nn.LeakyReLU(0.2, inplace=True) ]

        # 输入为64*128*128
        # 输出为128*64*64
        model += [  nn.Conv2d(64, 128, 4, stride=2, padding=1),
                    nn.InstanceNorm2d(128), 
                    nn.LeakyReLU(0.2, inplace=True) ]
 
        # 输入为128*64*64
        # 输出为256*32*32
        model += [  nn.Conv2d(128, 256, 4, stride=2, padding=1),
                    nn.InstanceNorm2d(256), 
                    nn.LeakyReLU(0.2, inplace=True) ]

        # 输入为256*32*32
        # 输出为512*31*31
        model += [  nn.Conv2d(256, 512, 4, padding=1),
                    nn.InstanceNorm2d(512), 
                    nn.LeakyReLU(0.2, inplace=True) ]

        # 全卷积分类层
        # 输入为输出为512*31*31
        # 输出为1*30*30
        model += [nn.Conv2d(512, 1, 4, padding=1)]

        self.model = nn.Sequential(*model)

    def forward(self, x):
        x =  self.model(x)
        # 使用平均池化的办法输出预测值
        # avg_pool2d(input,kernel_size),这里kernel_size为30
        return F.avg_pool2d(x, x.size()[2:]).view(x.size()[0], -1)

就是一个比较普通的分类网络。通过步长为2来逐步缩小尺寸。可能值得注意的是,相比于传统的分类神经网络。我们这里使用全局平均池化的方式进行最终输出预测。没有使用全连接层,减小了网络尺寸。

此外,我还做了一个exe交互程序。可以直接运行,实现图片中头像识别和对应性别转换。可以体验一下生成对抗网络的趣味。

对网络感兴趣,以及想要详细了解原理是具体如何用代码实现,或者想用有趣数据集做出创意应用的功能的话,可以参考这个视频课程:点击链接

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容