ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

BeautyGAN详解:无配对数据下的妆容迁移原理与复现指南

BeautyGAN详解:无配对数据下的妆容迁移原理与复现指南 做图像生成这行当我这两年反复被同一个需求折磨用户想给一张自拍上一款精致的妆但市面上大多数方案要么是整图色调滤镜要么干脆生成一张不像他自己的脸。直到我认真把 BeautyGAN 这篇论文吃透才发现给一张人脸实例迁移指定妆容这件事在 2018 年就已经被解决到了相当可用的程度——而且训练时根本不需要成对的素颜/带妆照片。这篇文章我前前后后读了三遍又跑了几轮代码今天把它的设计思路、损失函数、训练细节和复现时容易踩的坑一次说清楚。1. 妆容迁移这个任务到底难在哪里1.1 它不是换个滤镜那么简单的风格迁移很多人把妆容迁移和风格迁移混为一谈。风格迁移比如把照片变成梵高油画操作的是整张图片的全局纹理和色彩分布目标是画面的画风变了。但化妆这件事是典型的 instance-level 任务针对一张具体的、活生生的人脸把口红涂在嘴唇上把眼影压在眼睑周围把腮红刷在颧骨区域其他部位额头、鼻子、皮肤质感、五官结构都不能动。这里有两个关键词值得单独拎出来说。一个是instance-level意思是模型面对的是这一张图、这一个人而不是把所有图统一套用一个网红妆滤镜。另一个是局部性妆容的作用区域是由面部语义决定的嘴唇、眼睛、脸颊各司其职颜色不能乱跑。所以一个合格的妆容迁移算法至少要满足三个基本条件妆容颜色、质感、位置要跟参考图一致换完妆后五官结构、身份信息不能变没化妆的区域必须原样保留不能把额头染成腮红。这三条限制合在一起就让调滤镜这条路彻底走不通了。1.2 最麻烦的问题没有成对的训练数据做监督学习最理想的训练数据是同一个人、同一个角度、同一个光照下素颜和带妆各拍一张的成对照片。但现实里这种数据几乎不可能大规模获取。你不可能让几百个模特在完全相同的条件下卸妆再化妆只为了拍对照图。BeautyGAN 的聪明之处在于它彻底绕开了这个约束。它只需要一个素颜人脸图库和一个带妆人脸图库两者不需要有任何对应关系。训练时随便从两个图库里各抽一张一张当源图素颜一张当参考图带妆模型就能学起来。这种对数据条件的放宽是它后续能被广泛复现和应用的前提。1.3 2018 年前后的方案有什么瓶颈在 BeautyGAN 之前传统妆容迁移方法大多基于手工特征和颜色统计。比如有人做过对嘴唇区域做直方图匹配把眼影区域的色调迁移过去这类尝试。思路是对的——把妆容当成局部颜色统计问题来处理但问题在于传统方法需要人工设计掩膜、人工指定迁移区域而且像素级的颜色重映射常常把皮肤纹理搞得很脏。后来 GAN 出现之后CycleGAN 这类模型能实现无配对的风格迁移了但它是全局域之间的映射没法精确控制局部区域。一个 CycleGAN 模型可以把整张图从素颜域切到带妆域但生成的口红可能画在嘴唇外面眼影的边界也完全不可控。BeautyGAN 就是在这样的背景下第一次把局部颜色统计 双输出架构 对抗训练整合到了一套端到端的框架里。2. 双输入双输出BeautyGAN 把换妆和卸妆放进同一个生成器2.1 一个生成器同时干上妆和卸妆两件事BeautyGAN 的生成器 G 的输入是两个源图 I_src素颜和参考图 I_ref带妆。它的输出也是两个换妆后的源图 I_src_makeup以及卸妆后的参考图 I_ref_nonmakeup。注意这里不是让你先跑一遍上妆、再拿结果跑一遍卸妆而是在一个前向传播里同时输出这两张图。很多人第一次看到这个设计会觉得奇怪为什么非要输出一张卸妆图我当时也没想明白直到把论文的损失函数读进去才理解——这个额外的输出是训练策略的关键一环。它相当于构造了一组伪配对数据(I_ref_nonmakeup, I_ref) 恰好就是参考图素颜状态和带妆状态的一对。有了这对伪配对模型就可以用监督信号去约束卸妆这个方向的合理性而上妆和卸妆又是同一个网络实现的逆向过程被约束住了正向过程自然也不会太离谱。2.2 编码器-解码器结构里发生了什么生成器的整体骨架是编码器-解码器结构。源图和参考图先分别经过若干个卷积层做编码在浅层提取各自的特征到深层之后把两路特征融合起来再通过两个解码分支分别输出两张结果图。中间通常还堆叠了残差块用来保留图像的细节信息。用个生活化的类比这个生成器就像一个既会化妆又会卸妆的造型师。给他两张照片——一张素颜、一张带妆模板——他一边对着素颜照化出一个类似模板的妆一边对着带妆模板还原出这张脸素颜的样子。两件事用的是同一套审美逻辑和操作手法互相校验。网络在训练中会慢慢意识到要想把卸妆做得好就必须对上妆的过程有充分的理解两个任务共享一套参数等于逼着模型抓住妆容的本质特征而不是死记硬背某几张图的颜色。2.3 两个判别器各管一个域BeautyGAN 的判别器不是只配一个。它有两个判别器 D_A 和 D_B分别管两个域D_A 负责判断一张图是否属于真实带妆域把生成的 I_src_makeup 和真实的带妆图分开D_B 负责判断一张图是否属于真实素颜域把生成的 I_ref_nonmakeup 和真实的素颜图分开。我用过的绝大多数 GAN 工程都是一个判别器BeautyGAN 这种双域对抗设计一开始确实让我愣了一下。但看懂了生成器的双输出结构之后这个设计就顺理成章了你既然要同时生成上妆图和卸妆图那就需要两个域的监督信号分别拉高生成质量。对抗损失只管像不像具体妆容对不对还得看后面要讲的妆容损失函数。2.4 和 CycleGAN 的思路有什么不同你可能已经想到了 CycleGAN它用两个生成器把两个域来回映射配合循环一致性损失。BeautyGAN 的不同在于——它只用一个生成器却通过双输出天然形成了循环。这样做的好处是参数更少而且两个方向的映射是严格共享表征的。更关键的是CycleGAN 做的是整图域迁移BeautyGAN 在循环之外还叠加了局部区域的显式约束所以它才敢在十分精细的妆容任务上动手。3. 妆容损失函数把化这个妆变成可计算的约束3.1 为什么光靠对抗损失不行只靠 GAN 的对抗损失模型确实能生成看起来像化了妆的图但它无法保证化的是参考图那种妆。想象一下判别器只告诉你这张图像真实带妆图但真实带妆图有成百上千种口红色号、眼影画法对抗损失只能把整体分布拉近根本管不住具体是哪一种。所以如果只用 GAN你经常会看到生成结果确实有点妆感但口红色号跟参考图完全不是一回事。BeautyGAN 的做法是把妆容这个抽象概念拆解成可计算的先验显式地写进损失里。具体来说它把脸部按照语义区域拆开对不同区域分别约束颜色分布、高层感知和像素级重建。这就是整篇论文最核心的贡献之一——妆容损失Makeup Loss。3.2 直方图匹配损失妆容本质上是一种局部颜色分布先说传统图像处理里的直方图匹配是什么。给定一张图像和一个目标颜色直方图我们可以通过像素值的重新映射让这张图的颜色统计接近目标直方图。这相当于一个全局调色步骤。BeautyGAN 把这一思路用到了局部区域上先用关键点检测把人脸解析成语义区域——嘴唇、左眼、右眼、左脸颊、右脸颊等等得到几张二值掩膜mask然后对参考图 I_ref 的每个局部区域计算颜色直方图得到一个该区域应该长成什么颜色统计的目标。接下来用直方图匹配算法把这个目标统计映射到源图区域产生一张理想的换妆结果图再要求生成器的输出在 L2 距离上接近这张目标图。这里有个值得细品的点网络并不是真的去输出直方图匹配的结果因为像素级的直方图匹配往往会破坏空间结构它只是把这个匹配结果当成一个监督信号。真正的意义在于网络学到的是如何用生成的方式实现同等程度的颜色分布迁移而不是简单地复制像素。这一步把化这个妆在数学上变成了让嘴唇区域的颜色直方图逼近参考图嘴唇区域的颜色直方图。3.3 感知损失保住这张脸还是这个人直方图匹配只约束颜色分布它不关心五官结构。如果只加这一项模型很可能把口红颜色涂到嘴唇边界外或者把腮红的红色晕到鼻梁上。这时候需要感知损失。感知损失的做法是把换妆结果 I_src_makeup 和原图 I_src 分别输入 VGG16取中间层比如 relu4_1的特征图计算特征之间的距离。这个距离衡量的是高层语义上两张图像不像。由于 VGG 的中间层特征已经具备了一定的语义抽象能力两张图如果五官位置不同、脸型不同特征距离就会很大。优化这个损失等于逼着网络在换妆的同时保持身份信息不丢失。我在实际使用中有一个很直观的感受不加感知损失的模型改完妆后经常出现五官漂移——眼睛好像被放大了一点点嘴唇的位置也微妙地偏移了。加了感知损失之后这些问题基本被压住了它的作用比想象中要大得多绝不是一个可有可无的辅助项。3.4 重建损失让卸妆回到原样让非妆容区域保持原样BeautyGAN 还有一层重建约束分成两个方向把换妆结果 I_src_makeup 再输入生成器期望得到接近原图 I_src 的卸妆图把卸妆结果 I_ref_nonmakeup 再输入生成器期望得到接近参考图 I_ref 的上妆图。这就是前面说的隐藏循环。除此之外重建损失还会用掩膜加权专门约束非妆容区域额头、鼻子、下巴、脖子这些地方无论怎么换妆都必须和原图基本一致。用数学表达就是生成的换妆图要在非妆容区域上逐像素近似于源图。这一步是为了防止模型搞出全脸染上一层粉底这种越界行为。3.5 损失项之间的平衡是调参重心整个 BeautyGAN 的优化目标可以概括为对抗损失让生成图落在目标域里解决像不像带妆/素颜妆容转移损失直方图匹配让局部颜色分布精确匹配解决化的妆对不对感知损失保持身份和结构解决人还是不是这个人重建损失约束双输出循环和背景/非妆容区域解决有没有乱动不该动的地方。这几项之间是有冲突的。对抗损失太强生成图容易出现颜色贴皮感甚至五官变形妆容损失太强结果会像把参考图的嘴唇颜色直接糊到源图嘴上缺少真实的光影过渡感知损失太重换妆力度又会变弱颜色不够明显。论文里给出了一组默认权重但我实测发现这个权重在不同数据集上并不通用后面会专门说怎么调。4. 数据集与训练没有成对数据的妆容迁移是怎么学起来的4.1 MT 数据集从互联网抓图靠关键点生成掩膜BeautyGAN 论文发布时附带了一个公开数据集常被称为 MT 数据集Makeup Transfer 数据集。里面包含大约 1115 张无妆人脸图和 2719 张带妆人脸图都是从互联网上抓取的姿态以正脸为主光照相对均匀。为了支撑局部妆容约束数据集对每张人脸都做了关键点检测并基于检测到的关键点生成面部组件的二值掩膜涵盖左眼、右眼、左脸颊、右脸颊、嘴唇等区域。注意这里用的是基于关键点生成掩膜不是像素级精细语义分割。我刚开始以为越精细越好试了一圈才发现关键点生成的粗粒度掩膜已经够用因为妆容损失关注的是区域内的颜色统计对边界精度没那么敏感。反而如果掩膜的语义类别太多训练时会引入大量噪声让模型顾此失彼。4.2 每次训练随机配对天然形成大量样本组合BeautyGAN 的训练非常自由每个 step 随机从素颜图库抽一张 I_src从带妆图库抽一张 I_ref两者完全可以是不同的人、不同的光照、不同的肤色。这种随机配对的做法不仅解决了无配对数据的问题还相当于隐式地做了数据增强——同一个 I_src 会和成百上千个不同的妆容参考图组合模型必须学会通用的妆容迁移能力而不是记住某一个特定风格。实测下来这种训练方式的泛化能力其实相当不错。只要输入的参考图是正脸、对齐后的人脸模型基本都能把妆容风格移植过去不太挑参考图。4.3 预处理人脸检测和对齐决定了一半的成败所有的训练图像都要先经过人脸检测和对齐最终裁剪成 256x256 的输入。这一步是整条流水线里最容易被轻视、影响却最大的环节。举个实际的例子如果人脸检测框偏了或者对齐后眼睛没有落在标准位置那么基于关键点生成的掩膜就会错位——嘴唇掩膜可能盖在鼻子上眼影掩膜可能覆盖到眉毛。这时候妆容损失不仅没有帮助反而在拼命教坏模型。所以我在自己跑数据的时候花了远超预期的时间在处理对齐质量上先做人脸检测再做 68 点关键点定位然后根据两眼连线旋转和缩放最后裁剪到固定尺寸。这个流程做完训练过程明显稳定了许多。4.4 训练参数配置与硬件需求论文中的模型输入是 256x256 的 RGB 图生成器主体由编码器、残差块和解码器构成判别器采用带全局和局部感受野的卷积结构。训练使用 Adam 优化器初始学习率在 2e-4 这个量级batch size 不需要很大。我自己的经验是单张 2080Ti 级别的显卡就能比较顺畅地完成整个训练流程这也是 BeautyGAN 能被大量复现的原因之一——它的硬件门槛在 GAN 模型里算是相当亲民的了。不过有一点要提醒如果你用的是 BatchNormbatch size 太小会导致统计量不稳定生成图在推理时忽明忽暗。官方实现里对归一化层的设计在不同复现版本里并不完全一致用的时候最好确认你跑的版本用的是 BatchNorm 还是 InstanceNorm这直接影响你的 batch size 怎么设。5. 复现 BeautyGAN 时最值得注意的几个细节5.1 直方图匹配的颜色空间和归一化范围这个话题我必须单独拿出来说因为我在这一步浪费了将近两天。直方图匹配看似简单实现细节却非常容易错在 RGB 空间做还是在 LAB 空间做像素归一化到 [0,1] 还是 [-1,1]生成器的最后输出是 Tanh 还是 Sigmoid这些细节只要有一个不一致匹配出来的目标图的颜色就会和参考图差一截而网络又会拼命去拟合这个有偏的目标最后整个模型学出来的妆容都是偏色的。我踩过的一个典型坑是生成器输出用了 Tanh范围 [-1,1]但直方图匹配函数内部用的是 [0,1] 的 RGB 值我没有做反归一化直接拿预处理的张量去匹配。结果就是生成图的整体色调偏向灰绿怎么调损失权重都救不回来。建议复现时在特征层面调试直方图匹配前打印一下参考图和匹配结果的均值、方差确认颜色趋势正常再往下走。5.2 掩膜生成方式要跟关键点检测器保持一致BeautyGAN 官方实现里用的是 dlib 的 68 点关键点检测来生成掩膜但不同版本的关键点检测器之间坐标和语义定义可能略有差异。比如 OpenCV 的 facemark 和 dlib 的 landmark 编号规则就不同。复现时一定不要混用训练时的掩膜是用 dlib 生成推理时也得用同一套管线。否则会出现训练时掩膜和推理时掩膜语义不一致模型的局部约束直接失效。另外一个效率上的建议掩膜生成可以和训练数据预处理分开做先离线把数据集的掩膜全部算好存成文件训练时直接读。不要每次迭代都现场跑一次关键点检测——那会让训练慢到让人怀疑人生。5.3 损失权重的设置思路先跑通对抗再加妆容约束我复现过好几版 BeautyGAN有一个非常有用的训练策略不要一开始就把所有损失项全部加上而是分阶段来。第一阶段只开对抗损失和重建损失关掉感知损失和直方图匹配损失跑几十个 step确认生成器能生成结构正确、色彩范围合理的图第二阶段加上直方图匹配损失观察妆容区域的色彩是否开始向参考图靠拢第三阶段再加入感知损失重点观察人脸身份是否保持。为什么要这样因为如果一上来所有损失一起加你根本分不清是哪个环节出了问题颜色不像是直方图匹配的锅五官变形像感知损失的锅但各损失项在反向传播时会互相干扰排错难度极大。分阶段加损失相当于给每个模块单独做验证。等你确认每一项都正常工作之后再一起训练调权重。5.4 判别器感受野和生成质量的关系BeautyGAN 的判别器是 PatchGAN 风格的也就是说它不是一个输出单一真假值而是对图像的多个局部patch分别判断真假。patch 的尺寸决定了判别器的感受野感受野太小判别器只能看到纹理细节管不住整体结构感受野太大又容易忽略局部妆容的精细度。从我的实践看patch 大小对妆容效果的影响相当直观patch 偏大时生成图容易出现色块感妆容和皮肤界限生硬patch 偏小时生成图会更细腻但可能出现局部纹理震荡。官方实现里的 patch 设置是经过调优的建议先用默认参数跑通再根据自己的任务微调。盲目改大或改小 patch 都会让效果明显变差这也是一个容易白费功夫的隐形参数。5.5 推理阶段 BatchNorm 统计量波动的问题这个问题我印象太深了。有一版复现模型在训练时效果很好但一到推理阶段单张图喂进去出来的结果忽亮忽暗同一张图换一张参考图亮度也会跳变。查了很久才发现是 BatchNorm 在推理时的统计量问题。训练时模型见过整个 batch 的均值方差但推理时如果 batch1BN 的 running stats 和当前输入的统计量差异变大就会导致颜色不稳定。我的建议是如果代码里用的是 BatchNorm推理时可以改成使用训练阶段累计的 running mean 和 running variancePyTorch 里默认的 eval() 模式就是这么做的或者在训练时增大 batch size 让 running stats 更稳。最彻底的方案是换成 InstanceNorm。BeautyGAN 的官方实现里用了哪种归一化不同复现版本不一样建议跑之前先看清楚。5.6 明显的翻车场景肤色差异极大的参考图很多新手复现后第一个翻车案例是拿一张深肤色源图配一张浅肤色参考图或者反过来结果生成的脸颊和嘴唇发灰、发暗。这其实是直方图匹配和感知损失的天然冲突直方图匹配想让局部颜色逼近参考图感知损失又要求保持身份当两者目标严重矛盾时模型只能折中折中的结果就是颜色灰蒙蒙的。处理手段有两个方向一是适当降低妆容损失权重优先保住人脸自然度二是对参考图先做一次肤色粗校准让参考图的面部肤色向源图靠拢再进行颜色直方图匹配。我自己实践下来第二种方法的视觉效果好得多而且几乎不需要改网络结构。6. 从 BeautyGAN 到今天妆容编辑方向留下了什么6.1 它树立的范式至今还在被沿用BeautyGAN 这套局部语义掩膜 颜色直方图统计 感知身份约束 循环重建的配方后来几乎成了妆容相关生成任务的标准范式。直接对标它的后续工作一只手数不过来比如 PSGAN 引入了空间感知机制来应对源图和参考图姿态不一致的问题LADN 用局部对抗解耦支持了只改眼影不动口红这种细粒度编辑EleGANt 则在多尺度注意力上做文章。即使到了 StyleGAN 主导的时期不少工作做妆容编辑时依然会把 BeautyGAN 当作效果对比的基线。6.2 从论文里学到的通用方法论抛开妆容这个具体任务BeautyGAN 给我最大的启发其实有三点任务数据不是成对的情况下不要急着想办法标注先想能不能用循环结构 先验约束自己造监督信号。BeautyGAN 用一个生成器同时输出两个方向的图就凑出了伪配对数据这是很妙的工程思维。局部区域任务直接把位置信息以掩膜形式喂给网络让网络不用自己摸索口红长在哪儿。这种把先验知识显式注入的做法在工程里远比让网络从零学要稳。对抗损失负责解决像不像辅助损失负责解决对不对两者缺一不可。如果你发现某个 GAN 生成结果看起来没问题但内容不对问题大概率不在 GAN 本身而在缺少一个任务层面的显式约束。6.3 局限性和我的个人体会BeautyGAN 的局限也很明显它只能做整脸的风格迁移不能单独指定只化口红妆容结果偶尔会出现贴在皮肤表面的塑料感缺少真实化妆那种光影融合对大角度侧脸和夸张表情的鲁棒性也不够好。这些限制在它当时的背景下可以理解也给了后续工作很大的改进空间。最后再说一点个人感受。我复现 BeautyGAN 最大的收获不是它生成效果有多惊艳——平心而论今天很多基于扩散模型的方法效果已经明显超过它了——而是理解了一个道理在没有成对数据、又要做精细局部变换的任务里先验知识的人工设计比模型结构本身更能决定效果上限。如果你手头也有类似的项目不妨先把你对任务的常识尽可能翻译成可计算的损失约束再谈调模型。BeautyGAN 就是这样一个把常识拆解得明明白白的典型案例到现在我都还愿意把它推荐给每一个刚接触图像编辑方向的朋友。
返回列表