解密Deepfake(深度换脸)-基于自编码器的(Pytorch代码)换脸技术

前言

还记得在2018月3月份火爆reddit的deepfake吗?将视频中的头换成另外一我的的头像,虽然可能有些粗糙和模糊,可是在分辨率不要求很高的状况下能够达到以假乱真的效果。git

举个栗子,以下图中将希拉里换成特朗普的一段演讲视频。程序员

1fca0bd71f9e49038fd19ffff705f7e8

另外还有实现川普和尼古拉脸相换:github

deep-fake

固然这只是DeepFake的冰山一角,Deepfake当初火起来的缘由能够说是广大拥有宅男心态的程序员们一块儿奋斗的结果。那就是,呃,能够将你想要的某张脸换到AV中去。固然这里就不进行演示了,而且相关的reddit论坛已经被禁止。因此这里就很少进行讨论啦。算法

本文为 github.com/Oldpan/Face… 代码的搭配教程(填一下以前埋得坑),这里简单对此进行讲解下,填补一下以前的空缺。bash

相关研究

其实有关深度学习的换脸相关的研究已经很普及了,有基于GAN的也有基于Glow的,但本质上都是生成模型,只是换了一种实现方式,而这个DeepFake呢,使用的是机器学习中的自编码器,拥有与神经网络相似的结构,鲁棒性较好,咱们能够经过学习它来对生成网络有一个大概的了解,这样以后碰到类似的网络或者构造就好上手了。网络

技术讲解

人脸互换是计算机视觉领域中一个比较热门的应用,人脸互换通常能够用于视频合成、提供隐私服务、肖像更换或者其余有创新性的应用。最先以前,实现人脸互换是经过分别分析二者人脸的类似信息来实现换脸,也就是经过特征点匹配来提取一张脸中例如眉毛、眼睛等特征信息而后匹配到另外一张人脸上。这种实现不须要进行训练,不须要的数据集,可是实现的比较差,没法本身修改人脸中的表情。机器学习

而在最近发展的深度学习技术中,咱们能够经过深度神经网络提取输入图像的深层信息,从而读取出其中隐含的深层特征来实现一些新奇的任务,好比风格迁移(style transfer)就是经过读取训练好的模型提取图像中的深层信息来实现风格互换。函数

也有使用神经网络进行人脸互换(face-swap),其中使用VGG网络来进行特征提取并实现人脸互换。这里咱们经过特殊的自编码器结构来实现人脸互换,而且达到不错的效果。学习

基础背景:自编码器

自编码器相似于神经网络,能够说是神经网络的一种,通过训练后可以尝试将输入复制到输出。自编码器和神经网络同样,有着隐含层测试

h,能够将输入解析成编码序列,从而复现输入。自编码器内部有一个函数
h=f(x)能够进行编码,同时也有一个函数r=g(h)实现解码,以下图所示。

TIM截图20181227172834

(自编码器的结构,x为输入,f为编码函数,将x编码为隐含特征变量h,而g为解码网络,经过将隐变量h进行重构获得输出结果r,咱们也能够看到数字2被放入编码器以后获得其隐含层的编码也就是Compressed representation,以后经过解码器从新生成出来)

现代的自编码器从宏观上也能够理解为随机映射

P_encoder​(h/x)和P_decoder​(x/h)。以前自编码器通常用于数据降维或者图像去噪。可是近年来因为神经网络的发展,更多的潜变量被研究,自编码器也被带到了生成式建模的前沿,能够用于图像生成等方面。

关于更多自编码器的知识:理解深度学习:与神经网络类似的网络-自编码器(上)

网络构架

那么应该如何经过自编码器实现咱们的换脸技术呢?

在以前咱们已经知道了自编码器能够学习输入图像的信息从而对输入图像信息进行编码并将编码信息存到隐含层中,而解码器则利用学习到的隐含层的信息从新生成以前输入的图像,可是若是咱们直接将两个不一样个体图像集的图像输入到自编码器当中会发生什么呢?

TIM截图20181227173028

如上图,假如咱们仅仅是简单地将两张不一样的脸的集合扔到自编码网络中,而后挑选一个损失函数去训练,但这样去训练咱们是什么也得不到的,所以咱们须要从新设计一下咱们的网络。

怎么设计呢?

既然咱们想要将两张脸互换,那么咱们能够设计两个不一样的解码网络,也就是使用一个编码网络去学习两张不一样人脸的共同特征,而使用两个解码器去分别生成他们。

TIM截图20181227173317

如上图,也就是咱们设计一个输入端或者说一个编码器(分别输入两个不一样的脸),而后两个输出端或者说两个解码器,这样咱们就能够经过隐含层来分别生成两张不一样的人脸了。

具体构架

这样如此的话,咱们的具体构架以下:

TIM截图20181227173606

如上图,能够看到这个自编码器有一个input端而有两个output端,在input端分别输入两个不一样的人脸集(尼古拉和川普),而后在输出端再从新生成以前的人脸,注意,这里的每一个解码器分别负责生成不一样个体的脸。而在隐含层则学习到了两个不一样个体的共同信息,从而两个不一样的解码器能够根据学习到的共同信息去还原以前输入的图像。

再具体点就是这样:

TIM截图20181227191710

如上图,咱们将两个不一样个体的图像集分别进行进行输入,此时的Encoder是同一个编码器,随后将使用同一个Encoder生成的共同的隐含信息。再利用两个不一样个体图像的解码器A、B从新生成各自的图像进行损失标准(criterion)进行比较(这里损失采用L1损失函数),经过Adam优化算法进行梯度降低。

以后咱们利用解码器A、B分别去从新生成由输入图像B、A后隐含层学习到的信息,以下公式:

TIM截图20181227192314

网络结构

下面则是以前提到的结构的具体的网络设计,咱们能够看到网络结构有一个输入端和两个输出端,输入端由卷积层和全链接层构成,而输出端则一样由卷积层构成,可是须要注意这里的输入端是下采样卷积,而输出端则是上采样卷积,也就是图像的分辨率是先变低再慢慢升高。

能够看下Pytorch中网络设计的代码:

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

        self.encoder = nn.Sequential(   # 编码网络
            _ConvLayer(3, 128),     # 3 64 64
            _ConvLayer(128, 256),   # 32/2=16
            _ConvLayer(256, 512),   # 16/2=8
            _ConvLayer(512, 1024),  # 8/2=4
            Flatten(),
            nn.Linear(1024 * 4 * 4, 1024),
            nn.Linear(1024, 1024 * 4 * 4),
            Reshape(),
            _UpScale(1024, 512),
        )

        self.decoder_A = nn.Sequential(     # 解码网络
            _UpScale(512, 256),
            _UpScale(256, 128),
            _UpScale(128, 64),
            Conv2d(64, 3, kernel_size=5, padding=1),
            nn.Sigmoid(),
        )

        self.decoder_B = nn.Sequential(
            _UpScale(512, 256),
            _UpScale(256, 128),
            _UpScale(128, 64),
            Conv2d(64, 3, kernel_size=5, padding=1),
            nn.Sigmoid(),
        )

    def forward(self, x, select='A'):
        if select == 'A':
            out = self.encoder(x)
            out = self.decoder_A(out)
        else:
            out = self.encoder(x)
            out = self.decoder_B(out)
        return out
复制代码

上述代码网络能够由下图的图例所表示:

TIM截图20181227193143

可是这些结构都是能够变化的,其中卷积核的大小能够按照需求调整,而全链接这种能够打乱空间结构的网络咱们也能够寻找相似的结构去代替。另外,在解码器阶段的最后一层还采用了Sub-pixel的上采样卷积技术(在本博客的 一边Upsample一边Convolve:Efficient Sub-pixel-convolutional-layers详解 一文中有对此技术的详细讲解)能够快速而且较好地生成图像的细节。

总之,咱们想实现换脸的操做,在总体结构不变的基础上,须要知足如下几点:

TIM截图20181227193921

如上图,也就是相似于VGG的编码网络、还要能够打乱空间结构结构的全链接网络、以及能够快速且较好地上采样图像的Sub-Pixel网络。咱们进行了额外的测试,发现做为编码网络的话,传统的VGG形式的网络结构的效果最好,可使损失函数降到最低。可是若是采用其余较为先进的网络,其效果并无传统的VGG构架好,这也是为何风格迁移和图像生成使用VGG网络格式更多一些。

TIM截图20181227194302

一样,若是咱们将全链接网络从编码器中去掉或者使用卷积网络代替,那么图像是没法正常生成的,也就是编码器学习不到任何有用的知识,可是咱们可使用1x1的卷积网络去代替全链接网络,1x1网络如:

nn.Conv2d(1024, 128, 1, 1, 0, bias=False),
nn.Conv2d(128, 1024, 1, 1, 0, bias=False),
复制代码

一样也拥有打乱空间结构的特性,优势是比全链接网络运行更快,可是效果并无全链接网络好。

至此,咱们简单说明了基本构架以及网络层的选择。

图像预处理

固然训练的时候是有不少小技巧的,由于咱们须要隐含层学习到两个不一样个体的共同特征,咱们能够采起一些小Tricks来让咱们的训练过程更快更平滑,相似于以前谈到的 浅谈深度学习训练中数据规范化(Normalization)的重要性 ,咱们要作的就是使训练的图像的分布信息尽量相近:

TIM截图20181227173928

如上图,咱们能够将图像A(川普)集加上二者图像集的平均差值(RGB三通道差值)来使两个输入图像图像的分布尽能够相近,这样咱们的损失函数曲线降低会更快些。

拿代码表示则为:

images_A += images_B.mean(axis=(0, 1, 2)) - images_A.mean(axis=(0, 1, 2))
复制代码

图像加强

固然还有图像加强技术,这个技术就没必要多说了,基本是万能的神器了。咱们能够旋转、缩放、翻转训练图像从而使图像的数量翻倍进而增长训练的效果。

TIM截图20181227195611

如上图,对于人脸来讲,使用扭曲的加强技术能够有效地下降训练过程当中的损失值,固然加强也是有限度的,不要太过,物极必反。哦对了,还忘了说,咱们训练的时候使用的脸部图像只从原图截取了包含脸部的部分,好比上图右上角的aligned image,咱们使用OpenCV库截图脸部图像做为训练素材。

图像后处理

至于图像后处理,固然是将咱们生成的脸部图像从新拼回去了:

TIM截图20181227200107

这里使用了泊松融合以及Mask边缘融合的方法,咱们能够很容易地看出融合的效果。

总结

总得来讲,这个换脸技术是一个结构简单可是知识点丰富的一个小项目,其结构简单易于使用以及修改,而且能够生成不错的效果,可是由于其拥有较多的参数,其运行速度并非很快(固然咱们能够经过改变编码层和解码层结构加快训练生成的速度),而且对于脸部有异物的图像可能会生成不真实的效果。

这是由于自编码器网络并无针对图像中须要学习的部位进行学习而是所有进行了学习,固然会有不少杂质,这能够经过注意力的机制适当改善。

TIM截图20181227200242

就说这些吧~

文章来源于OLDPAN博客,欢迎来访:Oldpan博客

欢迎关注Oldpan博客公众号,持续酝酿深度学习质量文:

相关文章
相关标签/搜索