
你在搜索引擎里敲下“GAN”三个字母大概率会看到两种完全不同的东西一种讲的是氮化镓功率器件另一种讲的是能生成人脸、画作、街景的深度学习模型。这篇文章只聊后者即生成对抗网络Generative Adversarial Network以及它最出名的三个变种pix2pix、CycleGAN 和 pix2pixHD。这四个术语经常出现在论文、GitHub 仓库和各类影像处理工具中但很多人对它们的关系一知半解既然有了 GAN为什么还要搞出 pix2pixCycleGAN 和 pix2pix 到底差在哪一步pix2pixHD 又“HD”在哪里这篇文章用一条主线把这四个模型串起来从原理、结构再到实战踩坑尽量讲得直白但在关键地方又会给出足够的深度。适合刚入门生成模型的同学也适合已经在跑代码但对设计逻辑还有些模糊的工程师。1. 先搞清楚 GAN 在解决什么问题1.1 生成器与判别器造假者和鉴定师GAN 的基本思想其实非常朴素可以用一个比喻说清楚生成器Generator就像一个造假者负责伪造画作判别器Discriminator就像一个鉴定师负责分辨哪一幅是真迹、哪一幅是赝品。造假者一开始手艺很烂伪造出来的画作一眼就能被鉴定师识破。但每次被识破之后造假者会根据被识破的原因改进工艺画出更逼真的假画。鉴定师也在同步进化不断寻找更细的破绽。两个人就这么互相“卷”下去最后达到一种状态造假者画出来的图已经能以假乱真鉴定师完全分不清真假。这个博弈过程在数学上对应一个极小极大minimax目标函数[ \min_G \max_D V(D,G) \mathbb{E}{x \sim p{data}}[\log D(x)] \mathbb{E}_{z \sim p_z}[\log(1 - D(G(z)))] ]这个公式看起来吓人实际含义不复杂。( D(x) ) 表示判别器认为一幅图是真实数据的概率目标是把真实图的概率推向 1把生成图的概率推向 0。生成器 ( G ) 的目标恰好相反它想让判别器把生成图也识别为真实图也就是让 ( D(G(z)) ) 尽量接近 1从而让 ( \log(1-D(G(z))) ) 尽可能小。两者在这一个式子里面互相拉扯最后达到纳什均衡。1.2 训练的本质交替优化的猫鼠游戏在具体实现时GAN 的训练并不是把两个网络放在一起联合梯度下降而是交替更新。每个训练 step 大概分为两步固定生成器参数更新判别器。从真实数据中取一批图像再从噪声中取样一批生成图让判别器学会把真实图和生成图分开。固定判别器参数更新生成器。生成一批新的假图让生成器的梯度方向是尽量让判别器把假图判成真图。这个交替过程在实际操作中非常容易翻车。最常见的情况是判别器太强生成器怎么骗都骗不过去梯度消失或者生成器太强判别器被瞬间击穿无法提供有效梯度。所以很多人会在训练中加入标签平滑label smoothing、梯度惩罚gradient penalty等技巧本质上都是为了让这场猫鼠游戏不至于一边倒。1.3 原始 GAN 的两个致命问题原始 GAN 能生成图片但有两个问题特别突出。第一个是模式坍塌mode collapse。简单说就是生成器找到了一个“万能的答案”——不管输入什么噪声都输出同一张能骗过判别器的图。比如训练 MNIST 数字生成时生成器可能只学会了写数字“1”因为数字“1”最容易骗过当前状态的判别器。判别器还没来得及学会识破生成器就已经收敛到这条偷懒路径上了。第二个是生成结果不可控。原始 GAN 的输入是随机噪声 ( z )你没法指定生成出来的图像内容。想让模型生成一张“晚上有灯光照射的街景”直接改噪声向量是做不到的。噪声的维度空间太大每个维度和图像语义特征之间没有可解释的映射关系。这两个问题就是后面几个模型演化的核心推动力。pix2pix 解决的是可控性问题CycleGAN 解决的是数据配对问题pix2pixHD 解决的是分辨率与细节质量问题。2. 可控生成的第一步条件 GAN 的出现思路其实很直接既然随机噪声不可控那就在输入里加入“条件”。条件可以是一个类别标签、一段文字也可以是一张图像。加入条件之后生成器不再是“从纯噪声映射到图像”而是“从噪声加条件映射到图像”。pix2pix 用的正是条件 GANconditional GAN这个框架。它的核心公式可以写成[ \mathcal{L}{cGAN}(G,D) \mathbb{E}{x,y}[\log D(x,y)] \mathbb{E}_{x,z}[\log(1 - D(x, G(x,z)))] ]这里 ( x ) 是输入条件图( y ) 是目标图。判别器收到的是“条件图 待判别的图”这个组合而不是单独的一张图。为什么改成这样因为如果把条件图也喂给判别器判别器就能学习“条件图和目标图是否匹配”这个关系而不只是“目标图是不是真实的”。举个例子假设任务是把线稿变成彩色图。输入条件 ( x ) 是线稿目标 ( y ) 是成品彩图。判别器看到的是一对图左边线稿、右边彩色图。它要判断右边这张彩图是不是“真的”并且“和左边线稿匹配”。如果生成器输出了一张质量很高但跟线稿内容完全无关的彩图判别器照样能看出问题。这就强制了生成结果必须与输入条件保持一致。3. pix2pix成对数据下的图像翻译机3.1 任务定义与整体架构pix2pix 把大量图像生成问题统一成了一个“图像翻译”问题输入一张图如线稿、语义分割图、灰度图输出另一张图如彩色图、真实街景、彩色照片。训练要求是成对数据——同一场景同时存在输入图和目标图。模型整体是编码器-解码器结构。编码器把输入图压缩成特征向量解码器再从特征向量还原出目标图。但这里有个问题图像翻译任务要求输入和输出之间保留大量空间细节。比如从语义分割图还原街景时分割图里的每个边缘、每个物体的轮廓都必须原样保留只是把“语义类别”变成“真实纹理”。如果经过压缩再还原这些细节很容易丢失。解决办法是引入 U-Net 结构即在编码器和解码器对称层之间加跳跃连接skip connection。跳跃连接的意义是解码器第 ( i ) 层向上采样时直接把编码器第 ( i ) 层保存的高分辨率特征拼接到当前特征上。这样低层细节信息就不需要全部塞进压缩后的特征向量里而是走快捷通道传给解码器。这个改动在图像翻译任务里几乎是决定性的实测下来没有跳跃连接的生成结果会糊成一团。3.2 PatchGAN 的巧妙设计判别器只看局部pix2pix 的判别器设计也很有意思用的是 PatchGAN。传统 GAN 的判别器最后输出一个标量表示整张图是真还是假。PatchGAN 输出的是一张 ( N \times N ) 的特征图每一个位置代表原图的一个局部 patch 的真假。在原始实现中patch 大小一般是 ( 70 \times 70 ) 像素。为什么只看局部就够了原因是图像翻译任务中生成器最容易忽略的是高频细节边缘、纹理而这些刚好是局部特征。全局结构往往已经被 L1 损失约束住了不需要判别器再来监督。PatchGAN 只关注局部纹理是否真实既能提供更清晰的梯度信号又比判别整张图更省显存、更稳定。pix2pix 还有一个很关键的细节损失函数并不是只用 GAN 损失而是 GAN 损失加上 L1 距离损失。L1 损失的作用是拉近生成图和目标图的整体结构。[ \mathcal{L}{L1}(G) \mathbb{E}{x,y,z}[\lVert y - G(x,z) \rVert_1] ]总损失为[ G^* \arg\min_G \max_D \mathcal{L}{cGAN}(G,D) \lambda \mathcal{L}{L1}(G) ]( \lambda ) 默认取 100。为什么 L1 比 L2 更好L2 损失会惩罚大的误差导致生成器在不确定的地方倾向于取平均值结果就是图像模糊。L1 对大误差的惩罚是线性增长的生成器会更愿意做出“锐利的选择”。这是图像生成里一个非常实用的经验。3.3 跑通 pix2pix 的实操配置建议如果你第一次在 PyTorch 里复现 pix2pix有几组参数需要格外留心。输入图像通常会归一化到 [-1, 1] 区间目标图像也一样。生成器和判别器都使用 Adam 优化器学习率设 0.0002( \beta_10.5 )。注意这个 ( \beta_1 ) 跟默认的 0.9 不一样更小的 ( \beta_1 ) 能降低动量避免训练震荡。batch size 一般是 1 或 4配合实例归一化InstanceNorm而不是批归一化BatchNorm。实例归一化核心思想是对单张图的每个通道单独做归一化不依赖 batch 内的数据统计。这个在图像生成任务里很重要因为图像翻译的每个样本风格差异可能很大如果用 batch 统计量会引入样本间的干扰。4. CycleGAN没有成对数据就造一个循环4.1 从监督到无监督的跨越pix2pix 依赖成对数据这个条件在真实场景中往往非常苛刻。把白天的街景变成夜晚的街景要获得成对数据就得在同一机位从白天拍到晚上把马变成斑马自然界根本没有对应的成对照片。CycleGAN 解决了这个问题输入只有两个图像集一个是马的图片集一个是斑马的图片集不需要任何配对。它能学会把马匹照片转换成斑马风格同时保留姿态、背景和光照等关键信息。难点在于没有配对监督怎么保证生成图在语义上与原图一致假设把一张马的照片转换成斑马模型可能学到的是“把任何图都输出成一张固定的斑马图”因为从判别器角度看只要是看起来像斑马的图都是合格的。这就是标准的无监督翻译中的模式坍塌问题。4.2 循环一致性损失唯一的关键约束CycleGAN 的突破在于提出循环一致性损失cycle consistency loss。它同时学习两个映射( G: X \rightarrow Y ) 和 ( F: Y \rightarrow X )。把一张马的照片 ( x ) 通过 ( G ) 变成斑马 ( G(x) )然后再把这张生成的斑马图通过 ( F ) 变回马 ( F(G(x)) )要求 ( F(G(x)) \approx x )。反过来也一样[ \mathcal{L}{cyc}(G,F) \mathbb{E}{x \sim p_{data}(X)}[\lVert F(G(x)) - x \rVert_1] \mathbb{E}{y \sim p{data}(Y)}[\lVert G(F(y)) - y \rVert_1] ]这个约束的意思是如果你把一个物体翻译到另一个域再翻译回来必须回到原点。它通过“环形路径重构”间接强制了翻译过程保留内容信息。为什么这么有效因为如果 ( G ) 把马的位置、姿态等结构信息丢失了那么 ( F ) 再怎么努力也无法还原出原图。循环损失产生的梯度信号在结构保持上比单纯对抗损失强得多。整体损失函数是[ \mathcal{L}(G,F,D_X,D_Y) \mathcal{L}{GAN}(G,D_Y) \mathcal{L}{GAN}(F,D_X) \lambda \mathcal{L}_{cyc}(G,F) ]( \lambda ) 默认为 10。( D_X ) 是负责判别 X 域真假的判别器( D_Y ) 负责判别 Y 域。两个判别器各管一摊。4.3 身份映射损失防止颜色与纹理的意外漂移CycleGAN 原始论文里还有一个可选的损失项——身份映射损失identity loss[ \mathcal{L}{identity}(G,F) \mathbb{E}{y \sim p_{data}(Y)}[\lVert G(y) - y \rVert_1] \mathbb{E}{x \sim p{data}(X)}[\lVert F(x) - x \rVert_1] ]意思是如果输入本来就是 Y 域的图用 ( G ) 翻译还是应该输出 Y 域的图不应该发生改变。这个损失看起来多余实际作用很大。不加身份损失时模型经常会出现“过度翻译”——比如把一张已经画好斑马纹理的图案再叠一层痕迹或者改变背景色调。加了这个损失能约束模型只在非目标域的图像上动刀色彩偏移问题明显缓解。4.4 训练配置与生成器架构CycleGAN 的生成器采用 ResNet 残差块结构。先下采样提取高层语义然后用 9 个或 6 个残差块处理再上采样恢复分辨率。残差块内部通过跳跃连接维持梯度稳定让网络在加深时不退化。训练时如果用 256x256 分辨率学习率前 100 个 epoch 保持 0.0002后 100 个 epoch 线性衰减到 0。为什么要线性衰减而不是指数衰减因为生成对抗训练后期已经接近收敛大步长的更新容易跳出最优区域。线性衰减更平滑稳定性更好。我在实际训练中遇到过一个问题生成器反复震荡loss 始终不下降。查了很久发现是数据没有做随机裁剪和水平翻转增强。CycleGAN 的样本量通常不大不增强的话模型很容易过拟合到少量样本上导致生成结果在训练集上很惊艳在测试集上崩得一塌糊涂。5. pix2pixHD高分辨率多语义的工程化升级5.1 为什么要一个新的模型pix2pix 在低分辨率256x256上效果不错但直接放大到 1024x1024 或 2048x2048 时生成结果会出现两种劣化全局结构崩坏、局部纹理模糊。另外 pix2pix 每次只能处理“一张完整图像到一张完整图像”的翻译当输入是高度结构化的语义标签图比如城市街景的逐像素类别标注时它无法有效利用实例级别的信息。这两个问题催生了 pix2pixHD论文全称《High-Resolution Image Synthesis and Semantic Manipulation with Conditional GANs》本质上是 pix2pix 在分辨率和可控性上的全面升级。5.2 粗到细生成器两级网络协同工作pix2pixHD 把生成器拆成了两个阶段G1全局生成网络处理低分辨率图负责抓全局结构。G2局部增强网络以 G1 的输出和原始分辨率特征作为输入负责补细节。具体流程是输入的先下采样到较低分辨率送入 G1G1 输出“全局草图”后再与编码器的多尺度特征拼接送入 G2。G2 在 G1 的结果上做残差细化最终输出高分辨率图。这种粗到细coarse-to-fine设计的好处在于把全局结构和高频细节两个目标拆解到两个网络每个网络的任务更单纯。G1 不必在低层特征上浪费容量G2 也不必担心全局布局显著降低了高分辨率生成的难度。5.3 多尺度判别器从不同视野检视生成结果pix2pixHD 用了三个判别器分别工作在不同尺度上。这种设计称为多尺度判别器multi-scale discriminator。三个判别器的输入分别是原图、原图下采样 2 倍、原图下采样 4 倍。最深的判别器视野最大整体上看全局结构和语义布局是否合理最浅的判别器视野最小聚焦纹理和边缘细节。损失函数取三个判别器输出损失的平均[ \min_G \max_{D_1,D_2,D_3} \sum_{k1}^3 \mathcal{L}_{cGAN}(G, D_k) ]实测中多尺度判别器对稳定训练有很大帮助。三个判别器相当于三个不同经验的评审各自关注不同层面的质量综合意见后生成器不容易在某个尺度上“作弊”。5.4 感知损失让生成结果在人的感知上更接近目标除了 GAN 损失和多尺度判别器pix2pixHD 还引入了感知损失perceptual loss。做法是把生成图和目标图都送入预训练的 VGG16 网络取若干中间层如 relu1_2、relu2_2、relu3_2、relu4_2的特征图计算这些特征之间的 L1 距离。为什么直接用像素 L1 不够还要在 VGG 特征空间算距离像素级损失只衡量每个像素点的数值差异但人眼感知图像质量更关注的是“特征级别的差异”——比如纹理是否相似、结构是否一致。VGG 特征距离更能反映人眼感知到的差异称为感知距离。5.5 实例边界图带来的可控性提升pix2pixHD 的一个重要创新是支持输入实例边界图instance boundary map。也就是说除了语义分割标签哪些区域属于道路、哪些属于建筑、哪些属于树木还能额外输入每个物体的边界轮廓。实例边界图让模型能够区分“同一语义类别中的不同个体”。比如一片建筑区域里有两栋相邻的楼语义分割图把两栋楼标成一类实例边界图可以额外告诉模型“这两栋楼中间有一条边界”。这让模型能生成更自然的物体边界和阴影关系而不是把整个建筑区域糊成一大块。5.6 显存爆炸的处理思路pix2pixHD 在 2048x1024 分辨率下训练需要大约 8GB 以上的显存这还是在生成器足够精简的前提下。如果你只有一块消费级显卡有几个降显存的策略减小 batch size 到 1配合实例归一化使用。把输入分辨率降到 1024x512多数语义生成任务在这个分辨率下视觉效果差别不大。使用梯度累积gradient accumulation每步只算 1/4 的 batch 梯度累积 4 步再更新参数。关闭判别器不需要的层或减少判别器数量只用两个尺度判别器。不过要注意的是分辨率降低后VGG 感知损失的权重、判别器的感受野都会变化参数可能需要重新调整不能指望同一个配置完全换分辨率直接用。6. 四个模型的选型矩阵与踩坑实录6.1 什么场景选哪个模型模型数据要求核心能力典型应用输出分辨率GAN无需配对单域数据从噪声生成图像生成人脸、图像的分布建模通常较低pix2pix成对数据条件图像翻译线稿上色、分割图转街景、灰度图着色256~512CycleGAN两个域的独立数据集无配对风格迁移马变斑马、照片转油画、白天转黑夜256pix2pixHD成对数据 可选实例边界高分辨率、多语义可控图像合成城市街景合成、语义布局转照片1024一句话总结有配对数据用 pix2pix 系列没有配对数据用 CycleGAN需要高清输出优先考虑 pix2pixHD只是想生成全新图像、没有任何约束条件的用原始 GAN 或改进版 GAN。6.2 训练不稳定的标准排查路径我见过不少朋友训练这些模型时 loss 反复横跳第一反应是调学习率或者换损失函数权重。我建议按照下面这个优先级排查第一确认两个网络的更新频率是否均衡。GAN 训练时如果判别器 loss 降到接近 0说明判别器已经碾压生成器。此时可以降低判别器学习率或者给判别器加谱归一化spectral normalization。反过来如果生成器 loss 几乎不变判别器 loss 也飘忽不定说明判别器能力太弱反而要给判别器加容量。第二检查数据和归一化。确认图像预处理已经归一化到 [-1, 1] 还是保持 [0, 255]。不同实现要求不一样弄错的话 loss 永远降不下去。第三检查生成器输出是否过饱和。如果生成的图像对比度极高、颜色过于鲜艳通常是判别器对真实数据和生成数据的分布区分过于容易可以调大 L1 损失的 lambda 权重让生成器更多地依赖重建损失来引导训练。第四如果训练初期就出现 NaN先检查学习率是否过大。Adam 的默认学习率 0.001 对 GAN 来说偏大降到 0.0002 能解决绝大多数 NaN 问题。6.3 数据准备阶段最容易忽略的细节数据质量直接决定模型质量这个道理在 GAN 领域尤其明显。原图有重复样本CycleGAN 训练时模型会把重复样本当重点样本盲目学习导致生成结果偏向某些特定姿态。曝光不均的数据会让生成器把曝光异常也当成域特征学进去。对于 pix2pix 类任务尤其要注意配对图的对齐精度。输入图和目标图如果差了一个像素L1 损失就会给边缘区域模型带来极大干扰因为模型永远无法同时满足空间偏移的目标图。实际上这种情况下模型会倾向于生成模糊结果来“平均”掉偏移误差。预处理管线建议保持固定顺序先统一缩放尺寸再做中心裁剪或随机裁剪最后做翻转增强。色彩空间保持一致RGB 三通道顺序不要搞混。6.4 提升效果的三条经验第一CycleGAN 任务可以适当增强循环一致性损失权重。默认的 lambda10 是作者在大量实验下的选择但在颜色风格差异特别大的域之间翻译时比如照片转油画可以适当增加到 20。因为颜色风格变化大的任务更需要保留结构而循环一致性结构起着关键保护作用。第二pix2pix 家族更新参数时生成器和判别器的 loss 权重不要固定不变。训练中期比如前 1/3 轮次结束后如果判别器能力已经到位可以把 GAN 损失权重调低、L1 损失权重调高让生成器做更精细的重建微调。第三测试时把 dropout 关掉。很多实现中生成器网络里有 dropout 层训练时用于增强泛化性但推理时必须切换到 eval 模式。很多人测试结果不稳定就是这个原因。7. 这几个模型的后续演变可以怎么追掌握了这四个模型的原理再看目前各种生成模型论文会容易很多。比如 pix2pix 的后续工作 pix2pix3D 把图像翻译扩展到了三维场景CycleGAN 的循环一致性思想也被引入了视频域风格迁移pix2pixHD 的粗到细结构在超分辨率领域的很多 SOTA 模型里也能看到影子。还有一个值得关注的方向是把 GAN 和扩散模型结合。扩散模型近几年在图像生成质量上表现很好但迭代生成的速度还赶不上 GAN。有些工作尝试用 GAN 的结构加速扩散采样或者反过来用扩散模型的生成能力辅助 GAN 判别器的特征提取。这个交叉地带还有不少可以挖的空间。我个人的体会是GNN 系列模型的底子其实非常经典它把“生成”这个看似玄学的任务拆成了两个互相博弈、交替优化的子网络这种思想本身比任何具体实现都重要。被后续各种模型取代的是具体的损失函数和网络结构但“对抗博弈”这个大框架至今仍然是很多生成任务绕不开的出发点。如果你能把这篇里的原理吃透再动手跑至少两个模型的代码后续看新论文的路会顺很多。