2026/10/17 16:27:24

从毕设到实战:DCGAN图像修复全流程详解与调参心得

从毕设到实战:DCGAN图像修复全流程详解与调参心得 去年秋天提交毕设初稿的前一天我盯着屏幕上那张修复结果图鼻子和眼睛已经长出来了可脸颊的皮肤纹理还是糊的像铺了一层磨了皮的滤镜。这个问题伴随了“基于深度学习的图像修复算法 DCGAN”这个课题整整三个月。从数据准备、模型改造到损失函数的权重调节每一步都有大量文档里不会写的细节。现在回头看这个毕设真正让我学会的不是跑通一个模型而是当一个生成任务效果不够好时该怎么系统性地找出问题。这篇内容写给两类人看一是正在考虑选图像修复、人脸生成相关方向做毕设的同学二是已经把DCGAN跑起来但修复效果不理想、想搞清楚怎么调的人。我会从选题思路、技术脉络、模型结构、数据掩码、训练调参、效果评估一路讲下来内容和我在实验室里反复验证过的做法一致直接照着复现是可行的。1. 毕设选题思路图像修复这个方向到底值不值得做1.1 课题价值与场景拆解图像修复在学术上叫image inpainting任务描述很简单给定一张缺了一块区域的图像模型需要根据周围像素和图像语义把缺失部分补上并且补出来的结果不能被人一眼看穿。这个“不能看穿”是比分类、检测更有挑战性的目标因为它要求模型不是判别而是生成。这个课题的工程价值也非常清晰。老照片上的折痕和污渍修复是最常见的场景档案馆做历史影像数字化时会大量用到艺术品的裂纹补全和线稿复原也依赖这类算法电商平台的商品背景干扰物移除、房屋装修改造前的效果预览甚至影视后期里临时遮挡物的去除都是图像修复的用武之地。我第一次做实际需求调研时发现需求方最关心的其实就两个点修复后的区域够不够自然、处理速度能不能接受。作为毕设图像修复还有一个天然优势它同时覆盖了深度学习、生成模型、图像处理和工程评估四个维度任何一块都能展开写不会没东西可写。而且修复前后的对比效果非常直观展示和答辩时不需要评委懂算法一张图就能说明问题。1.2 为什么选DCGAN而不是别的生成模型我当时在技术选型时纠结过好几个方向直接用CNN做回归、用传统的GAN、用VAE、或者直接用预训练的修复模型。最后选了DCGAN核心原因有三点。第一DCGAN的结构足够简洁但又不失代表性。它把卷积网络和对抗生成框架结合生成器和判别器的思路都很清晰不管是画网络结构图、讲原理还是改代码都容易掌控。相比后来的StyleGAN这类几百层的高配模型DCGAN更适合作为理解和掌握的起始点。第二课题需要做的是“可控的实验研究”而不是“找最优工程方案”。毕设考察的是你对问题的理解、对方法的改进和对结果的分析能力。DCGAN的训练过程本身就充满可探究的问题比如训练不稳定、模式坍塌、生成模糊等这些问题既是难点也是论文里可以写的肉。第三DCGAN做修复在算力上足够友好。一张单卡就能完成训练不需要集群这对大多数学校的实验室条件是现实可行的。当时试着跑更复杂的模型显存直接吃紧而DCGAN的生成器在128分辨率下只占不到2GB显存给整个实验留出了很大余量。1.3 原始标题里的关键词实际对应了哪些工作量把“基于深度学习的图像修复算法 DCGAN”拆开看“深度学习”对应特征提取和生成能力“图像修复”定义问题域“DCGAN”指定技术路线。这三个词串联起来就构成了完整的工作流准备大量配对数据包含遮罩的图像 完整原图 → 设计生成网络和判别网络 → 设计多目标损失函数 → 训练并评估主观和客观效果。这里要提醒一点如果想在答辩时有说头建议从一开始就给课题加一个“改进点”。做完全照着别人的结构复现评委一句“你的创新点在哪里”就会很被动。我当时把改进点放在了两处一是给生成器加了跳跃连接来保留细节二是把单一的对抗损失改成“对抗损失 内容损失”的组合效果提升非常明显后面会详细说。2. 图像修复的技术脉络从像素扩散到语义生成2.1 传统修复方法的能力边界在深度学习之前主流的图像修复算法走的是两条技术路线基于扩散的方法和基于纹理合成的方法。基于扩散的方法思路是把缺失区域当作一个“洞”利用洞边界上已知像素的信息通过求解偏微分方程比如热传导方程或者变分模型逐步向洞内传播图像结构。这种方法能够较好地保持平滑区域的连续性但对带有复杂纹理、结构信息的区域无能为力补出来的结果往往是一块均匀的模糊斑块。基于纹理合成的方法以PatchMatch为代表思想是在已知区域里搜索与洞边界块最匹配的图像块然后进行复制粘贴。它对面状纹理效果拔群搬砖砌墙一类图案几乎看不出痕迹但它完全理解不了“这个洞里应该有一个眼睛”这种语义信息。洞里的内容如果在库里找不到相似的块结果就会非常怪异。这些传统算法给我的启发是修复任务有两个维度——风格连续性和内容合理性。传统方法只能在风格连续性上做文章而深度学习可以同时解决两者这是整个选题成立的逻辑基础。2.2 深度神经网络凭什么能“无中生有”卷积神经网络通过层层卷积操作提取特征底层关注颜色、边缘中层关注纹理、形状高层关注语义类别。如果把修复当作一个端到端的映射问题那么CNN要学习的就是已知区域的像素模式如何决定着缺失区域的内容。我用一句比较直白的话给自己打通了思路真正有价值的不是补上像素值而是先“理解”这张图是什么。如果模型能从已知区域识别出这是一张人脸、一个单人背影、一片天空它就能从训练数据中学到的“人脸应该有什么器官、五官在什么位置”等行业先验知识里寻找最合理的补全结果。在这一点上CNN远远强于传统算法。2.3 GAN如何把“看着像”升级成“像到看不出区别”朴素的CNN回归模型在训练时用的是均方误差或L1损失这种损失从设计上就更倾向于让输出靠近所有可能结果的“平均值”。具体表现就是生成图像偏模糊、边缘被磨平。原因不难理解多个合理的候选补全结果做平均得到的图像自然就是糊的。GAN给出的解决方案是引入一个对抗过程。生成器负责合成图像判别器负责判断输入是真实图像还是生成图像。两个网络在博弈中共同进化生成器越来越擅长骗过判别器而判别器也越来越敏锐。对抗损失迫使生成器不再追求“平均合理”而是选择极具真实感的具体结果。DCGANDeep Convolutional GAN在这个框架里做的手术在当时非常关键它用带步长的卷积替代池化层移除了全连接层在判别器里用LeakyReLU避免梯度稀疏在生成器里用BatchNorm稳定训练并把Adam优化器的momentum参数调整到了更适合对抗训练的数值。这套改进让卷积GAN的训练变得相对可复现、可收敛因此也成为了很多图像生成、图像编辑课题的基础底座。修复任务要特别注意的是经典DCGAN生成器接收的是随机噪声而修复场景需要的是“根据已有像素生成缺失像素”所以必须把生成器改造为条件输入网络输入是带遮挡的图像和掩码输出是完整的修复图。这一步是整篇技术方案里最重要的结构改造决定着后续所有实验的方向。3. DCGAN模型结构拆解与毕设落地实现3.1 生成器编解码框架和跳跃连接的取舍我的生成器实现采用了“编码器-解码器”的对称结构。编码器由四层卷积构成逐层将图像尺寸降低同时通道数翻倍目标是压缩出高层的语义特征解码器由对应的四层转置卷积构成逐层恢复空间分辨率。在生成器的最后一层使用Tanh激活函数将输出像素值规范到[-1, 1]这样和判别器输入以及归一化后的真实图像保持同一数值范围。这里有一个我踩过的典型坑原始DCGAN的生成器是纯串行的潜变量生成结构输出只依赖全局编码后的特征向量导致修复结果在缺失区域和已知区域的交界处经常出现不一致的接缝。解决办法是参考图像分割里常见的跳跃连接设计把编码器同尺寸的特征图拼接到解码器的对应层上。这样解码器就能同时看到高层的语义信息和低层的空间细节边界自然很多。值得说明的是这种改法后来也被很多工业级的修复模型所采用普通论文里提到backbone时说的U-Net结构本质上就是“编码器-解码器跨层连接”。我在毕设里很明确地把这个结构改进写成了一个小创新点而不是简单说自己跑了个vanilla DCGAN。3.2 判别器图像块真伪鉴别的实现细节判别器的设计相对简单它不需要产生图像只需对输入图像输出一个真伪评分。我的判别器结构是四层卷积加一层全连接卷积层使用LeakyReLU激活Leaky系数取0.2。由于卷积的平移等变性这个判别器实际上能对图像的局部纹理和全局结构进行特征打分而不需要简单地看整张图。在毕设阶段很多同学会问判别器到底要设计多强。我的经验是判别器不能远强于生成器否则生成器学不到新的东西只会不断重复已有的低级骗术也不能太弱否则它给不了生成器有效的梯度信号。一个可行的做法是从弱到强逐步试探让判别器先和生成器打成平手再观察生成损失下降情况根据实际情况调整层数。另一个值得记录的细节是损失函数接口的写法。以PyTorch为例建议把真实样本和生成样本分别送到判别器中计算两次而不是在同一个batch里拼接后一次性计算。这个习惯能让你在调试时方便地查看单个样本的分数变化判断判别器是否出现了“不管输入是什么都输出同一个分数”的退化情况。3.3 关键工程参数从官方推荐到实际调整DCGAN原论文推荐的参数组合在当时非常经典也一直被沿用优化器用Adam学习率0.0002beta1取0.5beta2取0.999batch size推荐64。beta1取0.5而不是常见的0.9是为了让梯度动量不过分累积这在对抗训练的场景里能明显减少震荡。我的实际显存是6GB处理128x128分辨率时batch size只能设到16。改小batch size会让训练步数变多梯度的噪声也更大这种情况下我观察到判别器更容易先饱和即判别损失迅速逼近0而生成器还没学到东西。针对这个问题我在实现里给生成器引入了一个“先队训练”的预热步骤前3000个iteration只训练生成器用L1损失把整体轮廓学出来再开启判别器对抗训练。这样能让两个网络之间的对抗起点更均衡。参数设置的完整参考表如下模块参数项配置值说明优化器类型Adam自适应矩估计适合稀疏梯度场景优化器learning rate0.0002原论文推荐实践验证稳定优化器beta10.5降低动量累积稳定对抗训练数据集批量大小16根据6GB显存实测调整生成器激活函数ReLU 输出层Tanh避免梯度消失规范输出范围判别器激活函数LeakyReLU(0.2)防止负区间梯度停滞输入图像分辨率128 × 128数据预处理目标尺寸输入图像像素范围[-1, 1]与Tanh输出匹配4. 数据与掩码设计决定修复上限的前置工作4.1 数据集的选取和清洗经验毕设选数据集时需要同时考虑规模的充足性、领域匹配度和获取难度。我做修复实验主打的是人脸场景因为人脸结构相对固定、语义明确修复效果的优劣在视觉上很容易判断。公开的人脸数据集大多已经做了人脸对齐图像都是面部居中、表情和光照各异的灰度图省去了很多预处理工作。数据清洗容易被忽略但影响很大。我随机抽样了上千张训练图像做过一次人工筛查删掉了三类问题数据部分失真的异常样本、带有夸张遮挡的样本、以及明显对齐错误的样本。这类坏样本混进训练集后模型会学到错误的统计规律常常在不好的数据上学到不正确的纹理映射。为了扩充数据我还做了一些常规增强操作水平翻转注意人脸场景不能随意旋转和翻转否则会导致左右五官信息颠倒、轻微的颜色抖动、随机裁切和缩放。增强操作一定要在生成掩码之前完成保证掩码和图像的空间位置始终对应。4.2 掩码生成从固定中心洞到不规则遮挡有效掩码直接决定了模型的学习难度。最初我采用的是最经典的“中心方形掩码”在128x128的图像中心扣掉一个64x64的正方形区域。这种做法在论文里很多见缺点也很明显模型只会学补中心遇到边缘破损或者不规则破损就束手无策。针对这一情况我从第二周开始实现了三种掩码的混合训练策略中心方形、多个随机大小方块、以及若干条随机宽度和角度的线条。为了让效果更加接近真实场景我最后还加入了不规则掩码模拟做法是先生成一张噪声图再将噪声经过模糊和阈值二值化处理形成类似真实的划痕纹理。这个混合策略实施之后模型对不同缺失形状的鲁棒性提升非常明显。掩码生成的Python示例import numpy as np import cv2 def generate_mask(h, w, mask_type): mask np.zeros((h, w), dtypenp.float32) if mask_type center: size np.random.randint(h // 4, h // 2) x1 (w - size) // 2 y1 (h - size) // 2 mask[y1:y1 size, x1:x1 size] 1.0 elif mask_type random_square: size np.random.randint(8, h // 3) x1 np.random.randint(0, w - size) y1 np.random.randint(0, h - size) mask[y1:y1 size, x1:x1 size] 1.0 elif mask_type stroke: num_strokes np.random.randint(1, 4) for _ in range(num_strokes): x1, y1 np.random.randint(0, w), np.random.randint(0, h) x2, y2 np.random.randint(0, w), np.random.randint(0, h) thickness np.random.randint(4, 14) cv2.line(mask, (x1, y1), (x2, y2), 1.0, thickness) return mask4.3 数据管道的搭建细节我在训练时把“损坏图的构建”放在了数据加载器里实时完成而不是提前离线生成好。原因有两个一是训练集可以因此实现无限扩展每个epoch都能看到不同的掩码二是掩码策略的参数大小、位置、笔触数量可以随时调整不需要重新生成整套数据集。实现时需要注意掩码、原图、受损图像的空间尺寸保持一致并在转换成网路张量时分别存储。我的数据管道输出顺序是受损图、原图、掩码三个Tensor。其中受损图由原图与掩码相乘得到即“保留区域 原图1 - 掩码”。还要提一个容易出错的地方图像归一化必须同时作用于原图和受损图掩码保持0/1值不变。有的人会把原图减均值、受损图忘了减或者掩码被除以255变成0.004的极小值这类精度问题不会让模型爆掉但会严重拖慢收敛速度。5. 训练过程实录损失曲线、不稳定性与调参心得5.1 正负样本的数值范围与判别器的饱和训练刚开始时判别器面对的是生成器输出的灰色糊状图很快就能学到“凡是这种图像的输入都判假”。这时判别器输出会迅速趋于极端判别损失降得很低但这并不代表我们训练得很成功只代表判别器已经饱和了。判断判别器饱和的典型做法是观察它对真实样本和生成样本的平均输出值。正常的训练状态里真实样本的评分应该在0.7到1.0之间生成样本的评分在0到0.3之间并且差距不会过于悬殊。如果真实样本评分长期接近1.0而生成样本评分长期接近0.0说明两个网络已经出现碾压局面这时候用生成器的梯度反馈做修复学习基本上是无效的。我解决这个问题用的是两个手段。一是适量降低判别器的学习率或者让判别器的参数更新频率低于生成器比如每训练2步生成器才训练1步判别器。二是给判别器的输入添加轻微的一处随机程度可调的噪声这能防止判别器“记死”特定模式的图像。5.2 损失函数组合的第一个版本修复区域L1 全局对抗生成器的总损失我一开始设成了两个部分加权相加公式如下G_loss 100 * L1(fake * mask, real * mask) 1.0 * Adversarial_LossL1项对缺失区域做逐像素计算迫使生成器逼近真实像素。权重为对抗损失的100倍意味着在训练初期像素逼近占主导网络先把大概结构搞清楚。对抗损失则保证细节丰富度。这个组合的思路是先用回归保证整体对得上再用对抗提升质感。这里有一个很值得记录的现象L1权重太高会让修复边界出现“水彩晕开”的反光感Discriminator再怎么施加压力也纠正不过来。原因是梯度被L1的数值压倒了生成器认为最重要的是像素差最小化。后来我把L1权重从100逐步降到10同时观察对抗损失对细节的改善最终在40-50之间找到了平衡点。所以我在调试时建议记住一个规律一开始权重取大是稳但要留出逐步下降的余地。5.3 第二个改进只在掩码区域计算内容损失一个普遍存在的误解是所有人都会在整张图上计算L1损失。我当时实际观察发现这样会让生成器花相当多的精力去还原本来就是输入内容、无需修复的区域像素。这个方向并非完全无用它帮助生成器保持背景一致性但代价是相对稀释了缺失区域的学习强度。从第二个实验开始我把修复区域的L1损失单独拆出来加权并和全图损失做了不同权重的对比实验。只计算掩码区域时缺失区域细节的恢复速度明显加快背景也没有出现可感知的偏差。这块工作最终成了我论文里效果对比实验的一环通过消融实验说明分区监督的作用讨论价值极高。5.4 训练回合的节奏选择总训练步数上我在128分辨率、单卡环境下8万步左右开始看到比较理想的结果10万步之后效果提升变得很平缓。此时如果继续训练而不做任何调整判别器会慢慢进入过于熟练的状态生成器的小幅优化很难再推动效果。我在训练时建议练完一个阶段后保存checkpoint在tensorboard里对比不同阶段的输出图既是习惯也是防止训练后期数据浪费的保障。还需要记录一个容易误判的问题生成器的损失在训练后期常常不是平滑下降的而是呈现明显的锯齿状震荡。在实践感受上只要生成图像本身在逐渐变清晰损失值的锯齿就不需要过于紧张这是GAN的正常行为。真正的危险信号是生成图像反复在“细节丰富”和“边缘崩坏”两种状态之间切换那说明判别器在做无意义的跳跃这个时候就停下来改结构或改学习率。6. 效果评估PSNR/SSIM之外的验收逻辑6.1 两个常见客观指标的量化逻辑PSNR峰值信噪比是基于像素级别的误差统计先计算修复图和原图之间的均方误差MSE再通过下面公式转成分贝值。PSNR 10 * log10(255^2 / MSE)如果图像像素范围是[0,255]PSNR值越高说明修复图与原图在像素级别越接近。我的实验里单纯跑DCGAN修复PSNR大概在24dB到28dB之间改进版加了内容损失和跳跃连接后到了29dB到31dB左右。SSIM结构相似性指标则是从亮度、对比度、结构三个维度结合局部窗口计算相似度得分在0到1之间越接近1说明和原图在结构上越相似。SSIM比PSNR更贴近人眼感受因为它对局部结构的敏感度和视觉系统更一致但在人脸纹理过于精细、高光和阴影复杂时SSIM的区分度仍不够。附上两个指标的解释表格指标计算基础优点缺点PSNR像素MSE实现简单可横向比较与视觉感知不完全一致SSIM局部亮度/对比度/结构更符合人眼感知对纹理细节敏感度有限主观评分人工观察最直观、最接近真实需求评价成本高主观性强6.2 失败案例分析边缘伪影与颜色偏差PSNR和SSIM都达标的样本肉眼效果也可能很糟糕。这类情况在我实验里分两类。第一类是边缘伪影修复区域和已知区域的边界处出现残留的接缝线或模糊过渡带。模型网络很可能并没有真正理解跨边界的纹理连续性只在边界处做了妥协。我的处理方式是在损失函数里额外加了掩码边界加权让靠近边界的像素在L1计算中有更高的权重效果比较明显。第二类是颜色偏差模型把区域内偏暗的皮肤修成了偏亮或者暗场景里补出了过亮的光斑。这种问题属语义理解不到位的环节虽然很看观感但往往被判分器错误地“认可”了。解决思路是提高对抗损失的相对权重让判别器对颜色分布的逼真度要求更高同时适量增加真实图像多样性和色彩扰动。6.3 毕设答辩时如何展示效果答辩展示不能只放一张修得最好的图。我的做法是准备四组对比原图、受损图、基础DCGAN修复图、我的改进版修复图每组再补充修补区域绝对误差的热力图用浅色标记误差大的区域。这样评委一眼就能看出结构改进带来的差异比单纯念指标有力得多。同时还要准备策略对比中心掩码 vs 随机掩码 vs 线条掩码每种情况放4-5个例子的网格图。这能让评委理解你的方法不只是在一个简单场景里自嗨而是考虑了更接近实际问题的场景覆盖。实验数据表格和案例图相互印证答辩逻辑就完整了。7. 回望整个毕设最值得分享的教训与扩展方向经历完整个课题最有价值的认识是图像修复的难点不在模型前向推理的几步代码而在大量细节耦合在一起的系统调试。掩码分布影响训练难度损失权重影响细节锐度判别器状态影响生成器梯度质量这些变量交织在一起就像在漆黑的房间里摸象。在初次调试时不少人容易一上来就盯着某一个指标死磕我的经验是建议把检查顺序固定好先检查数据管道再检查数值范围其次是训练稳定性最后才去精细调损失权重。数据管道错了后续再调也是自找麻烦。对于课题的扩展方向现在低配版思路一目了然把修复任务从单模DCGAN升级成多尺度层级生成在小分辨率上补结构在高分辨率上补细节。还可以尝试引入Transformer或者扩散模型的思路不过这些方向通常需要更多计算资源和更细的调参周期不过分建议在毕设周期里去冒险若时间和资源充裕再考虑。最后分享一个我在答辩前一天偶然发现的技巧在训练结束前最后2000个iteration把学习率降到原来的十分之一数值上做一个简单的学习率预热衰减。就这么一个操作肉眼可见地让修复图边缘的毛刺收敛了很多当时负责评审的同学还以为是模型结构又升级了。这种小技巧在正式论文里很难写成大的创新点但真实用起来却能带来显著的效果提升。所以当你觉得模型效果已经逼近极限时试着冻结判别器只微调生成器或者把最后阶段的噪声水平调低很可能会有意外收获。