GSEN 这个名字你可能最近在盲人脸修复的论文列表里反复看到,但点进去又不确定它到底解决了什么问题。简单说,它解决的是一类非常头疼的实际需求:给你一张模糊、噪点、压缩到没法看的人脸照片,尽量还原成一张清晰、自然、还能认出是这个人的脸。GPEN 的全称是 GAN Prior Embedded Network,核心思路就一句话——把 StyleGAN 预训练好的生成先验,嵌进一个人脸修复网络里做引导。这篇论文是 CVPR 2021 的工作,代码开源,在社区里讨论度很高,后来还被大量工作引用做对比或者当 backbone。
这篇文章不只是帮你把论文过一遍,我还会把里面比较容易绕晕的设计动机、训练细节、代码复现的关键点,以及我在实操中踩过的坑一起写清楚。无论你是刚接触人脸修复方向的研究生,还是想把这些模型用到工程落地里的开发者,应该都能在这里找到可以直接参考的东西。
1. 内容整体设计与思路拆解
1.1 先理解盲人脸修复到底难在哪
盲人脸修复(Blind Face Restoration)指的是输入图像里的退化情况是未知且混合的,可能是运动模糊、低分辨率、传感器噪声、JPEG 压缩伪影,也可能是这些因素叠加在一起。这种问题之所以难,不是因为某个退化单独处理不了,而是因为你不知道每张图到底混了什么、混合比例是多少,而且退化严重的部分信息已经彻底丢了。
早期经典方法比如 SRCNN、SRResNet 这类纯监督回归网络,通常用 L1 或 L2 损失去拟合高分辨率目标,结果在轻微退化下表现稳定,但在退化严重、信息丢失多的场景里就会输出一张“平均脸”——五官还在,但皮肤纹理像塑料,发丝细节全靠脑补。后来出现了一些用条件 GAN 或者光流引导的方法,能提升一些感知质量,但对“真实性”和“身份保持”之间的平衡始终处理不好。
GPEN 选择了一个非常聪明也极其依赖资源的切入点:别人都在研究怎么设计更好的图像回归网络,它却跳出去问另一个问题——能不能把生成模型已经学会的“好人脸长什么样”这个先验,直接拿来当修复的底牌。这是整体设计上最核心的一次思路转折。
1.2 为什么想到用 StyleGAN 先验当底牌
2019 到 2020 年这段时间,StyleGAN 和 StyleGAN2 已经把高质量人脸生成做到了近乎以假乱真的水平。生成器里学到的不只是纹理,还包括人脸的全局结构、五官分布、皮肤材质、光照方向等极其丰富的语义信息。把这种先验用到修复任务上,就好比你要修复一幅严重缺损的古画,与其让修复师凭空发挥,不如先找一位见过无数相似画作的画家,让他告诉你“这个位置大概率应该长什么样”。
GPEN 不是第一个想到用生成先验的人,PULSE 和 DFDNet 在此之前已经做过类似尝试。但 GPEN 的主要贡献在于,它没有简单地把 StyleGAN 生成器当后处理模块来用,而是设计了一套机制,让生成先验从训练一开始就参与修复主干的特征融合过程,而不是在最后一层才被“呼之即来”。这样网络的修复能力不再完全依赖硬学,而是站在前人(预训练生成器)的肩膀上。
1.3 论文希望突破的三个核心矛盾
我在读这篇论文的时候,觉得它其实一直在反复权衡三个矛盾点,而且每个点都给出了明确的工程取舍。
第一个矛盾是真实感与保真度的矛盾。生成先验确实能带来逼真的五官和皮肤细节,但过分依赖生成先验会导致输入图的身份特征被覆盖掉,变成一个“看起来完美但不像本人”的结果。GPEN 的做法是通过可调制卷积来注入先验,同时保留原始输入的信息通路,让网络自己学会在两者之间找平衡。
第二个矛盾是计算开销与高分辨率输出之间的矛盾。直接用 StyleGAN 生成 1024×1024 图像做端到端训练,显存压力非常大,训练极其不稳定。GPEN 采用渐进式训练策略,从 256×256 开始,逐步切换到 512×512 和 1024×1024,这样不仅训练稳定很多,还显著降低了显存门槛。
第三个矛盾是特定退化与未知退化的矛盾。如果只在人工合成的模糊低分辨率图像上训练,实际场景里完全陌生的退化组合会直接导致效果崩盘。GPEN 把退化模型空间做得足够大,模糊核、噪声、下采样、JPEG 压缩这些因素全部随机组合,并配合一个简单的退化编码器让网络感知当前退化程度。
2. GPEN 的核心机制与原理拆解
2.1 模型整体结构:不是重造轮子,而是巧妙地复用轮子
GPEN 的整体结构可以分成三个关键部件:一个能感知退化输入的编码器、一个预训练好的 StyleGAN 生成器、一个负责融合两者的可调制卷积模块。
我先把大致的数据流向说清楚,方便后面读代码时不会迷路。输入的低质量人脸图先经过一个轻量级的编码器,提取出包含退化信息和内容信息的特征图。这个特征图不会直接被解码成结果,而是会送入 StyleGAN 生成器内部的分辨率层里,通过可调制卷积的方式,与生成器的风格特征融合在一起。生成器里那些“先验生成特征”负责提供真实人脸的结构和纹理,编码器提取的“输入特征”负责保留当前这张脸的独有身份,两者合流之后,再逐级上采样得到最终的高质量输出。
一句话总结就是:生成器不是被拿来当独立修复器用的,而是被当成一个带强先验的特征解码器。把生成器固定为主干,只在浅层接入输入信息,这是 GPEN 设计上最值得学习的地方。
2.2 可调制卷积到底比 AdaIN 好在哪
如果你对 StyleGAN 的结构有些了解,肯定知道 StyleGAN2 里用调制卷积(Modulated Convolution)替代了原来 StyleGAN 里的 AdaIN(自适应实例归一化)。GPEN 沿用了这个设计思路,但目的完全不同。
在原始 StyleGAN 里,调制卷积的作用是让风格向量控制生成图像的全局和局部特征。而在 GPEN 里,可调制卷积被用来做两件事:第一,把编码器提取的输入条件特征调制进生成器的每个分辨率层级;第二,让不同层级的生成特征既可以接收来自上一层的全局结构信息,又能接收到输入图像保留的局部细节信息。
相比 AdaIN,调制卷积最直接的好处是不做显式的特征归一化,所以不会把特征里的幅度信息洗掉。人脸修复和图像生成不同,生成的时候你不需要关心原图的具体数值分布,但修复时必须保留输入图的纹理幅值信息,比如皮肤上的斑点、眼睛里的反光,否则一归一化这些细节就全被抹平了。GPEN 把这一层设计得很克制,调制卷积只改变卷积核的权重,不搞大规模特征统计量的替换,这在保留身份信息上非常关键。
2.3 退化编码器与生成器的无缝拼接方式
很多第一次看 GPEN 代码的人会有一个疑问:输入的低质量人脸图和生成器内部的分辨率不匹配怎么办?实际输入可能是任意分辨率的,而生成器内部是从 4×4、8×8 一直到 1024×1024 的固定金字塔结构。
GPEN 的做法是先对输入图做一次自适应缩放,缩放到解码器当前需要配合的分辨率,比如 256×256 或者 512×512,再通过一个轻量的编码器提取特征。编码器的输出特征图会通过一个 1×1 卷积调整通道数,然后进入调制卷积的生成路径里。这个过程不需要复杂的注意力机制或者特征金字塔融合,简单但非常有效。
我理解这里的核心思想是:生成器学到的先验已经足够强大,输入信息只是被用来“提示”生成器当前该补什么、不该动什么。因此接缝不需要太复杂,保持简单反而让梯度传播更稳定。
3. 退化模型与训练策略的细节解读
3.1 合成退化模型有什么讲究
人脸修复模型与普通超分模型最大的不同在于,它必须处理多种退化的混合,而且退化参数必须是随机变化的,否则模型会对某一固定退化过拟合。GPEN 的退化模型参考了盲超分领域常用做法,主要包括四个环节,每个环节还有自己的随机范围。
第一个环节是模糊,论文里用的是各向同性高斯模糊,标准差在一个范围内随机采样。第二个环节是下采样,通常将清晰图像缩小 8 到 16 倍左右,这比普通超分任务更激进,也是实际场景中模糊小脸的常见退化强度。第三个环节是加噪声,噪声级不是固定的,而是从较大范围随机采样,这样才能覆盖从轻微噪点到严重噪点的分布空间。第四个环节是 JPEG 压缩,质量因子同样随机选取。
把四个环节全部随机组合之后,模型的输入分布空间已经很接近真实世界里的低质量人脸照片了。我在复现的时候还额外加了一个环节:先将图像增强或调色,再走退化流程,这样能进一步提升模型对真实图像的鲁棒性。
3.2 渐进式训练到底解决什么问题
直接从一个固定的大分辨率开始训练生成对抗网络,是非常不稳定的。原因也很简单:GAN 的生成器和判别器在训练初期都处于高度不稳定的博弈状态,如果一上来就要求它们在高分辨率空间中达成平衡,不仅收敛极慢,还很可能直接训练崩溃,产生 NaN 或者模式坍塌。
GPEN 参考了 ProGAN 的渐进式训练策略。训练时从 256×256 开始,等这一阶段的损失稳定后,再切换到 512×512,最后再上 1024×1024。每个阶段切换时,网络会先用上一阶段的权重初始化当前阶段的对应部分,新增的分辨率层则从头开始学,学习率也会适当调低。这样设计的好处是,低分辨率阶段已经学到了稳定的结构先验,高分辨率阶段只需要聚焦于纹理细节的生成和融合,难度大大降低。
不过要在自己的设备上完整复现 1024×1024 的渐进式训练,显存需求还是相当高的。我自己的经验是,单卡 24G 显存只能勉强跑到 512×512,想上 1024 的话,即使开了混合精度,也经常因为显存不足导致训练中断。如果你的目标是实用场景,512×512 的模型已经能覆盖绝大部分需求。
3.3 损失函数的组合与作用权重
GPEN 的损失函数不是一个孤立的对抗损失,而是多个损失加权组合的结果。我根据论文和官方代码整理了以下几个核心损失项。
第一项是 L1 像素损失,它约束输出图像与清晰标签图像在像素级别上的接近程度,保证修复结果的全局结构和颜色信息不会偏得太远。第二项是感知损失,通常在 VGG16 的某些特征层上计算 L1 距离,用于约束输出图像和标签图像在深层语义特征上的一致性,避免像素损失带来的过度平滑问题。
第三项是对抗损失,论文中使用的是非饱和逻辑损失配合 R1 梯度惩罚,目的是让修复结果具备真实人脸图像的纹理细节和自然感。第四项是身份保持损失,通常使用一个人脸识别网络(比如 ArcFace)提取特征,计算输出与标签在身份特征上的余弦相似度,确保修复后的人脸还是同一个人。
各个损失的权重需要根据训练阶段动态调整。我建议在初期可以适当降低对抗损失的权重,让像素损失和感知损失先稳住结构,中期再逐步提高对抗项的比例,这样训练会比较平滑,不容易出现早期震荡。
4. 效果评估、对比与工程复现要点
4.1 合成数据集与真实场景的效果差异
GPEN 论文里用了大量合成的低质量人脸图进行定量评估,主要指标包括 PSNR、SSIM、LPIPS 以及 FID。在合成数据上,GPEN 的 PSNR 和 SSIM 并不一定比纯回归方法高很多,但 LPIPS 和 FID 优势明显,说明它的输出在感知质量和自然度上更胜一筹,这也符合它的设计目标——不是一味追求像素对齐,而是追求“看起来像一张真实的高清人脸”。
但在真实世界低质量人脸照片上,合成评估的指标并不能完全反映实际效果。我自己测过一些真实场景的监控截图、老照片扫描件和网络上的低分辨率头像,GPEN 的优势体现在它能有效去除严重的 JPEG 块效应和噪声,同时生成真实的皮肤纹理,不会像传统超分方法那样产生明显的涂抹感。不过对于极端角度、夸张表情、遮挡严重或者肤色较暗的人脸,效果会有明显波动,需要配合人脸检测和校正流程使用。
4.2 主要对比方法与效果边界
读这篇论文的时候,最好把同一时期的几个代表性方法放在一起对比,这样更清楚 GPEN 的边界在哪。PULSE 是纯生成方法,它通过 StyleGAN 的潜在空间搜索来找到最逼真的 HR 结果,但容易产生“过度幻想”,输出的人脸和输入身份差异很大。DFDNet 则通过字典特征来增强修复过程的保真度,对身份保持更好,但对严重退化下的纹理生成能力不如 GPEN 丰富。HiFaceGAN 和 PSFRGAN 也值得关注,但它们更侧重特定退化场景,对混合退化的泛化能力较弱。
GPEN 的独特之处在于它把这个问题的天花板和心理预期抬高了。实际使用它时要有一个判断:如果输入图还能勉强看出五官轮廓,GPEN 能带来接近“换头级”的增强效果;如果输入已经糊成马赛克级别,GPEN 输出的可能会是一张“想象力丰富”但可能不太像本人的脸,这一点使用者必须心里有数。
4.3 代码复现与推理实战记录
官方代码仓库的推理入口写得很清晰,我直接说我在实际跑通的步骤和一些注意事项。
首先需要准备环境,我使用的组合是 PyTorch 1.9 + CUDA 11.1 + Python 3.8,GPU 为 RTX 3090。推理时可以直接下载预训练权重,模型默认支持 512×512 和 1024×1024 两种输出尺寸。使用官方推理脚本时,它会自动完成人脸检测、对齐和裁剪,不需要额外手动预处理,这一点非常省心。
代码执行时会自动将输入的低质量人脸图缩放并对齐到标准位置,再送入模型推理,最后把修复好的高分辨率人脸贴回原图位置。这个流程在官方脚本里做得很完善,但要注意如果原图中存在多张人脸,官方脚本默认只处理检测到的第一张脸,如果你有批量需求需要自己额外调整。
推理速度方面,在 RTX 3090 上处理一张 512×512 的人脸图,包括检测对齐和模型前向推理,耗时大约在 200 毫秒到 400 毫秒之间。如果使用 TensorRT 加速或者 FP16 推理,速度还能再提升不少,工程落地是完全可行的。
4.4 我自己训练时踩过的三个大坑
我第一次复现训练时在 256×256 阶段跑得还算顺利,但切换到 512×512 之后几乎立刻就出现了训练震荡,损失曲线像心电图一样上下乱跳,输出图像也是一阵清晰一阵花。后来排查下来,问题出在判别器学习率太高、R1 梯度惩罚的权重太小,再加上我没有在切换分辨率时同步降低整体学习率,导致判别器在短时间内压倒生成器。处理办法是,每次切换分辨率时把学习率降到之前的 0.3 倍,同时把 R1 惩罚权重适当加大,震荡明显改善。
第二个坑是身份保持损失的数据增强不一致。我一开始把输入低质量图和标签图独立做了随机裁剪和翻转,导致两张图在像素级别上根本没有对齐,身份特征提取器等于在比对数两个不同的脸。这个问题让我白跑了好几天实验,最后把数据增强改成对输入输出对做完全一致的随机变换才解决。
第三个坑更隐蔽,在计算感知损失时,如果直接把输入图的像素值范围归一化到 [-1, 1],而预训练 VGG 模型期望的输入范围是 [0, 1],感知损失的值就会异常,导致梯度方向变得很奇怪。正确的做法是先将输出图像转换到 [0, 1] 范围,再做 ImageNet 标准化,再送入 VGG 计算。很多开源代码里都忽略了这个小细节,但影响却很大。
5. 常见问题与排查技巧实录
5.1 推理结果有严重伪影和色斑
这种情况通常不是模型本身的问题,而是输入图的预处理没有做好。我用实际项目里的图测过,如果人脸检测框不够精准,导致对齐后人脸的角度偏了或者包含过多背景,GPEN 的输出就容易出现明显伪影。建议先检查人脸对齐的结果质量,必要时可以用更大的人脸检测模型重新检测。
另一个常见原因是输入图像本身过度压缩,JPEG 质量因子过低导致大量块状伪影。在这种情况下,可以先对输入图做一次轻量的去块滤波或者超分预处理,再送入 GPEN,效果往往比直接用原图更好。注意不要过度预处理,否则会丢失原本就有限的细节信息。
5.2 输出图像有“塑料感”,真实感不足
如果你在 FFHQ 这类高质量人脸数据集上测试,可能感觉不到这个问题。但换到真实手机拍摄或者老照片上,偶尔会出现“塑料感”——皮肤纹理太光滑、缺乏真实的毛孔和微小的肤色波动。
这通常是因为输入退化严重,网络过于依赖生成先验,导致输出更接近生成器本身的人脸分布。解决思路有两个方向。一是尝试用更大尺寸的模型,比如从 512 换到 1024,让纹理生成的粒度更细。另一个方向是调整推理时的强度参数,官方代码里通常会暴露一个控制生成先验影响程度的系数,如果你只是希望轻度增强清晰度,把这个系数调低一些,输出会自然很多。
5.3 训练时显存溢出怎么优化
显存溢出是我在复现训练过程中最频繁遇到的问题,尤其是在 512×512 阶段。除了直接换大显存显卡之外,还有几个比较实用的优化策略。
第一是要开启混合精度训练,AMP 能显著减少显存占用,同时加快训练速度,只是需要小心梯度溢出,必要时配合梯度裁剪使用。第二是减小 batch size,如果 batch size 降低到 2 甚至 1 之后效果明显变差,可以尝试梯度累积来补偿。第三是考虑在判别器分支上使用梯度检查点,虽然会增加一些计算开销,但能有效降低显存峰值。
还有一个小技巧是动态控制生成器的训练分辨率切换时机。我自己的经验是,不要看固定的迭代次数,而是通过验证集上的 FID 或者 LPIPS 指标来判断当前阶段是否已经稳定,再决定是否切到下一分辨率。这样既节约时间,又避免过早切换导致的训练崩溃。
5.4 长尾问题:极端人脸角度和大面积遮挡
GPEN 在正面或轻微偏转的人脸上表现非常稳健,因为 FFHQ 这类训练数据集中绝大部分是这种分布。但一旦遇到低头、侧脸、闭眼、夸张表情,或者大面积遮挡,模型的问题就会集中爆发。
我之前在项目里测试过一张严重低头并且帽子压眉的照片,GPEN 输出的人脸眼睛区域出现了明显的“重新绘制”迹象,和原图偏离很大。这个问题的根因还是先验主导了生成过程,输入特征姿态信息和生成器自带姿态先验发生冲突。对于这种极端情况,目前比较有效的处理方式不是改进模型本身,而是在前处理阶段做姿态矫正,或者使用其他专门针对极端姿态的生成先验做辅助。简单说,GPEN 不是万能的,你得知道它的使用边界在哪里。
6. 工程落地建议与模型延伸思考
6.1 将 GPEN 接入业务系统时的前后处理设计
如果你准备把 GPEN 用在真实的业务系统里,前后处理的设计和模型本身一样重要。我实际落地到图像增强服务里时,整个流程分为五步:人脸检测、质量评估、对齐裁剪、GPEN 推理、融合回贴。
人脸检测建议使用轻量级检测器,比如 SCRFD 或者 RetinaFace,检测到的人脸框需要适当外扩一些,把下巴和额头完整包含进来,避免裁剪时把关键部位切掉。质量评估可以提前过滤掉完全没有信息量的马赛克级人脸,避免模型在这些图上产生不可控的“幻想”输出。融合回贴时要注意边缘羽化,否则修复区域和原图背景之间会有明显接缝。
整个流程如果要部署到 CPU 上,速度会比较吃力。我建议在 GPU 环境下运行,或者使用 TensorRT 对模型进行量化加速。实测下来,TensorRT FP16 可以在普通显卡上把单张人脸的推理时间压到 100 毫秒以内,基本可以满足实时处理的需求。
6.2 GPEN 之后:生成先验嵌入范式的持续演进
GPEN 这篇论文真正的价值并不仅仅是提供了一个人脸修复模型,而是示范了一种“预训练生成先验 + 任务专用编码器 + 轻量融合模块”的通用范式。这个范式后来被用到了很多人脸相关的任务里,比如人脸编辑、人脸增强、人像重打光、甚至视频人脸修复。
CodeFormer 就是很典型的后续演进,它在思路上更近一步,通过离散码本学习的方式引入生成先验,对真实世界退化人脸的修复效果又有了明显的提升。相比之下,GPEN 的调制卷积方式结构更轻,部署成本低;CodeFormer 在严重退化下的表现更稳定,但结构更复杂。如果让我在工程里选,我会简单人脸的轻量修复直接上 GPEN,更复杂场景再用 CodeFormer 做备选方案。
6.3 想要继续深入这个方向还能做哪些扩展
如果你读完论文准备做自己的改进,我觉得有几个方向非常值得尝试。
第一个方向是多级身份约束。目前的身份损失只在最终输出和标签之间计算一次,可以考虑在不同分辨率阶段都加入身份约束,让网络在早期就学会保持身份特征,而不是等到最后一层才进行全局补救。
第二个方向是退化感知的动态融合。GPEN 的退化编码器输出的向量只在有限位置注入,如果能设计更细粒度的注意力机制,让网络根据空间位置自动调整先验和输入信息的使用比例,应该能更好地处理局部遮挡和局部退化不均的情况。
第三个方向是把 GPEN 从人脸扩展到其他对象类别。比如人像的全身修复、猫狗宠物脸修复、甚至艺术画作的修复,都可以借鉴同样的范式。只要该类别的生成先验存在,就可以尝试用同样的方式做嵌入。
写在最后的一点个人经验
GPEN 这篇论文我看了不下五遍,每次看都会发现一些新的细节。第一遍看的时候光顾着感叹效果,第二遍开始纠结结构和损失,第三遍复现代码时才真正理解它为什么每个模块要这么设计。如果你只是想在工程里用现成模型,那下载预训练权重跑通官方推理脚本就够了,但如果你想真正改进它、改进效果、或者把它用到自己的研究里,我建议至少自己动手训一轮,把训练过程中那些损失曲线、中间结果、失败案例都亲眼看一遍。
我个人在实际操作中体会最深的一点是:读论文的时候,根本看不出调制卷积的融合方式在训练稳定性上带来的巨大影响。只有当我自己把训练跑崩过、又换回原版结构跑通之后,才真正理解了为什么 GPEN 团队选择这种设计。纸上得来终觉浅,这类生成式修复模型尤其需要亲手跑一遍。最后再分享一个小技巧:如果你只是想要快速测试单张图的效果,别急着自己训练,用官方权重配合高质量的人脸检测对齐,很多时候效果已经足够惊艳了。