1. 先聊一聊:为什么人脸修复到了GPEN这里才算“能用”
做图像修复、老照片还原或者视频增强的朋友,应该都经历过这种折磨:拿一张百年前斑驳的人像照或一张监控里的低分辨率人脸,跑传统超分算法,出来的图是“清晰了”,但五官完全是磨皮过度的假塑料感;跑GAN类的重建方法,细节倒是出来了,可人脸经常直接给你换了一张脸,身份都对不上。
GPEN就是在这个背景下让我比较服气的一篇工作。它的全称是GAN Prior Embedded Network for Blind Face Restoration in the Wild,2021年发表在CVPR上,作者包括陶大程参与的团队。核心思路一句话就能讲明白:把预训练好的StyleGAN v2的生成先验,嵌入到一个U-Net结构的编解码网络里,让网络既保留输入图像的结构信息,又利用GAN先验补充真实细腻的皮肤纹理和五官细节。
这篇论文我前后读了两遍,第一遍走马观花,第二遍对着源码一行一行抠,才真正把作者的设计意图理清楚。这篇文章就当作一份带注释的阅读笔记,把我踩过的坑、疑惑过的设计、实验里学到的细节都写出来。适合正在做人脸修复、老照片增强的同学,也适合准备入门GAN先验方向的人,至少能省下你啃原论文和源码的不少时间。
2. 盲人脸修复到底难在哪,为什么说这是“盲”
2.1 退化模型与真实世界的差距
盲人脸修复里的“盲”,指的是输入端退化未知。经典假设是高分辨率清晰图像经过模糊、下采样、加噪之后变成低质量图,公式长这样:
y = (x ⊗ k) ↓s + n
其中x是清晰原图,k是模糊核,↓s是s倍下采样,n是噪声。这个模型本身没问题,但真实场景远比它复杂:脸上的高频细节、皮肤毛孔、发丝,在退化过程中基本被抹平了;监控相机拍出来的人脸往往还带运动模糊、偏色、压缩伪影,各种问题叠加在一起。退化的多样性导致很难用一个固定的逆过程去恢复。
2.2 三条技术路线的“此消彼长”
在GPEN之前,主流方法大概分三类,我第一次接触时完全被绕晕,后来用一句话分别概括:
基于先验重建的方法,代表是PULSE和mGANprior。这类方法假设人脸在StyleGAN的潜空间里有一个对应的潜码,通过优化潜码让生成图的下采样结果逼近输入图。优点是真�质感好,缺点是优化不稳定,容易找到一个“五官长得不一样但下采样后很像”的潜码,身份漂移问题非常严重。
基于编解码器的方法,代表是HiFaceGAN。这类方法直接训练一个CNN网络从退化图回归清晰图,结构信息保持得不错,但高频细节往往不足,输出偏平滑,缺少真实的皮肤纹理。
基于GAN判别器的端到端方法,比如GFP-GAN和PSFRGAN。它们在编解码器基础上加判别器,用对抗损失逼迫输出更真实,效果已经不错,但对高频噪声的抑制、对真实退化图像的泛化仍有明显短板。
2.3 GPEN的设计取舍:所有路线都“吃一半”
GPEN的高明之处在于它没有站队,而是把三类方法的优点各取一部分。网络主体仍是编解码器,负责内容重建;解码器阶段引入预训练StyleGAN的合成权重,提供高保真的人脸先验;训练时再用多尺度判别器加对抗损失做兜底,保证纹理真实感。后面这几节就逐个拆解这些设计背后的逻辑。
3. 核心机制拆解:GAN先验到底是怎么“嵌”进去的
3.1 为什么要用StyleGAN的先验,而不是自己训一个生成器
人脸图像的特殊性在于,它高度结构化:双眼、鼻、嘴、颧骨都有固定位置和形态。一张退化严重的人脸,信息已经损失太多,不可能完全靠网络“算”出来,必须借助人脸数据集里统计出的分布知识来“猜”。StyleGAN v2在FFHQ上训练完成后,其生成网络内部每一层特征图都编码了丰富的五官结构信息和皮肤纹理统计规律。
直接拿高斯噪声输入StyleGAN去生成目标人脸是不可行的,因为退化图里其实保留了大量身份信息,关键是找到一个方式让网络“看”到退化图的同时,又能利用StyleGAN内部的先验知识。GPEN的做法是:编码器提取退化图的特征,然后把特征注入StyleGAN的合成层的输入位置,相当于把StyleGAN从“从噪声生成人脸”改造成“从退化特征生成清晰人脸”。
3.2 主干网络与GAN先验的衔接方式
具体到网络结构上,GPEN主体是一个U-Net,编码器部分从退化图提取多尺度特征,解码器部分使用了预训练StyleGAN v2的合成网络参数。这个“嵌入”不是简单地把两个网络拼在一起,而是做了残差式的融合。
在StyleGAN的每个合成块中,来自编码器的粗糙特征会先经过一个卷积调制,再与StyleGAN自身的调制特征相加,然后继续向上采样。这样做有个最直接的好处:编码器提供的结构信息和StyleGAN提供的纹理先验互不干扰,网络不需要从零开始学习如何生成人脸,只需要学会“该往StyleGAN的特征里补什么”。
我用个生活化的类比:这就像你让一位素描老师(编码器)先画出人脸的轮廓和明暗关系,再让一位油画大师(StyleGAN先验)在素描底稿上补上细腻的皮肤质感和光影。如果只让油画大师凭空画,他根本不知道画的是谁;如果只让素描老师画,那永远只是素描,没有油画的真实质感。
3.3 为什么只用了StyleGAN的深层,编码器没沾先验
我第一次看结构图时产生过一个疑问:编码器为什么不用预训练权重?作者的解释是,编码器的任务是提取退化图的语义特征,这是一个通用的表示学习问题,用普通CNN训练反而更灵活;而解码器面对的任务是“无中生有”地补充细节,必须依赖先验知识。
这个设计也和训练数据有关。训练时用高清人像图人工合成退化图,网络需要学会从退化图反推清晰图。编码器如果冻结了StyleGAN的权重,反而会限制它对不同退化类型的适应能力,毕竟StyleGAN的中间特征是为“生成”服务的,不是为“识别退化”服务的。
3.4 噪声调制模块:对抗频率域的脏东西
论文里另一个容易被忽略的模块是噪声调制模块(Noise Modulation Module)。代码端到端训练时我发现,如果直接把噪声图和其他输入一起送入网络,生成结果在高频区域容易出现彩色条纹或水波纹一样的伪影,这在真实老照片里尤其明显。
作者的处理方式是:把输入图像的高频噪声单独提取出来,拆成不同频率的噪声分量,然后预测一组通道级的缩放因子和偏移量,用它们对解码器特征做调制。这相当于模型自主学习:哪些频率的噪声是真实图像该有的,哪些是污染,然后有选择地抑制。
写代码时这个模块的实现很简单,本质上就是几层卷积加一个sigmoid输出作为门控。但它解决的问题非常实际,没有这个分支时,生成结果在皮肤区域经常出现不自然的斑块,加了之后干净很多。做工程的朋友可以重点关注这块,它和很多传统去噪算法的思路其实是相通的,只是做成了可学习的形式。
4. 设计细节里的门道:判别器、损失函数和训练策略
4.1 多尺度判别器为什么要“多尺度”
GPEN的判别器采用了多尺度结构,也就是在多个分辨率下分别判断图像真假。这样做的目的有二:一是大尺度判别器关注整体结构和语义合理性,防止人脸畸形;二是小尺度判别器关注局部纹理真假,提升皮肤毛孔、睫毛等细节的真实度。
只用一个高分辨率判别器在训练早期很容易崩塌,因为生成器发现靠“锐化边缘”就能骗过判别器,舍不得花精力学真实的纹理分布。多尺度判别器相当于拓宽了监督信号的来源,哪个尺度出问题都能及时反馈给生成器。
4.2 非负L1损失、感知损失和对抗损失的分工
GPEN的损失函数由四部分组成:
- 非负L1损失:保证重建图像与GT的结构一致。之所以强调“非负”,是担心生成器在L1梯度过小时产生偏向。读完源码我才发现,实际实现中非负损失就是一个ReLU之后的标准L1,设计目的更多是为了稳定训练。
- 感知损失:用VGG网络的特征空间衡量图像相似度。L1是在像素级约束,感知损失是在语义特征级约束,两者结合能保证输出既像素接近又语义合理。
- 对抗损失:逼迫输出图像分布接近真实人脸分布,负责最终纹理质量。
- 特征匹配损失:把判别器的中间层特征也拉近,算是对抗损失的一个辅助,训练稳定性会好很多。
我个人的理解是,这四个损失各管一段:非负L1管“像”,感知损失管“内容对”,对抗损失管“真”,特征匹配管“稳”。缺一个,最终效果都会肉眼可见地打折扣。
4.3 从合成训练到真实场景微调
GPEN在FFHQ上构造退化对完成预训练后,还用真实退化图像做了一步微调。这步操作看似简单,其实决定了模型在真实老照片上的泛化能力。合成退化与真实退化之间始终有domain gap,训练时再怎么加噪声、加模糊,也不如直接让模型见几张真实场景下的低质量人脸。
微调时因为真实图没有高清GT,作者用了半监督的思路:只计算感知损失和对抗损失,丢弃需要GT的L1类损失。这个思路在很多图像恢复论文里都能看到,工程上可以在小规模真实数据上低成本提升模型表现,值得借鉴。
5. 实验结果怎么看:消融实验的价值在哪里
5.1 从定量指标到主观效果
论文在CelebA-HQ和真实老照片上都做了评测,定量指标包括FID、L1、PSNR等。但人脸修复这个任务非常特殊,定量指标高的方案主观上不一定好看。我复现时对比过几个模型,有些模型PSNR很高,但人脸是“磨皮脸”,毛孔全没了,这种结果用户一眼就能看出假。GPEN的强项恰恰是主观效果好,皮肤纹理真实,发丝边缘自然,在真实老照片场景下尤明显。
5.2 各模块的消融结果说明了什么
论文里的消融实验核心结论有三条:
- 去掉GAN先验只保留codec,输出锐利但缺少真实纹理,说明StyleGAN先验承担了“细节补全”的职责。
- 去掉噪声调制模块,颜色偏移和高频伪影明显加重,说明该分支有独立且关键的作用。
- 去掉多尺度判别器,人脸五官比例偶发变形,说明多尺度判别器是语义正确性的重要保障。
这三条结论对做工程同样有指导意义。如果你发现自己魔改后的模型效果不对,优先检查这三个模块是否被意外破坏。
5.3 与其他方法的对比:GPEN的优势和不足
对比PULSE这类先验重建方法,GPEN的身份保持能力明显更强,推理时间也短得多;对比HiFaceGAN这类纯codec方法,GPEN的细节纹理丰富度高出不少;对比GFP-GAN,两者各有千秋,GPEN在颜色自然度上略好,GFP-GAN在严重退化的极端条件下鲁棒性更优。
GPEN的短板也很明显。对于极度模糊、五官信息完全丢失的输入,它仍然会“幻觉”出一张合理的脸,可能与真实长相相差甚远,这也是所有基于先验的方法共同的问题。第二个短板是模型的感知领域有限,如果人脸姿态过大、遮挡严重,生成效果会明显下降。
6. 从论文到源码:复现与工程落地全记录
6.1 官方仓库结构与核心文件
GPEN的官方代码开源在GitHub上,整体结构比较清爽,核心文件集中在几个目录里:
face_enhance_model.py:推理入口,封装了人脸检测、对齐、增强、复原的完整流程gpmen.py:模型定义,核心网络结构training/:训练脚本、数据集构建、损失函数weights/:模型权重存放目录
推荐先从face_enhance_model.py读起,它能帮你建立端到端的印象:输入一张图,经历人脸检测、裁剪对齐、模型增强、反变换回去,最后输出结果。理解了这个流程,再回到gpmen.py看细节,会顺畅很多。
6.2 环境搭建与依赖
复现GPEN对硬件有一定要求,官方推荐的依赖是PyTorch 1.7及以上,CUDA 10.2以上。我在一台RTX 3090上跑512×512的推理,单张耗时大约0.3秒;训练的话建议至少24GB显存起步,不然batch size拉不起来,训练效果会受影响。
环境搭建命令可以直接从官方README里取,基本没有额外花样。但有几个依赖版本要注意:facexlib用于人脸检测,gfpgan是后续有的库,不建议混用;basicsr工具包如果版本不一致,容易在degradations模块上踩坑。Windows用户建议优先用WSL2跑,纯Windows环境编译某些CUDA算子容易出问题。
6.3 推理实操:三步完成老照片人脸增强
推理分为三步:
- 下载预训练权重,放到
weights/目录下。 - 准备一张含人脸的图片,建议人脸大于64×64像素,过小的话检测和对齐都会不稳定。
- 运行命令:
python face_enhance_model.py --in_path input.jpg --out_path output.jpg默认参数已经能出不错的效果。想调节强度,可以关注--upscale_factor和--out_size参数。我自己的经验是,对于老照片修复,上采样倍数设2到3比较稳妥,设太高会出现过度锐化的假纹理。
6.4 训练自己的模型:数据准备与参数选择
如果你想在自己的数据上微调,训练脚本在training/目录下。数据准备阶段用FFHQ或者自己的高清人脸数据集,先对每张图做不同退化处理生成训练对。退化参数要注意多样性:模糊核大小随机范围要大,噪声强度也要从轻到重覆盖,否则模型只会“背诵”特定退化类型,换一种退化就失效。
训练时的关键参数有这么几个:
--batch_size:显存允许的范围内尽量大,至少4--lr:初始学习率0.002左右,配合余弦退火比较好--num_gpus:多卡训练时注意同步批次归一化层的统计量--finetune:加载官方预训练权重做微调,收敛速度会快很多
6.5 从单张图到视频:工程化落地的加速技巧
GPEN放上倍到视频人脸增强时,如果逐帧跑完整检测和对齐流程,一台3090大概只能跑到每秒2到3张,完全不够用。我实际工程中做了三个优化:
- 视频场景下相邻帧人脸位置变化很小,可以用光流或目标跟踪算法预测下一帧的人脸框,跳过重复检测。
- 模型输出的512×512结果在小尺寸视频上够用,没必要每次输出1024×1024,可以减少解码器计算量。
- 用TensorRT或ONNX对模型做量化推理,损失一点精度换2到3倍速度,对实时场景是值得的。
7. 复现与使用过程中踩过的坑
7.1 环境依赖:facexlib和basicsr版本冲突
这是复现时最容易遇到的问题。facexlib的face_restore_helper.py和GPEN的代码之间、basicsr的版本之间,都可能因为API改动出现不兼容。我把官方repo的requirements.txt里的版本号和实际安装的版本核对了一遍才发现问题。建议直接用官方仓库中锁定的版本,不要随意升级。如果非要用新版basicsr,记得检查degradations模块的导入路径是否发生了变化。
7.2 显存占用:批量推理时的OOM问题
输入多张图片时,如果直接把batch size设得很大,很容易触发显存不足。我的经验是把图片先缩放到合适尺寸,再统一batch。另一个做法是开启PyTorch的torch.cuda.amp.autocast做混合精度推理,显存可以省将近一半,速度还有小幅提升。
7.3 输出效果不理想:先检查对齐质量
很多用户反馈GPEN输出的脸歪了或者左右眼不对称,我排查了一圈发现并不是模型问题,而是人脸对齐环节出了问题。GPUEN依赖检测模型输出的人脸关键点做相似变换对齐,当原始图像人脸姿态偏大、侧脸明显时,对齐质量会明显下降,后续增强自然跟着歪。这种情况下,可以先手动裁剪出正脸区域,或者用更强的人脸对齐模型替换内置方案,问题基本能解决。
7.4 真实老照片上的颜色偏差处理
真实老照片往往自带偏色,GPEN直接输出后颜色可能和原图观感不一致。我的处理办法是推理后做一个颜色校正:把原图的颜色统计量迁移到输出图上,具体实现可以用简单的直方图匹配,或者用Reinhard颜色迁移算法,效果都不错。这样既保留了模型的纹理增强能力,又避免了颜色被“洗掉”的问题。
8. 读完论文之后的一些个人体会
GPEN这篇论文最值得学习的不是某个具体模块,而是它“混合多种思路”的解决问题方式。人脸修复这个任务,没有一个单一工具能搞定全部,结构信息需要编解码器来保持,纹理信息需要GAN先验来补充,噪声抑制需要专门的频率域处理,最终效果需要多尺度判别器来把关——每一步都不算惊艳,组合起来却产生了质变。
我在实际项目中把GPEN和传统图像融合算法做了一个简单组合:先用GPEN做人脸区域增强,再用泊松融合把增强结果无缝贴回原图,背景部分只做轻量级超分,整体效果比直接对全图跑GPEN自然很多。这种“分而治之”的思路,在工程落地时往往比追求单模型的极限效果更可靠。
如果你想深入了解人脸修复的前沿方向,GPEN是个非常好的分水岭。读懂它之后,再回头去看GFP-GAN、CodeFormer这些后续工作,会发现它们解决的问题、采用的思路都和GPEN一脉相承。论文本身代码量不大,但内部包含的设计哲学值得学习很久。
最后分享一个读论文的小技巧:像GPEN这类有官方开源代码的工作,第一遍看完论文,一定要把论文里的公式和代码里的变量逐一对应起来读。有些作者在论文里写得比较含糊的地方,代码里往往有最诚实的答案。我在读GPEN的噪声调制模块时,论文只给了一张示意图,对着代码看了半小时才彻底理解它在哪个位置、以什么方式嵌入了主干网络。这种“论文+代码”对照着读的方法,虽然慢,但信息密度极高,读完后你就真正掌握了这个模型,而不是只“看过”一篇论文。