
ACGAN 这个东西我最早是在做一个“按类别生成手写数字”的小实验时被它绊了一跤。当时用最朴素的 GAN 生成出来的数字肉眼看着都像模像样可一旦要求“给我生成一批全是 7 的图”模型就开始装傻——它根本不知道自己要生成什么类别。后来换成 ACGAN问题一下就通了。所以这篇不打算写成论文复述而是把我从“为什么普通 GAN 做不到条件生成”到“ACGAN 到底怎么把类别信息塞进生成器”再到“训练时哪些参数会咬人”这一整条链路拆开讲清楚。如果你正在做条件图像生成、想搞明白 ACGAN 的辅助分类器到底在干什么或者单纯被 GAN 训练的不稳定折磨过这篇应该能帮你少走几段弯路。1. 先搞清楚 ACGAN 到底想解决什么1.1 普通 GAN 的“无标签盲区”普通 GAN 的生成器只接收一个随机噪声向量 z判别器只判断“真/假”。这套结构在无条件生成上很能打但它有一个天然缺陷你没法告诉生成器“我要什么”。噪声向量里确实隐含了某种语义分布可这个分布是模型自己学出来的、不可控的。你想生成一只猫它可能给你一条狗而且你没有任何接口去纠正它。这个问题的本质是生成过程缺少一个显式的条件变量。普通 GAN 的损失函数里只有真假两个信号没有任何一项在约束“生成内容的类别属性”。所以哪怕训练到收敛生成器也只是在拟合整体数据分布而不是在拟合“给定类别下的条件分布”。我在早期实验里踩过一个很典型的坑以为只要在训练集里多放某类样本生成器就会偏向生成那一类。实测下来确实会偏但偏得非常粗糙——你没法精确控制而且一旦类别比例变化整个分布就跟着漂。这就是无条件生成的根本局限。1.2 ACGAN 的两个信号来源ACGANAuxiliary Classifier GAN的核心改动其实很克制给判别器加一个辅助分类头。判别器不再只输出“真/假”而是同时输出两样东西——真假概率以及输入图像的类别概率。生成器这边则接收两部分输入噪声 z 和类别标签 c。这么一改整个训练信号就变成了双通道真假信号保证生成图像在视觉上像真实数据类别信号保证生成图像在语义上属于指定类别。关键在于这个类别信号是双向作用的。对判别器来说它要在真实图像上学会分类用真实标签监督也要在生成图像上学会识别出“这张图声称自己是类别 c但它到底像不像 c”。对生成器来说它要骗过判别器的真假判断同时还要让判别器把生成图分到自己指定的类别上。两个目标互相拉扯最终逼出一个既能生成逼真图像、又能精确控制类别的模型。我用一个生活化的类比来解释普通 GAN 像一个只会说“这画好看/不好看”的评委ACGAN 的判别器则是一个既看画功、又看“画的是不是题目要求的东西”的评委。生成器要同时讨好这两个维度自然就学会了“按题作画”。1.3 和 cGAN 的区别在哪很多人会把 ACGAN 和 cGAN条件 GAN混为一谈两者确实都做条件生成但信息注入的位置不同。cGAN 是把条件标签同时喂给生成器和判别器判别器判断的是“这张图在给定条件下真不真”。ACGAN 则是把标签喂给生成器判别器通过辅助分类头去反推类别判别器本身不直接接收标签作为输入它接收的是图像然后预测类别。这个差别带来一个实际后果ACGAN 的判别器多了一个分类任务相当于一种多任务学习分类损失起到了正则化作用往往能让判别器的特征表示更有语义结构。而 cGAN 的判别器更像一个纯粹的条件真伪判断器。两者没有绝对优劣但 ACGAN 在类别数较多、需要强类别控制时通常更省心因为分类头天然适配多类别场景。2. 把结构拆开生成器与判别器各自在干什么2.1 生成器的输入拼接方式生成器接收 z 和 c。c 通常是 one-hot 向量类别数 K 维z 是比如 100 维的噪声。最直接的做法是把两者**拼接concatenate**成一个向量再送进全连接层或卷积层。拼接的位置有两种常见选择一是在最输入端拼接二是把标签通过 embedding 映射后与噪声在中间层融合。我实测下来对于图像任务在输入端直接拼接 one-hot 向量是最省事也最稳的做法尤其是类别数不大的时候比如 10 类手写数字。原因很朴素one-hot 维度低拼接后不会显著增加输入维度而且全连接第一层很容易学会把标签信息和噪声信息混合。如果类别数很大比如上百类one-hot 拼接会让输入维度膨胀这时候用 embedding 把标签压到低维再拼接更合理。这里有个容易忽略的细节标签的拼接顺序要和噪声的语义对齐。听起来玄乎其实就是别把标签放在噪声中间随便插统一放在末尾或开头让网络结构稳定。我见过有人把标签插在噪声向量中间训练也能跑但收敛明显更慢因为第一层的权重需要额外学习“哪一段是标签”。2.2 判别器的双头设计判别器的结构是 ACGAN 的灵魂。它共享一个特征提取主干卷积层堆叠然后在末端分出两个头真假头输出 1 维或 2 类 softmax判断真伪分类头输出 K 维 softmax预测类别。两个头共享主干特征这是多任务学习的典型结构。共享主干的好处是特征复用——判断真伪需要的纹理、边缘特征和判断类别需要的语义特征有大量重叠共享能减少参数量、加快收敛。但共享也带来一个隐患两个任务的梯度可能打架。如果真假任务和分类任务的难度差异太大主干特征会被其中一个任务主导另一个任务学不好。我的经验是分类头不要做得太浅。如果分类头只有一层线性层它从主干特征里提取类别信息的能力有限容易导致类别控制不精准。给分类头加一层隐藏层比如 128 或 256 维再接输出实测类别准确率会明显提升。真假头则可以简单一些一层线性足够。2.3 损失函数的组合逻辑ACGAN 的损失由两部分构成判别器和生成器各自的目标不同这里必须分清楚否则代码写出来训练必崩。对判别器来说真实图像真假头要判为真分类头要分类正确用真实标签监督生成图像真假头要判为假分类头不参与或者说生成图像的类别标签在这里不作为分类监督信号因为判别器要做的只是识别它是假的。对生成器来说生成图像喂给判别器真假头要判为真骗过判别器分类头要判为生成时指定的类别 c这是生成器要努力达成的目标。用公式化的语言说判别器的分类损失只在真实样本上计算生成器的分类损失在生成样本上计算目标是让判别器把生成样本分到指定类。这个不对称是 ACGAN 的精髓也是很多人写错的地方——如果判别器在生成样本上也用“指定类别”去监督分类头那判别器就被生成器的标签带偏了分类能力会退化。提示判别器在生成样本上的分类输出只用来给生成器提供梯度不参与判别器自身的分类损失计算。这一点在实现时最容易搞混建议在代码里把两个损失显式分开写。3. 训练流程的完整落地步骤3.1 数据与标签的准备以手写数字这类经典任务为例数据准备阶段要做两件事图像归一化到 [-1, 1]配合生成器输出层的 tanh标签转成 one-hot。归一化区间要和生成器最后一层的激活函数匹配用 tanh 就归一到 [-1,1]用 sigmoid 就归一到 [0,1]这个不匹配是新手最常见的翻车点——生成出来的图全是灰蒙蒙的就是因为区间对不上。标签 one-hot 的维度等于类别数 K。这里有个实操细节生成器采样标签时要保证每个 batch 内类别尽量均衡。如果随机采样导致某个 batch 里全是同一类判别器的分类头在这个 batch 上就学不到区分性。我的做法是每个 batch 按类别轮询采样或者至少保证每个类别在 batch 内都有出现。这个改动看似小但对训练稳定性影响很大。3.2 判别器单步更新一个训练步里判别器要处理两批数据一批真实图像带真实标签一批生成图像带生成时用的标签但分类损失不算。具体流程取真实图像和对应标签前向判别器得到真假输出和分类输出计算真实图像的真假损失目标为真和分类损失目标为真实标签用生成器生成一批假图像标签为随机采样的 c假图像前向判别器计算真假损失目标为假分类损失不计入判别器更新把真实部分和假部分的真假损失相加加上真实部分的分类损失反向传播更新判别器。这里要注意生成假图像时生成器的参数不更新要 detach否则判别器的更新会污染生成器。PyTorch 里对生成结果调用.detach()是标准操作。3.3 生成器单步更新生成器更新时判别器参数冻结。流程是采样噪声 z 和标签 c生成假图像假图像前向判别器此时不 detach梯度要传回生成器计算真假损失目标为真骗判别器和分类损失目标为 c让判别器把假图分到 c两者相加反向传播只更新生成器参数。生成器的分类损失是 ACGAN 区别于普通 GAN 的关键项。它的作用是生成器不仅要让图看起来真还要让判别器“认得出这是 c 类”。判别器在这里充当了一个免费的分类器监督信号。3.4 两个损失权重的平衡真假损失和分类损失的量级往往不同直接相加可能让其中一个主导训练。常见的做法是给分类损失乘一个权重系数比如 1.0 或 0.5。我实测下来分类损失权重取 1.0 在多数任务上表现稳定但如果发现生成图像的类别准确率上不去可以适当调高到 1.5 左右反过来如果图像质量明显下降模式崩塌、模糊说明分类信号太强压制了真假信号要调低。这个权重没有万能值和数据集、网络容量都相关。建议在训练初期观察两个损失的变化曲线如果分类损失下降很快但真假损失震荡剧烈说明分类项太强势如果分类损失几乎不降说明权重太小或分类头容量不足。4. 那些训练中真实会咬人的坑4.1 判别器太强导致生成器梯度消失这是 GAN 家族的通病ACGAN 也不例外而且因为判别器多了分类任务它往往学得更快、更强。表现是训练早期判别器的真假准确率迅速逼近 100%生成器的损失不再下降生成的图越来越糊或者干脆变成同一张图。根因是判别器太容易区分真假导致生成器收到的梯度信号趋近于零。应对手段有几个我按有效性排序降低判别器更新频率比如判别器每更新 2 次、生成器更新 1 次给生成器更多追赶机会给判别器加输入噪声在真实和假图像上叠加小幅高斯噪声削弱判别器的锐度标签平滑把真实标签从 1.0 改成 0.9避免判别器过度自信控制判别器容量别把判别器做得比生成器深太多。我个人的习惯是标签平滑 判别器更新频率略低于生成器这两个组合起来对稳定性的提升最明显。4.2 分类头学偏只认真实样本有时候会发现判别器在真实图像上分类很准但对生成图像的分类输出几乎均匀分布导致生成器拿不到有效的类别梯度。这通常是因为判别器在训练中把“分类”和“真实”强绑定了——它学到的特征是“只有真实图像才有明确类别”生成图像在它眼里就是“无类别”的。解决办法是让判别器的分类头在训练中同时见到真实和生成样本的特征分布。虽然分类损失只在真实样本上算但生成样本前向时分类头也在输出这些输出会通过生成器的损失间接影响分类头的梯度。如果这个间接信号太弱可以适当提高生成器分类损失的权重逼判别器的分类头对生成样本也给出有区分度的输出。4.3 模式崩塌在条件生成下的特殊表现普通 GAN 的模式崩塌表现为生成多样性下降ACGAN 的模式崩塌更隐蔽每个类别内部多样性下降。也就是说你让它生成 10 个类它每类都能生成但每类只生成一两种固定样式。这种崩塌在整体指标上不容易看出来因为类别覆盖是完整的。排查方法是按类别分别统计生成样本的多样性比如类内像素方差、或者用预训练特征算类内距离。如果某个类别的类内多样性明显低于其他类说明该类发生了局部模式崩塌。应对上除了常规的多样性正则还可以在采样标签时对少数类别适当加权避免某些类别训练不足。4.4 标签噪声与 one-hot 的边界情况如果类别标签本身有噪声比如标注错误ACGAN 的分类头会把这些噪声学进去进而污染生成器的条件信号。这在真实数据集上很常见。一个务实的做法是训练前先清洗标签或者在分类损失上用 label smoothing 缓解。另外one-hot 向量在拼接时要注意 dtype 和维度对齐我见过因为 one-hot 维度比实际类别数少一位导致索引越界的低级错误调试了半天才发现是数据预处理的问题。5. 评估怎么判断 ACGAN 真的学到了条件控制5.1 分类准确率不是唯一指标最直观的评估是拿一个独立的分类器在真实数据上训练好的去判断生成图像的类别看它能否正确识别出生成时指定的类别。这个指标叫条件准确率能直接反映类别控制能力。但它有局限——分类器可能对某些视觉特征过拟合导致准确率高但生成质量差。所以条件准确率要和图像质量指标一起看。常用的组合是条件准确率 FID衡量生成分布与真实分布的距离。两个指标都好看才说明模型既控制了类别又保证了质量。如果条件准确率高但 FID 很差说明生成器为了迎合分类信号牺牲了真实感这时候要回头调分类损失权重。5.2 按类别拆分的可视化检查指标之外我强烈建议做按类别的可视化网格每个类别生成一行每行多个样本。人眼扫一遍就能发现很多指标看不出的问题——比如某个类别的样本全是同一个朝向、某个类别的边缘有明显伪影、类别之间的过渡样本比如 3 和 8 混淆。这种检查在调试阶段比任何数字都直接。我踩过的一个坑是整体 FID 看着不错但可视化一看类别 1 生成的样本全是竖直的细线完全没有真实手写 1 的倾斜变化。这种类内多样性缺失在聚合指标里被平均掉了只有拆开看才暴露。5.3 插值实验验证条件连续性还有一个很有意思的验证固定噪声 z把类别标签从 0 逐步插值到 9在 one-hot 空间做线性插值再归一化观察生成图像是否平滑过渡。如果模型真的学到了类别语义插值过程中图像应该呈现渐变而非突变。这个实验能侧面反映类别嵌入空间的连续性对判断模型是否“理解”了类别结构很有帮助。如果插值结果全是噪声或者突变说明类别信号没有被有效编码进生成过程。6. 从 ACGAN 出发的扩展思路6.1 多条件场景的拼接策略ACGAN 天然支持多类别条件但如果条件不止一个维度比如同时控制类别和风格就需要设计多条件的融合方式。常见做法是把多个 one-hot 分别 embedding 后拼接或者用条件拼接后再过一层融合网络。这里的关键是各条件信号的权重平衡否则强条件会压制弱条件。我在一个双条件实验里发现直接拼接两个 one-hot 会导致其中一个条件几乎失效后来改成各自 embedding 到相同维度再相加效果明显改善。6.2 辅助分类头的正则化价值ACGAN 的分类头不只是为了条件生成它本身是一种正则化。多任务学习让判别器的特征表示更有结构这在数据量有限时尤其有价值。我对比过同样容量下普通 GAN 和 ACGAN 的判别器特征后者在中间层的特征聚类明显更清晰。所以即使你不需要条件生成给判别器加一个辅助分类任务如果有标签也可能提升训练稳定性这是一个容易被忽略的副产品。6.3 和投影判别器的取舍后来出现的投影判别器projection discriminator在处理条件时用了内积方式把条件融入判别和 ACGAN 的辅助分类头是两条路线。投影判别器在类别数很多时扩展性更好因为它不需要为每个类别维护分类输出ACGAN 则在类别数适中、需要显式分类监督时更直接。选哪个取决于你的类别规模和是否需要分类头带来的正则化效果。我的建议是类别数在几十以内、想要简单可控ACGAN 足够类别数上百、条件维度复杂可以考虑投影思路。我在实际项目里最后固定下来的配置是生成器输入端拼接 one-hot判别器共享主干加双头分类头带一层隐藏层分类损失权重 1.0标签平滑 0.9判别器每步更新、生成器同步更新但学习率略高。这套配置在多个中小规模类别生成任务上都能稳定收敛类别准确率能到 90% 以上。真正需要反复调的往往不是结构而是那两个损失的平衡和判别器的强度——这两点吃透了ACGAN 其实是个相当听话的模型。