求一首歌,英文的,发音是什么什么cycle 英文的

&figure&&img src=&https://pic1.zhimg.com/v2-1ba5bff38b5e9b4b63fdd_b.jpg& data-rawwidth=&1080& data-rawheight=&575& class=&origin_image zh-lightbox-thumb& width=&1080& data-original=&https://pic1.zhimg.com/v2-1ba5bff38b5e9b4b63fdd_r.jpg&&&/figure&&blockquote&本文由 「AI前线」原创,原文链接:&a href=&https://link.zhihu.com/?target=http%3A//t.cn/R8pYF7Q& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&送书 | AI插画师:如何用基于PyTorch的生成对抗网络生成动漫头像?&/a&&br&作者|陈云&br&编辑|Natalie&/blockquote&&p&&b&AI 前线导读:”&/b&2016 年是属于 TensorFlow 的一年,凭借谷歌的大力推广,TensorFlow 占据了各大媒体的头条。2017 年年初,PyTorch 的横空出世吸引了研究人员极大的关注,PyTorch 简洁优雅的设计、统一易用的接口、追风逐电的速度和变化无方的灵活性给人留下深刻的印象。&/p&&p&本文节选自《深度学习框架 PyTorch 入门与实践》第 7 章,为读者讲解当前最火爆的生成对抗网络(GAN),带领读者从零开始实现一个动漫头像生成器,能够利用 GAN 生成风格多变的动漫头像。&b&注意啦,文末有送书福利!”&/b&&/p&&p&&br&&/p&&p&生成对抗网络(Generative Adversarial Net,GAN)是近年来深度学习中一个十分热门的方向,卷积网络之父、深度学习元老级人物 LeCun Yan 就曾说过“GAN is the most interesting idea in the last 10 years in machine learning”。尤其是近两年,GAN 的论文呈现井喷的趋势,GitHub 上有人收集了各种各样的 GAN 变种、应用、研究论文等,其中有名称的多达数百篇。作者还统计了 GAN 论文发表数目随时间变化的趋势,如图 7-1 所示,足见 GAN 的火爆程度。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-f851abe1bbd7a_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&800& data-rawheight=&550& class=&origin_image zh-lightbox-thumb& width=&800& data-original=&https://pic2.zhimg.com/v2-f851abe1bbd7a_r.jpg&&&/figure&&p&图 7-1 GAN 的论文数目逐月累加图&/p&&p&&br&&/p&&p&&b&GAN 的原理简介&/b&&/p&&p&GAN 的开山之作是被称为“GAN 之父”的 Ian Goodfellow 发表于 2014 年的经典论文 Generative Adversarial Networks ,在这篇论文中他提出了生成对抗网络,并设计了第一个 GAN 实验——手写数字生成。&/p&&p&GAN 的产生来自于一个灵机一动的想法:&/p&&blockquote&“What I cannot create, I do not understand.”(那些我所不能创造的,我也没有真正地理解它。)&br&—Richard Feynman&/blockquote&&p&类似地,如果深度学习不能创造图片,那么它也没有真正地理解图片。当时深度学习已经开始在各类计算机视觉领域中攻城略地,在几乎所有任务中都取得了突破。但是人们一直对神经网络的黑盒模型表示质疑,于是越来越多的人从可视化的角度探索卷积网络所学习的特征和特征间的组合,而 GAN 则从生成学习角度展示了神经网络的强大能力。GAN 解决了非监督学习中的著名问题:&b&给定一批样本,训练一个系统能够生成类似的新样本&/b&。&/p&&p&生成对抗网络的网络结构如图 7-2 所示,主要包含以下两个子网络。&/p&&ul&&li&生成器(generator):输入一个随机噪声,生成一张图片。&/li&&li&判别器(discriminator):判断输入的图片是真图片还是假图片。&/li&&/ul&&figure&&img src=&https://pic2.zhimg.com/v2-5ace20f7fe7fa8655949_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1080& data-rawheight=&953& class=&origin_image zh-lightbox-thumb& width=&1080& data-original=&https://pic2.zhimg.com/v2-5ace20f7fe7fa8655949_r.jpg&&&/figure&&p&图 7-2 生成对抗网络结构图&/p&&p&训练判别器时,需要利用生成器生成的假图片和来自真实世界的真图片;训练生成器时,只用噪声生成假图片。判别器用来评估生成的假图片的质量,促使生成器相应地调整参数。&/p&&p&生成器的目标是尽可能地生成以假乱真的图片,让判别器以为这是真的图片;判别器的目标是将生成器生成的图片和真实世界的图片区分开。可以看出这二者的目标相反,在训练过程中互相对抗,这也是它被称为生成对抗网络的原因。&/p&&p&上面的描述可能有点抽象,让我们用收藏齐白石作品(齐白石作品如图 7-3 所示)的书画收藏家和假画贩子的例子来说明。假画贩子相当于是生成器,他们希望能够模仿大师真迹伪造出以假乱真的假画,骗过收藏家,从而卖出高价;书画收藏家则希望将赝品和真迹区分开,让真迹流传于世,销毁赝品。这里假画贩子和收藏家所交易的画,主要是齐白石画的虾。齐白石画虾可以说是画坛一绝,历来为世人所追捧。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-d8a7edc6e05c8b_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1080& data-rawheight=&1160& class=&origin_image zh-lightbox-thumb& width=&1080& data-original=&https://pic4.zhimg.com/v2-d8a7edc6e05c8b_r.jpg&&&/figure&&p&图 7-3 齐白石画虾图真迹&/p&&p&在这个例子中,一开始假画贩子和书画收藏家都是新手,他们对真迹和赝品的概念都很模糊。假画贩子仿造出来的假画几乎都是随机涂鸦,而书画收藏家的鉴定能力很差,有不少赝品被他当成真迹,也有许多真迹被当成赝品。&/p&&p&首先,书画收藏家收集了一大堆市面上的赝品和齐白石大师的真迹,仔细研究对比,初步学习了画中虾的结构,明白画中的生物形状弯曲,并且有一对类似钳子的“螯足”,对于不符合这个条件的假画全部过滤掉。当收藏家用这个标准到市场上进行鉴定时,假画基本无法骗过收藏家,假画贩子损失惨重。但是假画贩子自己仿造的赝品中,还是有一些蒙骗过关,这些蒙骗过关的赝品中都有弯曲的形状,并且有一对类似钳子的“螯足”。于是假画贩子开始修改仿造的手法,在仿造的作品中加入弯曲的形状和一对类似钳子的“螯足”。除了这些特点,其他地方例如颜色、线条都是随机画的。假画贩子制造出的第一版赝品如图 7-4 所示。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-54f8ad20e12c24da0a64_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&492& data-rawheight=&346& class=&origin_image zh-lightbox-thumb& width=&492& data-original=&https://pic1.zhimg.com/v2-54f8ad20e12c24da0a64_r.jpg&&&/figure&&p&图 7-4 假画贩子制造的第一版赝品&/p&&p&当假画贩子把这些画拿到市面上去卖时,很容易就骗过了收藏家,因为画中有一只弯曲的生物,生物前面有一对类似钳子的东西,符合收藏家认定的真迹的标准,所以收藏家就把它当成真迹买回来。随着时间的推移,收藏家买回越来越多的假画,损失惨重,于是他又闭门研究赝品和真迹之间的区别,经过反复比较对比,他发现齐白石画虾的真迹中除了有弯曲的形状,虾的触须蔓长,通身作半透明状,并且画的虾的细节十分丰富,虾的每一节之间均呈白色状。&/p&&p&收藏家学成之后,重新出山,而假画贩子的仿造技法没有提升,所制造出来的赝品被收藏家轻松识破。于是假画贩子也开始尝试不同的画虾手法,大多都是徒劳无功,不过在众多尝试之中,还是有一些赝品骗过了收藏家的眼睛。假画贩子发现这些仿制的赝品触须蔓长,通身作半透明状,并且画的虾的细节十分丰富,如图 7-5 所示。于是假画贩子开始大量仿造这种画,并拿到市面上销售,许多都成功地骗过了收藏家。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-cca7cd6bfa2f7_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&917& data-rawheight=&737& class=&origin_image zh-lightbox-thumb& width=&917& data-original=&https://pic3.zhimg.com/v2-cca7cd6bfa2f7_r.jpg&&&/figure&&p&图 7-5 假画贩子制造的第二版赝品&/p&&p&收藏家再度损失惨重,被迫关门研究齐白石的真迹和赝品之间的区别,学习齐白石真迹的特点,提升自己的鉴定能力。就这样,通过收藏家和假画贩子之间的博弈,收藏家从零开始慢慢提升了自己对真迹和赝品的鉴别能力,而假画贩子也不断地提高自己仿造齐白石真迹的水平。收藏家利用假画贩子提供的赝品,作为和真迹的对比,对齐白石画虾真迹有了更好的鉴赏能力;而假画贩子也不断尝试,提升仿造水平,提升仿造假画的质量,即使最后制造出来的仍属于赝品,但是和真迹相比也很接近了。收藏家和假画贩子二者之间互相博弈对抗,同时又不断促使着对方学习进步,达到共同提升的目的。&/p&&p&在这个例子中,假画贩子相当于一个生成器,收藏家相当于一个判别器。一开始生成器和判别器的水平都很差,因为二者都是随机初始化的。训练过程分为两步交替进行,第一步是训练判别器(只修改判别器的参数,固定生成器),目标是把真迹和赝品区分开;第二步是训练生成器(只修改生成器的参数,固定判别器),为的是生成的假画能够被判别器判别为真迹(被收藏家认为是真迹)。这两步交替进行,进而分类器和判别器都达到了一个很高的水平。训练到最后,生成器生成的虾的图片(如图 7-6 所示)和齐白石的真迹几乎没有差别。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-246d6e4ab9d16d74e37f5aab_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&895& data-rawheight=&599& class=&origin_image zh-lightbox-thumb& width=&895& data-original=&https://pic4.zhimg.com/v2-246d6e4ab9d16d74e37f5aab_r.jpg&&&/figure&&p&图 7-6 生成器生成的虾&/p&&p&下面我们来思考网络结构的设计。判别器的目标是判断输入的图片是真迹还是赝品,所以可以看成是一个二分类网络,参考第 6 章中 Dog vs. Cat 的实验,我们可以设计一个简单的卷积网络。生成器的目标是从噪声中生成一张彩色图片,这里我们采用广泛使用的 DCGAN(Deep Convolutional Generative Adversarial Networks)结构,即采用全卷积网络,其结构如图 7-7 所示。网络的输入是一个 100 维的噪声,输出是一个 3×64×64 的图片。这里的输入可以看成是一个 100×1×1 的图片,通过上卷积慢慢增大为 4×4、8×8、16×16、32×32 和 64×64。上卷积,或称转置卷积,是一种特殊的卷积操作,类似于卷积操作的逆运算。当卷积的 stride 为 2 时,输出相比输入会下采样到一半的尺寸;而当上卷积的 stride 为 2 时,输出会上采样到输入的两倍尺寸。这种上采样的做法可以理解为图片的信息保存于 100 个向量之中,神经网络根据这 100 个向量描述的信息,前几步的上采样先勾勒出轮廓、色调等基础信息,后几步上采样慢慢完善细节。网络越深,细节越详细。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-cecceb95a2_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&892& data-rawheight=&362& class=&origin_image zh-lightbox-thumb& width=&892& data-original=&https://pic1.zhimg.com/v2-cecceb95a2_r.jpg&&&/figure&&p&图 7-7 DCGAN 中生成器网络结构图&/p&&p&在 DCGAN 中,判别器的结构和生成器对称:生成器中采用上采样的卷积,判别器中就采用下采样的卷积,生成器是根据噪声输出一张 64×64×3 的图片,而判别器则是根据输入的 64×64×3 的图片输出图片属于正负样本的分数(概率)。&/p&&p&&br&&/p&&p&&b&用 GAN 生成动漫头像&/b&&/p&&p&本节将用 GAN 实现一个生成动漫人物头像的例子。在日本的技术博客网站上 有个博主(估计是一位二次元的爱好者),利用 DCGAN 从 20 万张动漫头像中学习,最终能够利用程序自动生成动漫头像,生成的图片效果如图 7-8 所示。源程序是利用 Chainer 框架实现的,本节我们尝试利用 PyTorch 实现。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-aaeea15cfecc813b5762_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&798& data-rawheight=&800& class=&origin_image zh-lightbox-thumb& width=&798& data-original=&https://pic2.zhimg.com/v2-aaeea15cfecc813b5762_r.jpg&&&/figure&&p&图 7-8 DCGAN 生成的动漫头像&/p&&p&原始的图片是从网站中爬取的,并利用 OpenCV 从中截取头像,处理起来比较麻烦。这里我们使用知乎用户何之源爬取并经过处理的 5 万张图片。可以从本书配套程序的 README.MD 的百度网盘链接下载所有的图片压缩包,并解压缩到指定的文件夹中。需要注意的是,这里图片的分辨率是 3×96×96,而不是论文中的 3×64×64,因此需要相应地调整网络结构,使生成图像的尺寸为 96。&/p&&p&我们首先来看本实验的代码结构。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-455c99a2dcc10e9ed7bc0_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&651& data-rawheight=&262& class=&origin_image zh-lightbox-thumb& width=&651& data-original=&https://pic4.zhimg.com/v2-455c99a2dcc10e9ed7bc0_r.jpg&&&/figure&&p&接着来看 model.py 中是如何定义生成器的。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-287e2ec8f784c2ae31efca_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1080& data-rawheight=&962& class=&origin_image zh-lightbox-thumb& width=&1080& data-original=&https://pic2.zhimg.com/v2-287e2ec8f784c2ae31efca_r.jpg&&&/figure&&p&可以看出生成器的搭建相对比较简单,直接使用 nn.Sequential 将上卷积、激活、池化等操作拼接起来即可,这里需要注意上卷积 ConvTransposed2d 的使用。当 kernel size 为 4、stride 为 2、padding 为 1 时,根据公式 &code&H_out=(H_in-1)*stride-2*padding+kernel_size&/code&,输出尺寸刚好变成输入的两倍。最后一层采用 kernel size 为 5、stride 为 3、padding 为 1,是为了将 32×32 上采样到 96×96,这是本例中图片的尺寸,与论文中 64×64 的尺寸不一样。最后一层用 Tanh 将输出图片的像素归一化至 -1~1,如果希望归一化至 0~1,则需使用 Sigmoid。接着我们来看判别器的网络结构。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-e0b1fd748c7c633cc00587_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1080& data-rawheight=&888& class=&origin_image zh-lightbox-thumb& width=&1080& data-original=&https://pic2.zhimg.com/v2-e0b1fd748c7c633cc00587_r.jpg&&&/figure&&p&可以看出判别器和生成器的网络结构几乎是对称的,从卷积核大小到 padding、stride 等设置,几乎一模一样。例如生成器的最后一个卷积层的尺度是(5,3,1),判别器的第一个卷积层的尺度也是(5,3,1)。另外,这里需要注意的是生成器的激活函数用的是 ReLU,而判别器使用的是 LeakyReLU,二者并无本质区别,这里的选择更多是经验总结。每一个样本经过判别器后,输出一个 0~1 的数,表示这个样本是真图片的概率。在开始写训练函数前,先来看看模型的配置参数。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-04af51c872b7_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1080& data-rawheight=&938& class=&origin_image zh-lightbox-thumb& width=&1080& data-original=&https://pic4.zhimg.com/v2-04af51c872b7_r.jpg&&&/figure&&p&这些只是模型的默认参数,还可以利用 Fire 等工具通过命令行传入,覆盖默认值。另外,我们也可以直接使用 opt.attr,还可以利用 IDE/IPython 提供的自动补全功能,十分方便。这里的超参数设置大多是照搬 DCGAN 论文的默认值,作者经过大量实验,发现这些参数能够更快地训练出一个不错的模型。&/p&&p&当我们下载完数据之后,需要将所有图片放在一个文件夹,然后将该文件夹移动至 data 目录下(请确保 data 下没有其他的文件夹)。这种处理方式是为了能够直接使用 torchvision 自带的 ImageFolder 读取图片,而不必自己写 Dataset。数据读取与加载的代码如下:&/p&&figure&&img src=&https://pic3.zhimg.com/v2-ea58de0cb0bd07ae047deb8_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1011& data-rawheight=&441& class=&origin_image zh-lightbox-thumb& width=&1011& data-original=&https://pic3.zhimg.com/v2-ea58de0cb0bd07ae047deb8_r.jpg&&&/figure&&p&可见,用 ImageFolder 配合 DataLoader 加载图片十分方便。&/p&&p&在进行训练之前,我们还需要定义几个变量:模型、优化器、噪声等。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-d1f0b97d314c08bb047cabedee11f512_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1074& data-rawheight=&775& class=&origin_image zh-lightbox-thumb& width=&1074& data-original=&https://pic1.zhimg.com/v2-d1f0b97d314c08bb047cabedee11f512_r.jpg&&&/figure&&p&&br&&/p&&p&在加载预训练模型时,最好指定 map_location。因为如果程序之前在 GPU 上运行,那么模型就会被存成 torch.cuda.Tensor,这样加载时会默认将数据加载至显存。如果运行该程序的计算机中没有 GPU,加载就会报错,故通过指定 map_location 将 Tensor 默认加载入内存中,待有需要时再移至显存中。&/p&&p&下面开始训练网络,训练步骤如下。&/p&&p&(1)训练判别器。&/p&&ul&&li&固定生成器&/li&&li&对于真图片,判别器的输出概率值尽可能接近 1&/li&&li&对于生成器生成的假图片,判别器尽可能输出 0&/li&&/ul&&p&(2)训练生成器。&/p&&ul&&li&固定判别器&/li&&li&生成器生成图片,尽可能让判别器输出 1&/li&&/ul&&p&(3)返回第一步,循环交替训练。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-dbd3e15fbc_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1080& data-rawheight=&939& class=&origin_image zh-lightbox-thumb& width=&1080& data-original=&https://pic3.zhimg.com/v2-dbd3e15fbc_r.jpg&&&/figure&&p&这里需要注意以下几点。&/p&&ul&&li&训练生成器时,无须调整判别器的参数;训练判别器时,无须调整生成器的参数。&/li&&li&在训练判别器时,需要对生成器生成的图片用 detach 操作进行计算图截断,避免反向传播将梯度传到生成器中。因为在训练判别器时我们不需要训练生成器,也就不需要生成器的梯度。&/li&&li&在训练分类器时,需要反向传播两次,一次是希望把真图片判为 1,一次是希望把假图片判为 0。也可以将这两者的数据放到一个 batch 中,进行一次前向传播和一次反向传播即可。但是人们发现,在一个 batch 中只包含真图片或只包含假图片的做法最好。&/li&&li&对于假图片,在训练判别器时,我们希望它输出为 0;而在训练生成器时,我们希望它输出为 1。因此可以看到一对看似矛盾的代码:error_d_fake = criterion(fake_output, fake_labels) 和 error_g = criterion(fake_output, true_labels)。其实这也很好理解,判别器希望能够把假图片判别为 fake_label,而生成器则希望能把它判别为 true_label,判别器和生成器互相对抗提升。&/li&&/ul&&p&接下来就是一些可视化的代码。每次可视化使用的噪声都是固定的 fix_noises,因为这样便于我们比较对于相同的输入,生成器生成的图片是如何一步步提升的。另外,由于我们对输入的图片进行了归一化处理(-1~1),在可视化时则需要将它还原成原来的 scale(0~1) 。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-ca6a5ae187d94ba2c98ffa_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1019& data-rawheight=&88& class=&origin_image zh-lightbox-thumb& width=&1019& data-original=&https://pic4.zhimg.com/v2-ca6a5ae187d94ba2c98ffa_r.jpg&&&/figure&&p&除此之外,还提供了一个函数,能加载预训练好的模型,并利用噪声随机生成图片。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-de270af58aa39cda239bf07afa1e31fe_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1076& data-rawheight=&826& class=&origin_image zh-lightbox-thumb& width=&1076& data-original=&https://pic1.zhimg.com/v2-de270af58aa39cda239bf07afa1e31fe_r.jpg&&&/figure&&p&完整的代码请参考本书的附带样例代码 chapter7/AnimeGAN。参照 README.MD 中的指南配置环境,并准备好数据,而后用如下命令即可开始训练:&/p&&figure&&img src=&https://pic1.zhimg.com/v2-fddf08aee1a1e34551de7a_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&756& data-rawheight=&145& class=&origin_image zh-lightbox-thumb& width=&756& data-original=&https://pic1.zhimg.com/v2-fddf08aee1a1e34551de7a_r.jpg&&&/figure&&p&如果使用 visdom 的话,此时打开 http://[your ip]:8097 就能看到生成的图像。&/p&&p&训练完成后,我们可以利用生成网络随机生成动漫头像,输入命令如下:&/p&&figure&&img src=&https://pic4.zhimg.com/v2-8ef120df302b_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&570& data-rawheight=&116& class=&origin_image zh-lightbox-thumb& width=&570& data-original=&https://pic4.zhimg.com/v2-8ef120df302b_r.jpg&&&/figure&&p&&br&&/p&&p&&b&实验结果分析&/b&&/p&&p&实验结果如图 7-9 所示,分别是训练 1 个、10 个、20 个、30 个、40 个、200 个 epoch 之后神经网络生成的动漫头像。需要注意的是,每次生成器输入的噪声都是一样的,所以我们可以对比在相同的输入下,生成图片的质量是如何慢慢改善的。&/p&&p&刚开始生成的图像比较模糊(1 个 epoch),但是可以看出图像已经有面部轮廓。&/p&&p&继续训练 10 个 epoch 之后,生成的图多了很多细节信息,包括头发、颜色等,但是总体还是很模糊。&/p&&p&训练 20 个 epoch 之后,细节继续完善,包括头发的纹理、眼睛的细节等,但还是有不少涂抹的痕迹。&/p&&p&训练到第 40 个 epoch 时,已经能看出明显的面部轮廓和细节,但还是有涂抹现象,并且有些细节不够合理,例如眼睛一大一小,面部的轮廓扭曲严重。&/p&&p&当训练到 200 个 epoch 之后,图片的细节已经十分完善,线条更流畅,轮廓更清晰,虽然还有一些不合理之处,但是已经有不少图片能够以假乱真了。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-4a94fd4acb5_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&525& data-rawheight=&800& class=&origin_image zh-lightbox-thumb& width=&525& data-original=&https://pic3.zhimg.com/v2-4a94fd4acb5_r.jpg&&&/figure&&p&图 7-9 GAN 生成的动漫头像&/p&&p&类似的生成动漫头像的项目还有“用 DRGAN 生成高清的动漫头像”,效果如图 7-10 所示。但遗憾的是,由于论文中使用的数据涉及版权问题,未能公开。这篇论文的主要改进包括使用了更高质量的图片数据和更深、更复杂的模型。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-36771e8fed48cf565dcbed_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&724& data-rawheight=&729& class=&origin_image zh-lightbox-thumb& width=&724& data-original=&https://pic2.zhimg.com/v2-36771e8fed48cf565dcbed_r.jpg&&&/figure&&p&图 7-10 用 DRGAN 生成的动漫头像&/p&&p&本章讲解的样例程序还可以应用到不同的生成图片场景中,只要将训练图片改成其他类型的图片即可,例如 LSUN 客房图片集、MNIST 手写数据集或 CIFAR10 数据集等。事实上,上述模型还有很大的改进空间。在这里,我们使用的全卷积网络只有四层,模型比较浅,而在 ResNet 的论文发表之后,也有不少研究者尝试在 GAN 的网络结构中引入 Residual Block 结构,并取得了不错的视觉效果。感兴趣的读者可以尝试将示例代码中的单层卷积修改为 Residual Block,相信可以取得不错的效果。&/p&&p&近年来,GAN 的一个重大突破在于理论研究。论文 Towards Principled Methods for Training Generative Adversarial Networks 从理论的角度分析了 GAN 为何难以训练,作者随后在另一篇论文 Wasserstein GAN 中针对性地提出了一个更好的解决方案。但是 Wasserstein GAN 这篇论文在部分技术细节上的实现过于随意,所以随后又有人有针对性地提出 Improved Training of Wasserstein GANs,更好地训练 WGAN。后面两篇论文分别用 PyTorch 和 TensorFlow 实现,代码可以从 GitHub 上搜索到。笔者当初也尝试用 100 行左右的代码实现了 Wasserstein GAN,感兴趣的读者可以去了解 。&/p&&p&随着 GAN 研究的逐渐成熟,人们也尝试把 GAN 用于工业实际问题之中,而在众多相关论文中,最令人印象深刻的就是 Unpaired Image-to-Image Translation using Cycle-Consistent Adversarial Networks ,论文中提出了一种新的 GAN 结构称为 CycleGAN。CycleGAN 利用 GAN 实现风格迁移、黑白图像彩色化,以及马和斑马相互转化等,效果十分出众。论文的作者用 PyTorch 实现了所有代码,并开源在 GitHub 上,感兴趣的读者可以自行查阅。&/p&&p&本章主要介绍 GAN 的基本原理,并带领读者利用 GAN 生成动漫头像。GAN 有许多变种,GitHub 上有许多利用 PyTorch 实现的各种 GAN,感兴趣的读者可以自行查阅。&/p&&p&&b&作者介绍&/b&&/p&&p&&b&陈云&/b&,Python 程序员、Linux 爱好者和 PyTorch 源码贡献者。主要研究方向包括计算机视觉和机器学习。“2017 知乎看山杯机器学习挑战赛”一等奖,“2017 天池医疗 AI 大赛”第八名。热衷于推广 PyTorch,并有丰富的使用经验,活跃于 PyTorch 论坛和知乎相关板块。&/p&&p&&b&福利!福利!我们将给 AI 前线的粉丝送出《深度学习框架 PyTorch 入门与实践》纸质书籍 10 本!在本文下方留言给出你想要这本书的理由,我们会邀请你加入赠书群,本次获奖名单由抽奖小程序随机抽取,2 月 6 日(周二)上午 10 点开奖,获奖者每人获得一本。另附京东购买地址,戳「阅读原文」!&/b&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-88c32fe09d4a_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&591& data-rawheight=&775& class=&origin_image zh-lightbox-thumb& width=&591& data-original=&https://pic3.zhimg.com/v2-88c32fe09d4a_r.jpg&&&/figure&&p&更多干货内容,可关注AI前线,ID:&b&ai-front&/b&,后台回复「&b&AI&/b&」、「&b&TF&/b&」、「&b&大数据&/b&」可获得《AI前线》系列PDF迷你书和技能图谱。&/p&
本文由 「AI前线」原创,原文链接: 作者|陈云 编辑|NatalieAI 前线导读:”2016 年是属于 TensorFlow 的一年,凭借谷歌的大力推广,TensorFlow 占据了各大媒体的头条。2017 年年初,PyTo…
&figure&&img src=&https://pic4.zhimg.com/v2-144eae1eea4_b.jpg& data-rawwidth=&720& data-rawheight=&322& class=&origin_image zh-lightbox-thumb& width=&720& data-original=&https://pic4.zhimg.com/v2-144eae1eea4_r.jpg&&&/figure&&p&最近“中国有嘻哈”特别火&br&&/p&&p&我也来跟风&br&&/p&&p&所以本周的 NLP 教程是&/p&&p&拿循环神经网络(RNN)来生成嘻哈歌词。&/p&&p&我觉得行!&/p&&p&先让我们看看一个简单的深度神经网络模型,&/p&&p&能生成什么样的“嘻哈歌词”。&/p&&blockquote&我的世界&br&能够有&br&看透&br&我的兄弟在我身边&br&每个人&br&或许他们是我的眼&br&我的能够将别约别约我&br&&br&在这种角度&br&我就是你你的帮派&br&他们的声音&br&原本地方叫&br&别约你约我&br&别约我&br&别约我&/blockquote&&p&看看起来还不错哈&/p&&p&那么……&/p&&figure&&img src=&https://pic1.zhimg.com/v2-b59e02e496ab2889c12adb_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&584& data-rawheight=&325& class=&origin_image zh-lightbox-thumb& width=&584& data-original=&https://pic1.zhimg.com/v2-b59e02e496ab2889c12adb_r.jpg&&&/figure&&p&不用买,我们自己做一个!&/p&&p&&b&原理&/b&&/p&&p&简单来说,中文句子中的每个字在统计上是相关的。比如对于“多想你陪我透透气”这句话,如果我们知道第一个字“多”,那下一字有可能是“想”;如果知道前两个字“多想”,那么第三个字是“你”的可能性就大些,以此类推,如果知道“多想你陪我透透”,那么最后一个字很有可能就是“气”字。&/p&&figure&&img src=&https://pic8.zhimg.com/v2-06bff79affc5b125bbce0c_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&640& data-rawheight=&381& data-thumbnail=&https://pic1.zhimg.com/v2-06bff79affc5b125bbce0c_b.jpg& class=&origin_image zh-lightbox-thumb& width=&640& data-original=&https://pic1.zhimg.com/v2-06bff79affc5b125bbce0c_r.gif&&&/figure&&p&&b&清理训练数据:&/b&&/p&&p&首先准备训练模型的数据。&/p&&p&我从网上爬了大约三万四千行嘻哈歌词。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&lrc_lines = open('../data/rapper.txt').readlines()
print(lrc_lines[:20])
['\n', ' 作曲 : Mixtape\n', ' 作词 : 啊之\n', '\n', '录音 MISO MUSIC\n', '混缩 MAI\n', 'MIX BY MAI\n', &hey baby Don't worry\n&, &hey baby Don't worry\n&, &hey baby Don't worry\n&, &hey baby Don't worry\n&, '抱歉我依旧不稳定 DAMN\n', '鱼龙混杂的街头不只靠努力 REAL 别烦恼\n', '多想你陪我透透气 发发牢骚\n', '倾诉着最近不如意 let me let me\n', '这就是生活里 的问题 不必不报忧\n', '做真实的自己 不用比 早晚都能够\n', '我始终担心你 出问题 人心难看透\n', '我怎么都可以 唯有你 所以 所以\n', '我早已日夜颠倒 烟酒成瘾 制作巧克力\n'
&/code&&/pre&&/div&&p&&b&去除无关信息:&/b&&/p&&p&歌词中包含“作词”,“作曲”等信息,这对训练模型生成歌词是没有任何帮助的,先去掉他们。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&# 去除文本中不是属于歌词的行
# 我的程序写的都比较水啦
# 大家可以自由发挥写出更好的程序
for i in range(len(lrc_lines)):
line = lrc_lines[i]
if &作词& in line or &作曲& in line or &编曲& in line or &录音& in line or &混缩& in line or &制作人& in line:
lrc_lines[i] = &\n&
&/code&&/pre&&/div&&p&观察下已经去除作者信息的歌词文本:&/p&&p&可以看到现在歌词中还包括一些英文,这是嘻哈的特色。&/p&&p&&b&减少语言种类:&/b&&/p&&p&训练数据中存在的语言种类越多,意味着深度神经网络模型学习起来的难度越大。&/p&&p&因为这次做的是一个简单的模型,所以我不打算让模型再去预测英文歌词,所以我要把歌词中的英文去掉。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-082be63a7b4f1ffef473556_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&668& data-rawheight=&438& class=&origin_image zh-lightbox-thumb& width=&668& data-original=&https://pic4.zhimg.com/v2-082be63a7b4f1ffef473556_r.jpg&&&/figure&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&import re
final_lrc = []
# 使用正则表达式的手段识别中文
# 中文(包括繁体)在unicode中的编码范围是u4e00~u9fa5
zhPattern = re.compile(u'[\u4e00-\u9fa5]+')
# 遍历所有歌词行
for line in lrc_lines:
# findall会返回一个列表
zh_list = zhPattern.findall(line)
# 将中文拷贝到 final_lrc 中去
for item in zh_list:
final_lrc.append(item + '\n')
# 这样就只剩中文啦!
print(final_lrc[:10])
['抱歉我依旧不稳定\n', '鱼龙混杂的街头不只靠努力\n', '别烦恼\n', '多想你陪我透透气\n', '发发牢骚\n', '倾诉着最近不如意\n', '这就是生活里\n', '的问题\n', '不必不报忧\n', '做真实的自己\n']
&/code&&/pre&&/div&&p&&b&建立“字典”:&/b&&/p&&p&还是像之前一样对所有文字建立一个“文字表”,因为其中元素都是独立的汉字,所以这次我们称它为“字典”。&/p&&p&在字典中所有的文字都会有对应的一个索引号。&/p&&p&在训练神经网络模型的时候,传入神经网络的是某个字的“索引号”,而不是这个字本身。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&# 字典
word_to_ix = {}
for line in final_lrc:
for word in line:
if word not in word_to_ix:
# 在单词表的末端添加这个单词
word_to_ix[word] = len(word_to_ix)
# 打印出字典中的前10个元素,注意它是无序的
for i, key in zip(range(10), word_to_ix):
print(key, &:&, word_to_ix[key])
&/code&&/pre&&/div&&p&除了“字典”,还需要建立一个“有序列表”。&/p&&p&“有序列表”就是将“字典”按照索引号从小到大的顺序排序。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&import operator
sorted_char_list = sorted(word_to_ix.items(), key=operator.itemgetter(1), reverse=False)
# 有序列表
# 这里注意字典是“dict”类型
# 有序列表是“list”类型
char_list = []
for item in sorted_char_list:
char_list.append(item[0])
# 有序列表中的元素
print(char_list[:10])
# 观察有序列表中的元素即字典的有序排列
for ch in char_list[:10]:
print(ch, &:&, word_to_ix[ch])
&/code&&/pre&&/div&&p&&br&&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&['抱', '歉', '我', '依', '旧', '不', '稳', '定', '\n', '鱼']
&/code&&/pre&&/div&&p&“字典”的长度代表训练数据中有多少不同的字,这个长度即是我们模型输入层的大小,我们建立一个变量 nn_characters 来保存它。&/p&&h2&&b&训练前的准备:&/b&&/h2&&p&&b&数据随机选择器:&/b&&/p&&p&这次搭建的神经网络模型是“字符级”的,即每次输入“一个字”。&/p&&p&所以先要把上面处理过后的“一条条”的数据,都保存到一个“大字符串”里,便于随机取用。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&big_string = ''
for line in final_lrc:
for ch in line:
big_string += ch
print(big_string[:20])
抱歉我依旧不稳定
鱼龙混杂的街头不只靠努
&/code&&/pre&&/div&&p&然后我们再编写一个工具方法“random_chunk”,用于每次从训练数据中随机选择201个字。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&import random
chunk_len = 200
# 随机选取200 + 1个字符的数据
def random_chunk():
# 起点的可选范围:0 ~ final_lrc_len-chunk_len
start_index = random.randrange(0, len(big_string) - chunk_len)
end_index = start_index + chunk_len + 1
# 将抽取的列表项转化为字符串再返回
return big_string[start_index:end_index]
result = random_chunk()
print(len(result))
&/code&&/pre&&/div&&p&注意这里选取的字符串长度实际为201,为什么是201,到下面建立输入和目标数据的时候你就知道。&/p&&p&&b&将数据转化为张量(Tensor):&/b&&/p&&p&建立一个辅助方法“char_tensor”,用于将文字转化为张量(Tensor)。&/p&&p&张量中保存的是文字对应的索引。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&import torch
import torch.nn as nn
from torch.autograd import Variable
def char_tensor(string):
# 先按指定长度创建一个longTensor,填充0
tensor = torch.zeros(len(string)).long()
# 逐字查找字典
# 取出每个字的索引号,保存到 Tensor 中
for c in range(len(string)):
tensor[c] = word_to_ix[string[c]]
return Variable(tensor)
# 来试试这个方法
index_tensor = char_tensor(random_chunk())
# 查看 Tensor 长度
print(len(index_tensor))
# 查看 Tensor 中保存的索引
print(index_tensor[:10])
Variable containing:
[torch.LongTensor of size 10]
&/code&&/pre&&/div&&p&&b&输入和目标:&/b&&/p&&p&上面我们建立了“random_chunk”,用于选取201条数据。&/p&&p&为什么是201条?让我们回头看看文章开头的那张图:&/p&&figure&&img src=&https://pic4.zhimg.com/v2-e36a1dfd009eab309541abc2_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&344& data-rawheight=&193& class=&content_image& width=&344&&&/figure&&p&RNN 模型在输入“多”时,需要预测出“想”;在输入“想”时,需要预测“你”。&/p&&p&即如果我们随机选取的总数据是“多想你陪我透透气”,那么训练输入数据是“多想你陪我”,训练目标数据是“想你陪我透透气”。&/p&&p&训练输入数据与训练目标数据正好错开一个字,那么当我们选取了201条数据,那输入数据和目标数据就正好是各200条。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&def random_training_set():
# 先选取201字符长度的文本
chunk = random_chunk()
# 将选取的文本全部转化为 Tensor
inp = char_tensor(chunk[:-1])
target = char_tensor(chunk[1:])
return inp, target
inp, target = random_training_set()
print(len(inp))
print(len(target))
&/code&&/pre&&/div&&p&到这里我们在训练前要做的准备就全部完成了,下面要做的就是&b&搭建模型、训练模型、评估模型&/b&了。&/p&&hr&&p&如果您有任何关于Pytorch方面的问题,欢迎进【集智—清华】火炬群与大家交流探讨,添加集智小助手微信&b&swarmaAI&/b&,备注(&b&pytorch交流群&/b&),小助手会拉你入群。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-ac94c0c63d6e0fe7a9cbf_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&512& data-rawheight=&512& class=&origin_image zh-lightbox-thumb& width=&512& data-original=&https://pic3.zhimg.com/v2-ac94c0c63d6e0fe7a9cbf_r.jpg&&&/figure&&p&&b&关注集智AI学园公众号&/b& &/p&&p&&b&获取更多更有趣的AI教程吧!&/b&&/p&&p&&b&搜索微信公众号:swarmAI&/b&&/p&&p&&b&集智AI学园QQ群:&/b&&/p&&p&&b&学园网站:
&/b&&a href=&https://link.zhihu.com/?target=http%3A//campus.swarma.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&campus.swarma.org&/a&&/p&&p&&br&&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//weixin.qq.com/r/FzpGXp3ElMDrrdk9928F& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&weixin.qq.com/r/FzpGXp3&/span&&span class=&invisible&&ElMDrrdk9928F&/span&&span class=&ellipsis&&&/span&&/a& (二维码自动识别)&/p&&p&&/p&&p&&/p&
最近“中国有嘻哈”特别火 我也来跟风 所以本周的 NLP 教程是拿循环神经网络(RNN)来生成嘻哈歌词。我觉得行!先让我们看看一个简单的深度神经网络模型,能生成什么样的“嘻哈歌词”。我的世界 能够有 看透 我的兄弟在我身边 每个人 或许他们是我的眼 我的…
&figure&&img src=&https://pic4.zhimg.com/v2-afc7b537f5bd8db04ba59d_b.jpg& data-rawwidth=&3456& data-rawheight=&2304& class=&origin_image zh-lightbox-thumb& width=&3456& data-original=&https://pic4.zhimg.com/v2-afc7b537f5bd8db04ba59d_r.jpg&&&/figure&&h2&&b&前言&/b&&/h2&&p&什么是图像风格的迁移?其实现在很多的APP应用中已经普遍存在了,比如让我们选择一张自己的大头照,然后选择一种风格的图片,确认后我们的大头照变成了所选图片类似的风格。&/p&&p&图像风格迁移重点就是找出一张图片的特征,然后将其融合到需要改变的图片中去,如下图所展示的就是一种典型的风格迁移。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-b2b813c68a2ade403d70_b.jpg& data-caption=&& data-rawwidth=&261& data-rawheight=&264& class=&content_image& width=&261&&&/figure&&figure&&img src=&https://pic1.zhimg.com/v2-ae318ee90a95d6f65640cdbbddc6ef82_b.jpg& data-caption=&& data-rawwidth=&261& data-rawheight=&264& class=&content_image& width=&261&&&/figure&&figure&&img src=&https://pic3.zhimg.com/v2-f433fe8b735e8359ae56cf_b.jpg& data-caption=&& data-rawwidth=&261& data-rawheight=&264& class=&content_image& width=&261&&&/figure&&p&&br&&/p&&p&所以图像风格迁移实现的难点就在于如何提取一张图片的特征,这里说的特征也就是图像的风格。论文《&a href=&https://link.zhihu.com/?target=https%3A//arxiv.org/abs/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&A Neural Algorithm of Artistic Style&/a&》使用了CNN(卷积神经网络)来对图像的风格进行提取。因为我们都知道CNN本来就可以对特征图像进行提取,然后通过特征来实现图像的分类。当我们有了图像风格的提取方法后,只需要将新提取到的风格融入到新的图片中去,就实现了图像风格的迁移。&/p&&p&&br&&/p&&h2&&b&1、PyTorch核心代码实现&/b&&/h2&&p&其实代码的核心思想并不复杂,就是利用CNN提取内容图片的内容和风格图片的风格,然后输入一张新的图像。对输入的图像提取出内容和风格与CNN提取的内容和风格进行Loss计算,Loss的度量可以使用MSE,然后逐步对Loss进行优化,使Loss值达到最理想,将被优化的参数进行输出,这样输出的图片就达到了风格迁移的目的。&/p&&p&&br&&/p&&p&&b&(一)、计算内容损失&/b&&/p&&p&为什么使用卷积提取内容?&/p&&p&下图是我通过一个卷积提取到的其中一个特征映射,说明使用卷积作为内容提取的方法是完全可行的。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-32d0c2a962f0b00ce2c6e93_b.jpg& data-caption=&& data-rawwidth=&264& data-rawheight=&268& class=&content_image& width=&264&&&/figure&&p&计算内容损失的代码如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&class Content_loss(torch.nn.Module):
def __init__(self, weight, target):
super(Content_loss, self).__init__()
self.weight = weight
self.target = target.detach()*weight
self.loss_fn = torch.nn.MSELoss()
def forward(self, input):
self.loss = self.loss_fn(input*self.weight, self.target)
self.output = input
return self.output
def backward(self):
self.loss.backward(retain_graph = True)
return self.loss
&/code&&/pre&&/div&&p&这里的target就是CNN对内容图像提取得到的内容,weight是用来控制内容和风格对input图像的影响程度,这里的input就是我们输入图像,还有定义的backward主要目的其实是为了调用方向传播方法和返回我们计算得到的Loss。Loss计算使用的是MSE来度量。&/p&&p&&br&&/p&&p&&b&(二)、计算风格损失&/b&&/p&&p&计算风格损失的代码如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&class Style_loss(torch.nn.Module):
def __init__(self, weight, target):
super(Style_loss, self).__init__()
self.weight = weight
self.target = target.detach()*weight
self.loss_fn = torch.nn.MSELoss()
self.gram = gram_matrix()
def forward(self, input):
self.output = input.clone()
self.G = self.gram(input)
self.G.mul_(self.weight)
self.loss = self.loss_fn(self.G, self.target)
return self.output
def backward(self):
self.loss.backward(retain_graph = True)
return self.loss
&/code&&/pre&&/div&&p&这里的target、weight、input、backward、Loss使用的意义和之前的内容计算类似,唯一不同的地方是引入了Gram矩阵,通过对CNN提取后的内容进行Gram矩阵运算来定义图像的风格。 &/p&&p&为什么Gram矩阵能够定义图像的风格了?&/p&&p&因为CNN卷积过后提取了图像的特征图,每个数字就是原图像的特性大小,而Gram矩阵是矩阵的内积运算,运算过后特征图中越大的数字会变得更大,这就相当于对图像的特性进行了缩放,使得特征突出了,也就相当于提取到了图片的风格。&/p&&p&Gram矩阵的代码如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&class gram_matrix(torch.nn.Module):
def forward(self, input):
a,b,c,d = input.size()
feature = input.view(a*b, c*d)
gram = torch.mm(feature, feature.t())
return gram.div(a*b*c*d)
&/code&&/pre&&/div&&p&&br&&/p&&p&&b&(三)、构建训练CNN&/b&&/p&&p&构建新的训练模型代码:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&content_layer = [&Conv_5&,&Conv_6&]
style_layer = [&Conv_1&, &Conv_2&, &Conv_3&, &Conv_4&, &Conv_5&]
content_losses = []
style_losses = []
conten_weight = 1
style_weight = 1000
new_model = torch.nn.Sequential()
model = copy.deepcopy(cnn)
gram = gram_matrix()
if use_gpu:
new_model = new_model.cuda()
gram = gram.cuda()
for layer in list(model):
if isinstance(layer, torch.nn.Conv2d):
name = &Conv_&+str(index)
new_model.add_module(name, layer)
if name in content_layer:
target = new_model(content_img).clone()
content_loss = Content_loss(conten_weight, target)
new_model.add_module(&content_loss_&+str(index), content_loss)
content_losses.append(content_loss)
if name in style_layer:
target = new_model(style_img).clone()
target = gram(target)
style_loss = Style_loss(style_weight, target)
new_model.add_module(&style_loss_&+str(index), style_loss)
style_losses.append(style_loss)
if isinstance(layer, torch.nn.ReLU):
name = &Relu_&+str(index)
new_model.add_module(name, layer)
index = index+1
if isinstance(layer, torch.nn.MaxPool2d):
name = &MaxPool_&+str(index)
new_model.add_module(name, layer)
&/code&&/pre&&/div&&p&要完成风格迁移,我们还需要构建自己的CNN网络。首先迁移了vgg16的模型,剔除了全连接部分,之后就是根据vgg16模型架构重构训练模型,加入了内容和风格Loss的计算部分。这里内容的提取只是选择了5、6层卷积,风格的提取只选择了1、2、3、4、5层卷积。&/p&&p&&br&&/p&&p&&b&(四)、定义优化&/b&&/p&&p&优化定义代码:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&input_img = content_img.clone()
parameter = torch.nn.Parameter(input_img.data)
optimizer = torch.optim.LBFGS([parameter])
&/code&&/pre&&/div&&p&这里为什么使用LBFGS来进行优化?&/p&&p&原因是我们要优化的Loss其实是多个,而不是像处理分类问题中只是需要优化一个Loss值,LBFGS能够获得更好的效果。&/p&&p&&br&&/p&&p&&b&(五)、训练新定义的CNN&/b&&/p&&p&训练代码如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&n_epoch = 1000
while run[0] &= n_epoch:
def closure():
optimizer.zero_grad()
style_score = 0
content_score = 0
parameter.data.clamp_(0,1)
new_model(parameter)
for sl in style_losses:
style_score += sl.backward()
for cl in content_losses:
content_score += cl.backward()
run[0] += 1
if run[0] % 50 == 0:
print('{} Style Loss : {:4f} Content Loss: {:4f}'.format(run[0],
style_score.data[0], content_score.data[0]))
return style_score+content_score
optimizer.step(closure)
&/code&&/pre&&/div&&p&n_epoch定义了训练次数为1000次,使用sl.backward()和cl.backward()实现了反向传播,对参数进行优化。&/p&&p&&br&&/p&&h2&&b&2、改进&/b&&/h2&&p&本文的图像风格迁移的方法没次实现都要进行一轮训练,而且风格调节的方式需要通过weight权重来控制,在实际应用中并不理想,现实中我们需要更加高效智能的实现方式。改进方法已经出现,先放出两篇论文&/p&&p&&a href=&https://link.zhihu.com/?target=https%3A//arxiv.org/abs/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Fast Patch-based Style Transfer of Arbitrary Style&/a&&/p&&p&&a href=&https://link.zhihu.com/?target=https%3A//arxiv.org/abs/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Visual Attribute Transfer through Deep Image Analogy&/a&&/p&&p&代码还在实现中......&/p&&p&&br&&/p&&p&参考资料:1、&a href=&https://link.zhihu.com/?target=http%3A//pytorch.org/tutorials/index.html%23& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Welcome to PyTorch Tutorials&/a&&/p&&p&
2、&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&图像风格迁移(Neural Style)简史&/a&&/p&&p&&br&&/p&&p&完整代码:&a href=&https://link.zhihu.com/?target=https%3A//github.com/JaimeTang/PyTorch-and-Neural-style-transfer& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&JaimeTang/PyTorch-and-Neural-style-transfer&/a&&/p&&p&&b&如果觉得还行,请点个赞哦......&/b&&/p&
前言什么是图像风格的迁移?其实现在很多的APP应用中已经普遍存在了,比如让我们选择一张自己的大头照,然后选择一种风格的图片,确认后我们的大头照变成了所选图片类似的风格。图像风格迁移重点就是找出一张图片的特征,然后将其融合到需要改变的图片中去…
&figure&&img src=&https://pic1.zhimg.com/v2-690153dddfc4fbc3_b.jpg& data-rawwidth=&2020& data-rawheight=&1348& class=&origin_image zh-lightbox-thumb& width=&2020& data-original=&https://pic1.zhimg.com/v2-690153dddfc4fbc3_r.jpg&&&/figure&&p&又一个月没更新了,前段时间事情多,还赶了一波ICLR的deadline。最近又出了点状况,得找工作了。哎,秋招基本结束了,该如何是好......&/p&&p&吐槽完了回归正题,今天要介绍的文章是NVIDIA投稿ICLR 2018的一篇文章,Progressive Growing of GANs for Improved Quality, Stability, and Variation[1],姑且称它为PG-GAN。从行文可以看出文章是临时赶出来的,毕竟这么大的实验,用P100都要跑20天,更不用说调参时间了,不过人家在NVIDIA,不缺卡。作者放出了&a href=&https://link.zhihu.com/?target=https%3A//github.com/tkarras/progressive_growing_of_gans& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&基于lasagna的代码&/a&,今天我也会简单解读一下代码。另外,我也在&a href=&https://link.zhihu.com/?target=https%3A//github.com/github-pengge/PyTorch-progressive_growing_of_gans& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&用Pytorch做复现&/a&。&/p&&p&在PG-GAN出来以前,训练高分辨率图像生成的GAN方法主要就是LAPGAN[2]和BEGAN[6]。后者主要是针对人脸的,生成的人脸逼真而不会是鬼脸,这里也提一下,生成鬼脸的原因是Discriminator不再更新,它不能再给予Generator其他指导,Generator找到了一种骗过Discriminator的方法,也就是生成鬼脸,而且很大可能会mode collapse。下图是我用PyTorch做的&a href=&https://link.zhihu.com/?target=https%3A//github.com/github-pengge/GANs& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&BEGAN复现&/a&,当时没有跑很高的分辨率,但是效果确实比其他GAN好几本没有鬼脸。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-9adad92db6bc07d19b800d5b0eee80ff_b.jpg& data-size=&normal& data-rawwidth=&646& data-rawheight=&642& class=&origin_image zh-lightbox-thumb& width=&646& data-original=&https://pic2.zhimg.com/v2-9adad92db6bc07d19b800d5b0eee80ff_r.jpg&&&figcaption&BEGAN的PyTorch复现结果,参看我的github&/figcaption&&/figure&&p&PG-GAN能够稳定地训练生成高分辨率的GAN。我们来看一下PG-GAN跟别的GAN不同在哪里。&/p&&h2&1. 训练方式&/h2&&p&作者采用progressive growing的训练方式,先训一个小分辨率的图像生成,训好了之后再逐步过渡到更高分辨率的图像。然后稳定训练当前分辨率,再逐步过渡到下一个更高的分辨率。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-aeede5ecaad0def8f2fe66e554f0e52c_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1478& data-rawheight=&678& class=&origin_image zh-lightbox-thumb& width=&1478& data-original=&https://pic4.zhimg.com/v2-aeede5ecaad0def8f2fe66e554f0e52c_r.jpg&&&/figure&&p&如上图所示。更具体点来说,当处于fade in(或者说progressive growing)阶段的时候,上一分辨率(4*4)会通过resize+conv操作得到跟下一分辨率(8*8)同样大小的输出,然后两部分做加权,再通过to_rgb操作得到最终的输出。这样做的一个好处是它可以充分利用上个分辨率训练的结果,通过缓慢的过渡(w逐渐增大),使得训练生成下一分辨率的网络更加稳定。&/p&&p&上面展示的是Generator的growing阶段。下图是Discriminator的growing,它跟Generator的类似,差别在于一个是上采样,一个是下采样。这里就不再赘述。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-d836a6ba48f76ff8cab4cc2_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1390& data-rawheight=&632& class=&origin_image zh-lightbox-thumb& width=&1390& data-original=&https://pic4.zhimg.com/v2-d836a6ba48f76ff8cab4cc2_r.jpg&&&/figure&&p&不难想象,网络在growing的时候,如果不引入progressive(fade in),那么有可能因为比较差的初始化,导致原来训练的进度功亏一篑,模型不得不从新开始学习,如此一来就没有充分利用以前学习的成果,甚至还可能误导。我们知道GAN的训练不稳定,这样的突变有时候是致命的。所以fade in对训练的稳定性来说至关重要。&/p&&p&说到growing的训练方式,我们很容易想到autoencoder也有一种类似的训练方式:先训各一层的encoder和decoder,训好了以后再过渡到训练各两层的encoder和decoder,这样的好处是避免梯度消失,导致离loss太远的层更新不够充分。PG-GAN的做法可以说是这种autoencoder训练方式在GAN训练上的应用。&/p&&p&此外,训练GAN生成高分辨率图像,还有一种方法,叫LAPGAN[2]。LAPGAN借助CGAN,高分辨率图像的生成是以低分辨率图像作为条件去生成残差,然后低分辨率图上采样跟残差求和得到高分辨率图,通过不断堆叠CGAN得到我们想要的分辨率。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-0d06dcf280b113f0e3fe_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1998& data-rawheight=&552& class=&origin_image zh-lightbox-thumb& width=&1998& data-original=&https://pic4.zhimg.com/v2-0d06dcf280b113f0e3fe_r.jpg&&&/figure&&p&LAPGAN是多个CGAN堆叠一起训练,当然可以拆分成分阶段训练,但是它们本质上是不同的,LAPGAN学的是残差,而PG-GAN存在stabilize训练阶段,学的不是残差,而直接是图像。&/p&&p&作者在代码中设计了一个LODSelectLayer来实现progressive growing。对于Generator,每一层插入一个LODSelectLayer,它实际上就是一个输出分支,实现在特定层的输出。从代码来看,作者应该是这样训练的(参见&a href=&https://link.zhihu.com/?target=https%3A//github.com/tkarras/progressive_growing_of_gans/blob/master/train.py& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&这里&/a&的train_gan函数),先构建4*4分辨率的网络,训练,然后把网络存出去。再构建8*8分辨率的网络,导入原来4*4的参数,然后训fade in,再训stabilize,再存出去。我在复现的时候,根据文章的意思,修改了LODSelectLayer层,因为pytorch是动态图,能够很方便地写if-else逻辑语句。&/p&&p&借助这种growing的方式,PG-GAN的效果超级好。另外,我认为这种progressive growing的方法比较适合GAN的训练,GAN训练不稳定可以通过growing的方式可以缓解。不只是在噪声生成图像的任务中可以这么做,在其他用到GAN的任务中都可以引入这种训练方式。我打算将progressive growing引入到CycleGAN中,希望能够得到更好的结果。&/p&&p&&br&&/p&&h2&2. 增加生成多样性&/h2&&p&增加生成样本的多样性有两种可行的方法:通过loss让网络自己调整、通过设计判别多样性的特征人为引导。&/p&&p&WGAN属于前者,它采用更好的分布距离的估计(Wasserstein distance)。模型收敛意味着生成的分布和真实分布一致,能够有多样性的保证。PG-GAN则属于后者。&/p&&p&作者沿用improved GAN的思路,通过人为地给Discriminator构造判别多样性的特征来引导Generator生成更多样的样本。Discriminator能探测到mode collapse是否产生了,一旦产生,Generator的loss就会增大,通过优化Generator就会往远离mode collapse的方向走,而不是一头栽进坑里。&/p&&p&Improved GAN引入了minibatch discrimination层,构造一个minibatch内的多样性衡量指标。它引入了新的参数。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-aa7e7eff862a78b96996a_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1678& data-rawheight=&1124& class=&origin_image zh-lightbox-thumb& width=&1678& data-original=&https://pic4.zhimg.com/v2-aa7e7eff862a78b96996a_r.jpg&&&/figure&&p&而PG-GAN不引入新的参数,利用特征的标准差作为衡量标准。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-cffe5b121ea6cf02fa59f70e_b.jpg& data-size=&normal& data-rawwidth=&1988& data-rawheight=&1304& class=&origin_image zh-lightbox-thumb& width=&1988& data-original=&https://pic4.zhimg.com/v2-cffe5b121ea6cf02fa59f70e_r.jpg&&&figcaption&Minibatch statistic layer: averaging=&all&&/figcaption&&/figure&&p&这里啰嗦地说明上面那张图做了什么。我们有N个样本的feature maps(为了画图方便,不妨假设每个样本只有一个feature map),我们对每个空间位置求标准差,用numpy的std函数来说就是沿着样本的维度求std。这样就得到一张新的feature map(如果样本的feature map不止一个,那么这样构造得到的feature map数量应该是一致的),接着feature map求平均得到一个数。这个过程简单来说就是求mean std,作者把这个数复制成一张feature map的大小,跟原来的feature map拼在一起送给Discriminator。&/p&&p&从作者放出来的代码来看,这对应averaging=“all”的情况。作者还尝试了其他的统计量:“spatial”,“gpool”,“flat”等。它们的主要差别在于沿着哪些维度求标准差。至于它们的作用,等我的代码复现完成了会做一个测试。估计作者调参发现“all”的效果最好。&/p&&p&&br&&/p&&h2&3. Normalization&/h2&&p&从DCGAN[3]开始,GAN的网络使用batch(or instance) normalization几乎成为惯例。使用batch norm可以增加训练的稳定性,大大减少了中途崩掉的情况。作者采用了两种新的normalization方法,不引入新的参数(不引入新的参数似乎是PG-GAN各种tricks的一个卖点)。&/p&&p&第一种normalization方法叫pixel norm,它是local response normalization的变种。Pixel norm沿着channel维度做归一化,这样归一化的一个好处在于,feature map的每个位置都具有单位长度。这个归一化策略与作者设计的Generator输出有较大关系,注意到Generator的输出层并没有Tanh或者Sigmoid激活函数,后面我们针对这个问题进行探讨。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-17b4752461bcd4aadf916dca_b.jpg& data-size=&normal& data-rawwidth=&1546& data-rawheight=&1468& class=&origin_image zh-lightbox-thumb& width=&1546& data-original=&https://pic1.zhimg.com/v2-17b4752461bcd4aadf916dca_r.jpg&&&figcaption&Pixel norm。注意:minibatch statistic layer沿着batch维度求标准差,而它沿着channel维度求norm。&/figcaption&&/figure&&p&第二种normalization方法跟凯明大神的初始化方法[4]挂钩。He的初始化方法能够确保网络初始化的时候,随机初始化的参数不会大幅度地改变输入信号的强度。&/p&&p&&img src=&https://www.zhihu.com/equation?tex=%5Cfrac%7B1%7D%7B2%7Dn_l+%5Ctext%7BVar%7D%5Bw_l%5D+%3D+1& alt=&\frac{1}{2}n_l \text{Var}[w_l] = 1& eeimg=&1&&&img src=&https://www.zhihu.com/equation?tex=%28w_l%3A+%5Ctext%7Bweights+of+layer+%7D+l%3B+%5C+n_l%3A+%5Ctext%7Bnumber+of+weights+at+layer+%7D+l%29& alt=&(w_l: \text{weights of layer } \ n_l: \text{number of weights at layer } l)& eeimg=&1&&&/p&&p&根据这个式子,我们可以推导出网络每一层的参数应该怎样初始化。可以参考pytorch提供的&a href=&https://link.zhihu.com/?target=http%3A//pytorch.org/docs/master/nn.html%23torch-nn-init& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&接口&/a&。&/p&&p&作者走得比这个要远一点,他不只是初始化的时候对参数做了调整,而是动态调整。初始化采用标准高斯分布,但是每次迭代都会对weights按照上面的式子做归一化。作者argue这样的归一化的好处在于它不用再担心参数的scale问题,起到均衡学习率的作用(euqalized learning rate)。&/p&&p&&br&&/p&&h2&4. 有针对性地给样本加噪声&/h2&&p&通过给真实样本加噪声能够起到均衡Generator和Discriminator的作用,起到缓解mode collapse的作用,这一点在WGAN的前传中就已经提到[5]。尽管使用LSGAN会比原始的GAN更容易训练,然而它在Discriminator的输出接近1的适合,梯度就消失,不能给Generator起到引导作用。针对D趋近1的这种特性,作者提出了下面这种添加噪声的方式&/p&&p&&img src=&https://www.zhihu.com/equation?tex=%5Ctext%7Bnoise+strength%7D+%3D+0.2%2A%5Cmax%280%2C+d_t+-+0.5%29%5E2+& alt=&\text{noise strength} = 0.2*\max(0, d_t - 0.5)^2 & eeimg=&1&&&/p&&p&其中, &img src=&https://www.zhihu.com/equation?tex=d_t+%3D+0.9%2Ad_%7Bt-1%7D+%2B+0.1%2Ad& alt=&d_t = 0.9*d_{t-1} + 0.1*d& eeimg=&1&& , &img src=&https://www.zhihu.com/equation?tex=d_t%2C+d& alt=&d_t, d& eeimg=&1&&
分别为第t次迭代判别器输出的修正值、第t-1次迭代真样本的判别器输出。&/p&&p&从式子可以看出,当真样本的判别器输出越接近1的时候,噪声强度就越大,而输出太小(&=0.5)的时候,不引入噪声,这是因为0.5是LSGAN收敛时,D的合理输出(无法判断真假样本),而小于0.5意味着D的能力太弱。&/p&&p&&br&&/p&&p&文章还有其他很多tricks,有些tricks不是作者提出的,如Layer norm,还有一些比较细微的tricks,比如每个分辨率训练好做sample的时候学习率怎么decay,每个分辨率的训练迭代多少次等等,我们就不再详细展开。具体可以参见官方代码,也可以看我复现的代码。&/p&&p&目前复现的结果还在跑,现在训练到了16*16分辨率的fade in阶段,放一张当前的结果图,4个方格的每个方格左边4列是生成的图,右边4列是真实样本。现在还处于训练早期,分辨率太低,过几天看一下高分辨率的结果。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-b4c262d90b7de_b.jpg& data-size=&normal& data-rawwidth=&902& data-rawheight=&636& class=&origin_image zh-lightbox-thumb& width=&902& data-original=&https://pic1.zhimg.com/v2-b4c262d90b7de_r.jpg&&&figcaption& 16*16 resolution temporary results (phase=fade in)&/figcaption&&/figure&&p&&b&Update()&/b&: 训练开始变得很慢,不过稳定性似乎有所上升,现在训练到了32x32分辨率,效果图(每一方格左边四列是生成图,右边四列是原图)如下。另外,我补充了Generator的输出层使用linear激活函数的实验,实验效果也很好。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-128de481df8fab3ca55bdf_b.jpg& data-size=&normal& data-rawwidth=&1264& data-rawheight=&650& class=&origin_image zh-lightbox-thumb& width=&1264& data-original=&https://pic4.zhimg.com/v2-128de481df8fab3ca55bdf_r.jpg&&&figcaption&32x32 resolution temporary result (phase=fade in)&/figcaption&&/figure&&p&&b&Update()&/b&: 128x128 fade in结果如下,左边两列是生成图,右边两列是真实图。获得类似的效果,可以参看github上面的说明。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-6fe10a4f2e375f952cbc0_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&524& data-rawheight=&512& class=&origin_image zh-lightbox-thumb& width=&524& data-original=&https://pic4.zhimg.com/v2-6fe10a4f2e375f952cbc0_r.jpg&&&/figure&&p&&br&&/p&&p&官方Lasagna代码:&a href=&https://link.zhihu.com/?target=https%3A//github.com/tkarras/progressive_growing_of_gans& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&tkarras/progressive_growing_of_gans&/a&&/p&&p&我的PyTorch复现:&a href=&https://link.zhihu.com/?target=https%3A//github.com/github-pengge/PyTorch-progressive_growing_of_gans& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&github-pengge/PyTorch-progressive_growing_of_gans&/a&&/p&&p&&br&&/p&&h2&参考文献&/h2&&p&[1]. Karras T, Aila T, Laine S, et al. Progressive Growing of GANs for Improved Quality, Stability, and Variation[J]. arXiv preprint arXiv:, 2017.&/p&&p&[2]. Denton E L, Chintala S, Fergus R. Deep Generative Image Models using a? Laplacian Pyramid of Adversarial Networks[C]//Advances in neural information processing systems. -1494.&/p&&p&[3]. Radford A, Metz L, Chintala S. Unsupervised representation learning with deep convolutional generative adversarial networks[J]. arXiv preprint arXiv:, 2015.&/p&&p&[4]. He K, Zhang X, Ren S, et al. Delving deep into rectifiers: Surpassing human-level performance on imagenet classification[C]//Proceedings of the IEEE international conference on computer vision. -1034.&/p&&p&[5]. Arjovsky M, Bottou L. Towards principled methods for training generative adversarial networks[J]. arXiv preprint arXiv:, 2017.&/p&&p&[6]. Berthelot D, Schumm T, Metz L. Began: Boundary equilibrium generative adversarial networks[J]. arXiv preprint arXiv:, 2017.&/p&
又一个月没更新了,前段时间事情多,还赶了一波ICLR的deadline。最近又出了点状况,得找工作了。哎,秋招基本结束了,该如何是好......吐槽完了回归正题,今天要介绍的文章是NVIDIA投稿ICLR 2018的一篇文章,Progressive Growing of GANs for Improved Qual…
&figure&&img src=&https://pic2.zhimg.com/v2-cb28234a04fade3ee559606_b.jpg& data-rawwidth=&450& data-rawheight=&369& class=&origin_image zh-lightbox-thumb& width=&450& data-original=&https://pic2.zhimg.com/v2-cb28234a04fade3ee559606_r.jpg&&&/figure&&p&人脸属性识别已经是一个解决的比较好的问题了。这里是花了一天时间做的一个简单的验证性项目。工程完整代码(GitHub)在&/p&&a href=&https://link.zhihu.com/?target=https%3A//github.com/WynMew/FaceAttribute& data-draft-node=&block& data-draft-type=&link-card& data-image=&https://pic3.zhimg.com/v2-eff3270adae26f7ab1f7a64cf4b84b82_ipico.jpg& data-image-width=&400& data-image-height=&400& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&WynMew/FaceAttribute&/a&&p&训练数据使用CUHK的&a href=&https://link.zhihu.com/?target=http%3A//mmlab.ie.cuhk.edu.hk/projects/CelebA.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Large-scale CelebFaces Attributes (CelebA) Dataset&/a&. 该数据集有40个属性标定(Attribute Label). 情况如下[1]:&/p&&figure&&img src=&https://pic2.zhimg.com/v2-e7efc5fe52b80_b.jpg& data-size=&normal& data-rawwidth=&1708& data-rawheight=&468& class=&origin_image zh-lightbox-thumb& width=&1708& data-original=&https://pic2.zhimg.com/v2-e7efc5fe52b80_r.jpg&&&figcaption&CelebA Label分布(蓝色为正样本)&/figcaption&&/figure&&p&可见其中各个Label的正负样本都是不均衡的,而且大部分的Label都不实用。这里我选择了6个比较实用的Attribute Label做试验:Attractive(魅力), EyeGlasses(眼镜), Male(男性), MouthOpen(张嘴), Smiling(微笑), Young(年轻).&/p&&p&&b&数据预处理&/b&&/p&&p&数据预处理的目的在于减少数据集内数据分布的差异性,有减少类内距离的同时增加类间距离的实际效果。在人脸数据处理方面,常用的有人脸检测和对齐。由于人脸属性识别任务比较简单,在这里的验证中我只使用了人脸检测(抠出图像中的人脸)。&/p&&p&在GitHub的代码中(&a href=&https://link.zhihu.com/?target=https%3A//github.com/WynMew/FaceAttribute/blob/master/detMTCNN_celebA.py& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&github.com/WynMew/FaceA&/span&&span class=&invisible&&ttribute/blob/master/detMTCNN_celebA.py&/span&&span class=&ellipsis&&&/span&&/a&),人脸检测使用MTCNN[2]实现,这依赖于caffe,并且效果在当今看来已经差强人意了。如果希望不依赖caffe, 可以使用dlib解决这个问题:&/p&&a href=&https://link.zhihu.com/?target=https%3A//github.com/davisking/dlib/blob/master/python_examples/face_detector.py& data-draft-node=&block& data-draft-type=&link-card& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&github.com/davisking/dl&/span&&span class=&invisible&&ib/blob/master/python_examples/face_detector.py&/span&&span class=&ellipsis&&&/span&&/a&&p&在检测完之后,会有部分的图像中不能检测出人脸,这部分数据就不管了。我们使用能检测出人脸的数据做训练和测试。为了训练,我们需要知道每个检测到的人脸的Label. 这就需要从celebA提供的图像Label中查找图像名称和对应的Label值。代码为&a href=&https://link.zhihu.com/?target=https%3A//github.com/WynMew/FaceAttribute/blob/master/AttrListGen.py& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&github.com/WynMew/FaceA&/span&&span class=&invisible&&ttribute/blob/master/AttrListGen.py&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&预处理完后将数据分解为三部分: Train, Val 和Test.&/p&&p&&b&数据读取&/b&&/p&&p&PyTorch提供了数据读取接口(torch.utils.data.Dataloader),可供很方便的读取数据,对数据进行变换(data augmentation) 和调试。在dataloader中我们需要读取图像, 并归一化:&/p&&div class=&highlight&&&pre&&code class=&language-python3&&&span&&/span&&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&normalize&/span& &span class=&o&&=&/span& &span class=&n&&transforms&/span&&span class=&o&&.&/span&&span class=&n&&Normalize&/span&&span class=&p&&(&/span&&span class=&n&&mean&/span&&span class=&o&&=&/span&&span class=&p&&[&/span&&span class=&mf&&0.5&/span&&span class=&p&&,&/span& &span class=&mf&&0.5&/span&&span class=&p&&,&/span& &span class=&mf&&0.5&/span&&span class=&p&&],&/span& &span class=&n&&std&/span&&span class=&o&&=&/span&&span class=&p&&[&/span&&span class=&mf&&0.5&/span&&span class=&p&&,&/span& &span class=&mf&&0.5&/span&&span class=&p&&,&/span& &span class=&mf&&0.5&/span&&span class=&p&&])&/span&
&/code&&/pre&&/div&&p&读取6个label, 并转换成Tensor. &/p&&div class=&highlight&&&pre&&code class=&language-python3&&&span&&/span&&span class=&k&&def&/span& &span class=&nf&&__getitem__&/span&&span class=&p&&(&/span&&span class=&bp&&self&/span&&span class=&p&&,&/span& &span class=&n&&idx&/span&&span class=&p&&)&/span&
&span class=&k&&class&/span& &span class=&nc&&ToTensorDict&/span&&span class=&p&&(&/span&&span class=&nb&&object&/span&&span class=&p&&)&/span&
&/code&&/pre&&/div&&p&完整代码为&/p&&a href=&https://link.zhihu.com/?target=https%3A//github.com/WynMew/FaceAttribute/blob/master/dataloadercelebA.py& data-draft-node=&block& data-draft-type=&link-card& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&github.com/WynMew/FaceA&/span&&span class=&invisible&&ttribute/blob/master/dataloadercelebA.py&/span&&span class=&ellipsis&&&/span&&/a&&p&&b&模型&/b&&/p&&p&数据集有限,同时仅仅是验证目的,所以这里从现成的模型开始。我们选用ResNet [3]开始finetune. 验证使用了ResNet18和ResNet34两个相对较小的模型作为feature提取器:&/p&&a href=&https://link.zhihu.com/?target=https%3A//github.com/WynMew/FaceAttribute/blob/master/AttrPreModelRes18_256V0.py& data-draft-node=&block& data-draft-type=&link-card& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&github.com/WynMew/FaceA&/span&&span class=&invisible&&ttribute/blob/master/AttrPreModelRes18_256V0.py&/span&&span class=&ellipsis&&&/span&&/a&&a href=&https://link.zhihu.com/?target=https%3A//github.com/WynMew/FaceAttribute/blob/master/AttrPreModelRes34_256V0.py& data-draft-node=&block& data-draft-type=&link-card& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&github.com/WynMew/FaceA&/span&&span class=&invisible&&ttribute/blob/master/AttrPreModelRes34_256V0.py&/span&&span class=&ellipsis&&&/span&&/a&&p&在feature提取之后,简单的接上了6个暴力分类器:&/p&&div class=&highlight&&&pre&&code class=&language-python3&&&span&&/span&&span class=&k&&class&/span& &span class=&nc&&Classifier&/span&&span class=&p&&(&/span&&span class=&n&&nn&/span&&span class=&o&&.&/span&&span class=&n&&Module&/span&&span class=&p&&):&/span&
&span class=&k&&def&/span& &span class=&nf&&__init__&/span&&span class=&p&&(&/span&&span class=&bp&&self&/span&&span class=&p&&,&/span& &span class=&n&&output_dim&/span&&span class=&o&&=&/span&&span class=&mi&&1&/span&&span class=&p&&):&/span&
&span class=&nb&&super&/span&&span class=&p&&(&/span&&span class=&n&&Classifier&/span&&span class=&p&&,&/span& &span class=&bp&&self&/span&&span class=&p&&)&/span&&span class=&o&&.&/span&&span class=&n&&__init__&/span&&span class=&p&&()&/span&
&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&fc1&/span& &span class=&o&&=&/span& &span class=&n&&nn&/span&&span class=&o&&.&/span&&span class=&n&&Sequential&/span&&span class=&p&&(&/span&
&span class=&n&&nn&/span&&span class=&o&&.&/span&&span class=&n&&Linear&/span&&span class=&p&&(&/span&&span class=&mi&&2048&/span&&span class=&p&&,&/span& &span class=&mi&&512&/span&&span class=&p&&),&/span&
&span class=&n&&nn&/span&&span class=&o&&.&/span&&span class=&n&&ReLU&/span&&span class=&p&&(&/span&&span class=&kc&&True&/span&&span class=&p&&),&/span&
&span class=&n&&nn&/span&&span class=&o&&.&/span&&span class=&n&&Dropout&/span&&span class=&p&&(&/span&&span class=&n&&p&/span&&span class=&o&&=&/span&&span class=&mf&&0.5&/span&&span class=&p&&),&/span&
&span class=&n&&nn&/span&&span class=&o&&.&/span&&span class=&n&&Linear&/span&&span class=&p&&(&/span&&span class=&mi&&512&/span&&span class=&p&&,&/span& &span class=&mi&&128&/span&&span class=&p&&),&/span&
&span class=&n&&nn&/span&&span class=&o&&.&/span&&span class=&n&&ReLU&/span&&span class=&p&&(&/span&&span class=&kc&&True&/span&&span class=&p&&),&/span&
&span class=&n&&nn&/span&&span class=&o&&.&/span&&span class=&n&&Dropout&/span&&span class=&p&&(&/span&&span class=&n&&p&/span&&span class=&o&&=&/span&&span class=&mf&&0.5&/span&&span class=&p&&),&/span&
&span class=&n&&nn&/span&&span class=&o&&.&/span&&span class=&n&&Linear&/span&&span class=&p&&(&/span&&span class=&mi&&128&/span&&span class=&p&&,&/span& &span class=&n&&output_dim&/span&&span class=&p&&),&/span&
&span class=&p&&)&/span&
&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&fc1&/span&&span class=&o&&.&/span&&span class=&n&&cuda&/span&&span class=&p&&()&/span&
&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&fc2&/span& &span class=&o&&=&/span& &span class=&n&&nn&/span&&span class=&o&&.&/span&&span class=&n&&Sequential&/span&&span class=&p&&(&/span&
&span class=&n&&nn&/span&&span class=&o&&.&/span&&span class=&n&&Linear&/span&&span class=&p&&(&/span&&span class=&mi&&2048&/span&&span class=&p&&,&/span& &span class=&mi&&512&/span&&span class=&p&&),&/span&
&span class=&n&&nn&/span&&span class=&o&&.&/span&&span class=&n&&ReLU&/span&&span class=&p&&(&/span&&span class=&kc&&True&/span&&span class=&p&&),&/span&
&span class=&n&&nn&/span&&span class=&o&&.&/span&&span class=&n&&Dropout&/span&&span class=&p&&(&/span&&span class=&n&&p&/span&&span class=&o&&=&/span&&span class=&mf&&0.5&/span&&span class=&p&&),&/span&
&span class=&n&&nn&/span&&span class=&o&&.&/span&&span class=&n&&Linear&/span&&span class=&p&&(&/span&&span class=&mi&&512&/span&&span class=&p&&,&/span& &span class=&mi&&128&/span&&span class=&p&&),&/span&
&span class=&n&&nn&/span&&span class=&o&&.&/span&&span class=&n&&ReLU&/span&&span class=&p&&(&/span&&span class=&kc&&True&/span&&span class=&p&&),&/span&
&span class=&n&&nn&/span&&span class=&o&&.&/span&&span class=&n&&Dropout&/span&&span class=&p&&(&/span&&span class=&n&&p&/span&&span class=&o&&=&/span&&span class=&mf&&0.5&/span&&span class=&p&&),&/span&
&span class=&n&&nn&/span&&span class=&o&&.&/span&&span class=&n&&Linear&/span&&span class=&p&&(&/span&&span class=&mi&&128&/span&&span class=&p&&,&/span& &span class=&n&&output_dim&/span&&span class=&p&&),&/span&
&span class=&p&&)&/span&
&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&fc2&/span&&span class=&o&&.&/span&&span class=&n&&cuda&/span&&span class=&p&&()&/span&
&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&fc3&/span& &span class=&o&&=&/span& &span class=&n&&nn&/span&&span class=&o&&.&/span&&span class=&n&&Sequential&/span&&span class=&p&&(&/span&
&span class=&n&&nn&/span&&span class=&o&&.&/span&&span class=&n&&Linear&/span&&span class=&p&&(&/span&&span class=&mi&&2048&/span&&span class=&p&&,&/span& &span class=&mi&&512&/span&&span class=&p&&),&/span&
&span class=&n&&nn&/span&&span class=&o&&.&/span&&span class=&n&&ReLU&/span&&span class=&p&&(&/span&&span class=&kc&&True&/span&&span class=&p&&),&/span&
&span class=&n&&nn&/span&&span class=&o&&.&/span&&span class=&n&&Dropout&/span&&span class=&p&&(&/span&&span class=&n&&p&/span&&span class=&o&&=&/span&&span class=&mf&&0.5&/span&&span class=&p&&),&/span&
&span class=&n&&nn&/span&&span class=&o&&.&/span&&span class=&n&&Linear&/span&&span class=&p&&(&/span&&span class=&mi&&512&/span&&span class=&p&&,&/span& &span class=&mi&&128&/span&&span class=&p&&),&/span&
&span class=&n&&nn&

我要回帖

更多关于 有一首歌是京剧和英文 的文章

 

随机推荐