GFPGAN源码精读:人脸修复与增强的架构、训练与部署解析

GFPGAN源码精读:人脸修复与增强的架构、训练与部署解析 简介基于Python深度学习的GFPGAN图片修复算法实现源码面向图像修复方向开发者与研究者覆盖人脸修复、低分辨率照片增强、老照片翻新等典型场景适合具备基础Python与GAN知识的读者直接学习与二次开发。压缩包共62个文件主要包括26个py源码、多个yml/yaml配置文件与Markdown文档另有png/jpg示例图、mdb数据库、pth模型文件及license等整体6.22MB结构清晰便于快速定位核心算法与训练推理逻辑。已有429人学习具备一定参考热度。资源内含完整的训练与推理脚本、GFPGAN各版本网络结构实现、配置参数及使用说明并附带演示图片与模型权重相关文件可帮助读者理解生成对抗网络在图像修复中的落地应用也能直接用于个人项目或学术实验。1. 为什么还要做图片修复GFPGAN 的定位与价值一张 512 像素的糊脸图普通超分模型放大后得到的是更清晰的马赛克GFPGAN 却能在放大的同时把眼睛、嘴巴、皮肤纹理按语义补回来。这个基于 Python 和深度学习框架实现的图像修复算法把生成对抗网络、身份特征提取和 StyleGAN2 生成器串在一条推理链上专门解决低质量人脸的修复与增强问题。源码包一共 64 个文件其中 26 个 Python 源文件覆盖了网络定义、训练数据管线、推理入口和辅助工具是典型的学术项目工程化结构。本文按照架构 → 推理 → 训练 → 部署这条链路拆开讲重点落在模型各文件怎么组织、推理参数怎么选、自定义训练要动哪些配置以及交付前容易踩的坑。适合已经跑过基础深度学习项目、想在图像修复方向深入复现并改写源码的工程师。2. 源码布局与生成器架构GFPGAN 不是单纯把 GAN 放大2.1 26 个 Python 文件的分工架构、训练、工具三类把源码包解压后可以看到项目根目录下有inference_gfpgan.py、train.py核心代码全在gfpgan子包中。不要被 26 个 Python 文件的数量吓到按职责归类后其实只有三块网络结构定义、训练与数据逻辑、辅助脚本。下面这张表把关键文件与职责对应起来后续排查问题时就清楚该打开哪个文件。文件路径职责gfpgan/archs/gfpganv1_clean_arch.py主生成器无 BatchNorm 版本推理和训练行为一致gfpgan/archs/stylegan2_clean_arch.pyStyleGAN2 骨干提供注入式上采样通路gfpgan/archs/gfpganv1_arch.py早期带 BN 的生成器版本和 clean 版共存但通常不直接部署gfpgan/archs/arcface_arch.pyArcFace 人脸识别骨干提取 512 维身份特征gfpgan/archs/restoreformer_arch.pyRestoreFormer 风格的 Transformer 修复分支gfpgan/models/gfpgan_model.py训练逻辑封装组织 GAN loss 与感知 lossgfpgan/data/ffhq_degradation_dataset.py从 LMDB 读取 FFHQ 并做退化合成inference_gfpgan.py推理入口支持单张、批量以及背景上采样train.py训练入口解析 YAML 配置并拉起训练循环scripts/convert_gfpganv_to_clean.py把带 BN 的旧权重转换成 clean 权重scripts/parse_landmark.py为眼嘴增强分支准备特征点标签archs目录下还有gfpgan_bilinear_arch.py和stylegan2_bilinear_arch.py这两个文件对应使用双线性上采样替代转置卷积的兼容版本。如果训练时改了生成器的上采样方式权重文件必须和架构严格对应arch参数填错会直接抛出 shape mismatch。2.2 GFPGANv1Clean 的 forward 骨架身份码注入与特征空间修复GFPGAN 的核心设计可以概括成一句话先用退化图提取模糊的中间特征再用 ArcFace 提供的身份码去引导 StyleGAN2 生成器逐级上采样。这样修复结果既能保留原本的身份信息又能在特征空间里去除噪声和模糊。从gfpganv1_clean_arch.py里剥出来的逻辑骨架大致如下# 逻辑骨架非完整源码用于理解数据流向 def forward(self, x, return_latentsFalse): fea self.encoder(x) # 退化图 - 多尺度特征 identity_code self.arcface(x) # 人脸 - 512 维身份向量 latent self.stylegan_decoder.style2latent(identity_code) # 身份码映射到 latent space out self.stylegan_decoder(fea, latent) # 特征注入后逐级上采样修复 return out这段代码里最值得琢磨的是style2latent这一步。普通 GAN 修复模型是把整张图编码成一个全局向量然后让生成器从零开始重建GFPGAN 把身份特征的维度压得很低生成器的语义主要由预训练的 StyleGAN2 权重决定网络只需要学会在特征空间里做局部修正。这样做的直接好处是训练不容易崩且对输入分辨率不敏感。clean 版本把 BatchNorm 全部去掉换成实例归一化和权重归一化的组合。原因很实际BN 在训练时会统计当前 batch 的均值和方差batch size 小的时候统计量抖动大推理时又会切换到全局统计量训练和推理行为不一致在 GAN 这种大模型上会被放大成明显的伪影。clean 架构在单卡小 batch 训练场景下明显更稳。2.3 ArcFace 与 RestoreFormer两个可选分支分别解决什么问题arcface_arch.py提供了身份先验它在整个人脸修复链路里几乎是必须的。没有身份约束生成器很容易把一张人脸修成看着自然但不像本人的通用脸。ArcFace 分支在训练时参与身份一致性损失的计算推理时提供 latent 引导这就是修复结果能保留个人特征的原因。restoreformer_arch.py是源码里保留但默认配置不常启用的分支。它设计用于非面部区域的语义修复比如头发、衣物、背景中带有结构化纹理的部分。我在实际使用中一般把背景修复交给--bg_upsampler realesrgan让独立的后台上采样模型去处理背景主生成器专注五官区域。只有当背景里含有大量人脸相似结构时再考虑打开 RestoreFormer 分支代价是显存占用明显上升。# gfpgan_model.py 中分支加载的简化示意 if opt.get(use_arcface, True): self.arcface ArcFace(...) # 身份分支默认开启 if opt.get(use_restoreformer, False): self.restoreformer RestoreFormer(...) # 语义修复分支按需开启3. 推理实操inference_gfpgan.py 的参数、后台上采样与输出语义3.1 环境准备torch、basicsr、facexlib 与权重落位源码包在requirements.txt里列明了依赖我习惯用 conda 独立环境避免污染基础环境。基础安装命令如下如果你的 CUDA 版本不同torch 的安装方式要按自己的环境调整。conda create -n gfpgan python3.8 -y conda activate gfpgan pip install torch torchvision # 按本机 CUDA 版本安装 pip install basicsr facexlib pip install -e . # 以可编辑模式安装当前仓库这里有一个常见误区直接pip install gfpgan虽然能装上包但仓库根目录的inference_gfpgan.py是独立脚本它依赖的是本地源码里的gfpgan.utils与 PyPI 上的包版本不一定匹配。我一般用pip install -e .把当前目录安装成开发模式这样改代码立即生效脚本和包的版本也始终一致。权重文件需要手动放到experiments/pretrained_models/目录下推理脚本默认从这里加载模型。mkdir -p experiments/pretrained_models # 将下载好的权重放入该目录 ls experiments/pretrained_models3.2 命令行参数拆解-i、-s、--bg_upsampler、--only_center_faceinference_gfpgan.py的参数设计得很集中绝大多数场景只需要调整五六个。下面这张表是我实际使用中会关注的参数括号里是常用取值。参数作用我的使用习惯-i输入路径文件或目录批量处理时直接传目录-o输出目录按任务分文件夹避免覆盖-s上采样倍数修复老旧照片用 2超分场景用 4--bg_upsampler背景上采样器realesrgan或none追求整体效果用realesrgan只想做人脸修复用none--bg_tile背景上采样分块大小显存不足时从 400 降到 200--face_upsample先对人脸区域单独超分再融合小脸区域多的图建议开启--only_center_face只处理图像中心人脸多人合影且目标明确时使用--aligned输入是否已按人脸对齐裁剪直接喂cropped_faces下的图时开启一个典型的完整命令长这样python inference_gfpgan.py \ -i inputs/whole_imgs \ -o results \ -s 2 \ --bg_upsampler realesrgan \ --bg_tile 400 \ --face_upsample-s 2表示把输入图像的长边放大到原来的两倍如果原图本身分辨率和清晰度尚可-s 2的修复痕迹最轻-s 4会让背景区域有更强的锐化感但如果背景本身很模糊放大后反而会暴露压缩伪影。--bg_tile是分块参数显存不够时优先调它而不是调-s因为分块大小直接控制背景模型单次计算的数据量。3.3 代码走读从整图输入到四类输出的处理链看懂了参数再看脚本内部就轻松了。推理脚本的初始化部分核心是构造一个GFPGANer实例如果需要背景增强会先创建一个 RealESRGAN 的背景上采样器# inference_gfpgan.py 初始化逻辑简化 from gfpgan.utils import GFPGANer bg_upsampler None if args.bg_upsampler realesrgan: from basicsr.archs.rrdbnet_arch import RRDBNet from realesrgan import RealESRGANer bg_upsampler RealESRGANer( scale4, model_pathargs.bg_model, modelRRDBNet(num_in_ch3, num_out_ch3), tileargs.bg_tile, ) restorer GFPGANer( model_pathargs.model_path, upscaleargs.upscale, archclean, bg_upsamplerbg_upsampler, face_upsampleargs.face_upsample, )RealESRGANer的scale4是背景模型内部的固定上采样倍数与命令行里的-s是两回事。主修复器会把输入图按upscale参数调整到目标尺寸背景模型再做额外的细节增强。理解这个双阶段结构对调参会很有帮助-s控制整体的输出尺寸bg_upsampler控制背景纹理的锐化程度。处理每张图时脚本内部先做人脸检测把人脸从背景中裁剪出来对齐后送入 GFPGAN 生成器背景部分走背景上采样器最后按原位置融合回去。这一步由GFPGANer.enhance完成cropped_faces, restored_faces, restored_img restorer.enhance( img, has_alignedargs.aligned, only_center_faceargs.only_center_face, )返回值有三个列表cropped_faces是检测并裁剪出的原人脸restored_faces是修复后的人脸restored_img是融合后的完整图片。脚本会把它们分别保存为不同的后缀文件。所以一次推理得到的不只是最终图还有中间产物这对排查问题很有价值。如果最终效果不对先看restored_faces里的人脸是否正常人脸正常而整体图有问题说明融合或背景环节出错人脸本身就有伪影问题在生成器输入或权重。4. 自定义训练退化数据管线、YAML 配置与 train.py 的串联4.1 FFHQDegradationDatasetLMDB 存取与退化合成想要拿自己的数据训练 GFPGAN第一步是理解数据管线。gfpgan/data/ffhq_degradation_dataset.py负责从ffhq_gt.lmdb读取高质量人脸图并按预设的退化流程生成低质量输入。LMDB 是一种高效的键值存储这里用它代替一堆散落的 PNG 文件避免大量小文件的随机 IO 拖慢训练。# ffhq_degradation_dataset.py 逻辑骨架 class FFHQDegradationDataset: def __getitem__(self, index): gt self._load_lmdb(index) # 512x512 的高清人脸 lq degrade(gt) # 模糊 下采样 噪声 JPEG 压缩 landmark self._load_landmark(index) # 眼睛、嘴巴特征点 return {gt: gt, lq: lq, landmark: landmark}degrade这一步的质量直接决定模型最终能修到什么程度。GFPGAN 的退化合成遵循真实感退化策略高斯模糊的核大小、下采样倍率、噪声强度、JPEG 质量因子都不固定而是在一定范围内随机抽取。这样做的目的是覆盖更广的低质量分布修复模型才能应对真实场景里的老照片扫描件和网络压缩图。源码里单独提供了tests/test_ffhq_degradation_dataset.py和对应的 YAML 配置。我在换数据集时一定会先跑一遍这个测试脚本把lq和gt成对可视化确认退化强度符合预期再启动训练。否则训了一天发现退化太轻模型只学会了超分没学会修复返工成本很高。4.2 train_gfpgan_v1.yml 逐段拆解与关键超参训练配置集中在options/train_gfpgan_v1.yml和train_gfpgan_v1_simple.yml两个文件里。YAML 的结构大致分成网络、数据、路径、训练、日志五个部分。下面的节选保留了常见字段network_g: type: GFPGANv1Clean out_size: 512 num_style_feat: 512 channel_multiplier: 2 decoder_input_scale: 1 datasets: train: type: FFHQDegradationDataset gt_path: data/ffhq_gt.lmdb io_backend: lmdb use_hflip: true train: total_iter: 450000 lr_g: !!float 1e-4 lr_d: !!float 4e-4 beta1: 0.9 beta2: 0.99 path: pretrain_network_g: experiments/pretrained_models/GFPGANv1.pthnetwork_g里的channel_multiplier是生成器宽度系数调大能提升生成细节但显存翻倍。decoder_input_scale控制解码器输入特征图的缩放比例一般保持默认。train部分里lr_g和lr_d分别是生成器和判别器的初始学习率判别器比生成器快四倍是 GAN 训练里常见的设定目的是让判别器先跟上生成器的更新节奏。pretrain_network_g路径下的预训练权重是训练起点。GFPGAN 的常见训练策略是用预训练的 StyleGAN2 生成器权重初始化解码器ArcFace 权重也直接加载预训练模型训练只微调修复相关的部分。这样做比从零训练快几个量级也更稳定。4.3 train.py 入口与训练循环GAN loss 和感知 loss 如何组织启动训练的命令很简洁所有参数都走 YAML。python train.py -opt options/train_gfpgan_v1.ymltrain.py内部调用的是 Basicsr 的train_pipeline。这条管线会依次完成加载配置、初始化日志器、构建数据集与数据加载器、构建生成器和判别器、进入迭代训练循环。# train.py 入口逻辑简化 import argparse from basicsr.train import train_pipeline def main(): parser argparse.ArgumentParser() parser.add_argument(-opt, typestr, requiredTrue, help训练配置文件路径) args parser.parse_args() train_pipeline(args.opt)每一次迭代中模型先更新判别器再更新生成器。生成器的损失通常由四部分组成GAN loss 让输出看起来真实感知 loss 约束整体结构和纹理不失真L1 loss 约束像素级接近身份一致性 loss 让人脸仍是同一个人。身份 loss 的权重在 YAML 里通过network_g附近的参数控制我习惯把身份 loss 权重设得偏高一点尤其是做人像修复时因为像本人比像素接近更重要。4.4 显存不足时的调参策略simple 配置、混合精度与 batch 选择GFPGAN 完整配置在单张 24G 显存的卡上勉强能跑资源受限时优先使用train_gfpgan_v1_simple.yml。这个配置去掉了部分可选分支降低 decoder 的计算负载适合单卡小显存环境先跑通流程。下面是几个常见调整项调整对象完整配置显存紧张时的做法副作用batch_size824梯度噪声变大可配合调低学习率混合精度关闭开启 AMP数值精度略降但对 GAN 训练通常可接受梯度累积无累积两到四步等效增大 batch但会拖慢单步速度生成器宽度channel_multiplier2降至 1输出纹理细节变弱开启混合精度需要在 YAML 的train部分配置use_amp: trueBasicsr 管线会自动处理 GradScaler。注意混合精度与某些自定义 loss 的数值稳定性需要额外观察如果训练过程中 loss 出现 NaN先关掉 AMP 跑一百步确认是否由精度导致。显存实在不足时降低channel_multiplier比降低batch_size效果更直接因为生成器宽度影响的是每张图的显存占用而 batch size 影响的是梯度统计质量。5. 模型转换、landmark 与批量验证交付前最后一步5.1 convert_gfpganv_to_clean.py把 BN 权重折叠干净训练早期版本保存的权重可能带 BatchNorm但要部署时我们通常希望用 clean 架构。scripts/convert_gfpganv_to_clean.py做的就是这件事把 BN 层的缩放和偏移吸收进前一层的卷积权重然后从 state dict 里移除 BN 参数。转换后生成器在推理时不再依赖任何 batch 统计量单张图输入和批量输入的结果完全一致。python scripts/convert_gfpganv_to_clean.py \ --input experiments/pretrained_models/GFPGANv1.pth \ --output experiments/pretrained_models/GFPGANv1_clean.pth转换完不要直接上生产先拿同一张图分别用转换前后权重推理对比restored_img。理论上视觉差异应该很小如果背景或人脸出现明显色差说明原权重里的 BN 统计值和卷积权重并没有被正确折叠检查转换脚本是否覆盖了所有 BN 层。5.2 parse_landmark.py眼嘴增强需要什么标签GFPGAN 在意眼睛和嘴巴区域单独增强这依赖人脸特征点标签。parse_landmark.py负责批量解析 FFHQ 数据集中每张人脸的眼睛、眉毛、嘴巴位置并把结果缓存到 pth 文件。训练数据管线在加载数据时会读取这些标签用来引导增强分支只对关键区域做更大力度的修复。python scripts/parse_landmark.py \ --gt_path data/ffhq_gt.lmdb \ --save_path data/eye_mouth_landmarks.pth运行一次即可之后训练多个版本都能复用。如果换成自己的数据集特征点解析这一步要重新执行且要确认特征点坐标的尺度与数据集中图像的尺寸一致否则区域 mask 会整体偏移。5.3 批量推理验证的小技巧交付前我会用下面的循环把测试集批量过一遍配合--only_center_face只处理画面中心的人脸既节省时间又能快速发现问题for f in inputs/whole_imgs/*.jpg; do outresults/$(basename ${f%.*}) python inference_gfpgan.py \ -i $f \ -o $out \ -s 2 \ --only_center_face \ --bg_upsampler realesrgan done批量跑完后不要只看缩略图放大到 100% 检查人脸边缘和背景交界处。如果restored_faces清晰但融合后边界发虚把--bg_tile调大重新跑如果背景纹理出现过强的水波纹则改成--bg_upsampler none只靠 GFPGAN 主体做修复整体效果可能反而更干净。本文还有配套的精品资源点击获取