ACGAN的Pytorch实现:条件生成与类别控制实战
简介这份资源是面向深度学习初学者与生成模型爱好者的 ACGAN PyTorch 实现代码包帮助读者理解如何在生成对抗网络中引入类别条件信息从而生成指定类别的样本。包内共 4 个文件以 3 个 py 脚本和 1 个 md 说明文档为主py 文件分别承担模型定义、训练主流程与判别逻辑等核心功能md 文档则用于补充项目说明压缩包整体约 3KB结构精简、便于快速阅读与二次修改。目前已有 823 人学习下载适合作为入门 ACGAN 的轻量级参考。通过这份代码读者可以掌握生成器、判别器与辅助分类器的网络搭建方式理解对抗损失与类别指导损失的组合思路并借鉴交替优化生成器与判别器的训练策略为图像生成、数据增强等场景打下实践基础。1. ACGAN 的 Pytorch 实现从条件生成到类别控制的落地路径如果你手头有一份 ACGAN 的 Pytorch 实现代码包却不确定它到底比普通 GAN 多解决了什么问题那这篇笔记就是写给你的。ACGANAuxiliary Classifier GAN的核心思路并不复杂在判别器上额外挂一个分类头让判别器不仅要判断真假还要判断样本属于哪个类别。这样一来生成器就被迫学会“按标签生成”——你给它一个类别标签它就能生成对应类别的图像而不是随机碰运气。这份资源适合已经跑通过基础 GAN、想进一步做条件生成和类别控制的从业者也适合需要快速复现论文效果、拿来做图像增广或数据合成的工程场景。代码基于 Pytorch结构清晰能直接跑通但有几个参数和训练细节如果不注意很容易翻车。2. 判别器双头结构为什么多一个分类头就能控制类别2.1 ACGAN 与普通 GAN 的结构差异普通 GAN 的判别器只输出一个标量表示输入图像为真的概率。ACGAN 的判别器则输出两个东西一个是真假判别 logits另一个是类别分类 logits。这个设计看起来只是多了一个分支但它改变了整个博弈的性质。生成器的输入除了噪声向量 z还拼接了一个类别标签 c通常是 one-hot 或 embedding。生成器要同时骗过判别器的真假判断和类别判断所以它必须学会把类别信息编码进生成图像中。从梯度传播的角度看判别器的分类损失会反向传播到生成器迫使生成器生成的图像在特征空间上靠近对应类别的真实样本。这比单纯用真假损失来训练要稳定得多因为类别标签提供了额外的监督信号。常见做法是让判别器共享卷积主干然后在最后分成两个全连接头一个输出 1 维真假 logits一个输出 N 维类别 logits。2.2 判别器与生成器的代码实现下面这份代码是 ACGAN 判别器的典型写法主干用卷积层提取特征然后分两个头输出。注意这里用了nn.LeakyReLU和nn.Dropout来防止判别器过强。import torch import torch.nn as nn class Discriminator(nn.Module): def __init__(self, num_classes10, img_channels1, feature_dim128): super(Discriminator, self).__init__() # 共享卷积主干 self.conv nn.Sequential( nn.Conv2d(img_channels, 64, 4, 2, 1), # 输入 28x28 - 14x14 nn.LeakyReLU(0.2, inplaceTrue), nn.Conv2d(64, 128, 4, 2, 1), # 14x14 - 7x7 nn.BatchNorm2d(128), nn.LeakyReLU(0.2, inplaceTrue), nn.Conv2d(128, 256, 4, 2, 1), # 7x7 - 3x3 nn.BatchNorm2d(256), nn.LeakyReLU(0.2, inplaceTrue), ) self.fc nn.Linear(256 * 3 * 3, feature_dim) # 真假判别头 self.adv_head nn.Linear(feature_dim, 1) # 类别分类头 self.cls_head nn.Linear(feature_dim, num_classes) def forward(self, x): feat self.conv(x) feat feat.view(feat.size(0), -1) feat self.fc(feat) adv_logits self.adv_head(feat) # 真假 logits cls_logits self.cls_head(feat) # 类别 logits return adv_logits, cls_logits这段代码里num_classes根据数据集类别数设置MNIST 是 10CIFAR-10 也是 10。feature_dim是共享特征维度太小会导致分类能力不足太大会让判别器过强。我一般从 128 起步如果训练不稳定再降到 64。adv_head输出 1 维配合BCEWithLogitsLoss使用cls_head输出num_classes维配合CrossEntropyLoss使用。注意卷积层的通道数和 kernel size 要根据输入图像尺寸调整上面是按 28x28 写的如果换成 32x32 或 64x64需要重新计算全连接层的输入维度。生成器的结构相对简单输入是噪声向量 z 和类别 embedding 的拼接然后通过全连接层和转置卷积逐步上采样。class Generator(nn.Module): def __init__(self, num_classes10, latent_dim100, img_channels1): super(Generator, self).__init__() self.label_emb nn.Embedding(num_classes, latent_dim) self.fc nn.Sequential( nn.Linear(latent_dim * 2, 256 * 3 * 3), nn.BatchNorm1d(256 * 3 * 3), nn.ReLU(inplaceTrue), ) self.deconv nn.Sequential( nn.ConvTranspose2d(256, 128, 4, 2, 1), # 3x3 - 7x7 nn.BatchNorm2d(128), nn.ReLU(inplaceTrue), nn.ConvTranspose2d(128, 64, 4, 2, 1), # 7x7 - 14x14 nn.BatchNorm2d(64), nn.ReLU(inplaceTrue), nn.ConvTranspose2d(64, img_channels, 4, 2, 1), # 14x14 - 28x28 nn.Tanh(), ) def forward(self, z, labels): label_emb self.label_emb(labels) # (batch, latent_dim) x torch.cat([z, label_emb], dim1) # 拼接噪声和标签 x self.fc(x) x x.view(x.size(0), 256, 3, 3) return self.deconv(x)这里latent_dim是噪声维度通常取 100 或 128。label_emb把离散标签映射成连续向量和噪声拼接后一起送入生成器。注意Tanh把输出限制在 [-1, 1]所以真实图像也要归一化到同一范围否则判别器会学到“只要看数值范围就能分辨真假”的捷径导致生成器学不到东西。2.3 损失函数与训练循环ACGAN 的损失由两部分组成判别器的真假损失和类别损失生成器同样要优化这两部分。下面是一个训练步骤的核心代码。criterion_adv nn.BCEWithLogitsLoss() criterion_cls nn.CrossEntropyLoss() for epoch in range(num_epochs): for imgs, labels in dataloader: batch_size imgs.size(0) real_imgs imgs.to(device) real_labels labels.to(device) # 训练判别器 optimizer_D.zero_grad() z torch.randn(batch_size, latent_dim).to(device) fake_labels torch.randint(0, num_classes, (batch_size,)).to(device) fake_imgs G(z, fake_labels).detach() real_adv, real_cls D(real_imgs) fake_adv, fake_cls D(fake_imgs) # 真假损失真实样本标签为 1生成样本标签为 0 loss_real_adv criterion_adv(real_adv, torch.ones_like(real_adv)) loss_fake_adv criterion_adv(fake_adv, torch.zeros_like(fake_adv)) # 类别损失真实样本用真实标签生成样本用生成时指定的标签 loss_real_cls criterion_cls(real_cls, real_labels) loss_fake_cls criterion_cls(fake_cls, fake_labels) loss_D loss_real_adv loss_fake_adv loss_real_cls loss_fake_cls loss_D.backward() optimizer_D.step() # 训练生成器 optimizer_G.zero_grad() z torch.randn(batch_size, latent_dim).to(device) gen_labels torch.randint(0, num_classes, (batch_size,)).to(device) gen_imgs G(z, gen_labels) gen_adv, gen_cls D(gen_imgs) # 生成器希望判别器把生成样本判为真且分类正确 loss_G_adv criterion_adv(gen_adv, torch.ones_like(gen_adv)) loss_G_cls criterion_cls(gen_cls, gen_labels) loss_G loss_G_adv loss_G_cls loss_G.backward() optimizer_G.step()这段训练循环里判别器更新时用了detach()切断生成器梯度这是标准做法。生成器损失里loss_G_adv让判别器把生成样本判为真loss_G_cls让判别器把生成样本分类到指定标签。两个损失的权重通常是 1:1但如果分类准确率上不去可以适当调大loss_G_cls的系数。我一般会监控判别器在真实样本上的分类准确率如果低于 80%说明判别器分类头没学好生成器的类别控制也会受影响。3. 数据准备与标签对齐别让 one-hot 和 embedding 打架3.1 数据集加载与归一化ACGAN 对数据归一化很敏感。因为生成器最后用了Tanh输出范围是 [-1, 1]所以真实图像必须归一化到同一范围。常见做法是用transforms.Normalize(mean(0.5,), std(0.5,))把 [0,1] 映射到 [-1,1]。如果漏了这一步判别器会很快学会通过数值范围区分真假生成器梯度消失训练直接崩掉。from torchvision import datasets, transforms transform transforms.Compose([ transforms.Resize(28), transforms.ToTensor(), transforms.Normalize(mean(0.5,), std(0.5,)) # 归一化到 [-1, 1] ]) train_dataset datasets.MNIST(root./data, trainTrue, downloadTrue, transformtransform) train_loader torch.utils.data.DataLoader(train_dataset, batch_size64, shuffleTrue, num_workers2)这里batch_size我一般设 64 或 128。太小会导致 BatchNorm 统计量不稳定太大则显存吃紧。num_workers根据机器 CPU 核数调整Windows 下如果报错就设 0。3.2 标签格式embedding 与 one-hot 的选择生成器里用的是nn.Embedding输入是整数标签输出是连续向量。判别器的分类头输出的是 logits配合CrossEntropyLoss使用标签也是整数。所以整个流程里标签始终保持整数格式不需要手动转 one-hot。这一点和有些实现不一样有些代码会在生成器里用 one-hot 拼接那样也可以但 embedding 更灵活参数量也更少。注意fake_labels和gen_labels的生成方式用torch.randint(0, num_classes, (batch_size,))随机采样。这样每个 batch 里各类别分布大致均匀不会出现某个类别主导训练的情况。如果数据集本身类别不平衡可以考虑按类别权重采样但 ACGAN 原论文里没有特别处理我一般先跑通再调。3.3 训练轮数与监控指标ACGAN 在 MNIST 上通常 50 到 100 个 epoch 就能看到比较清晰的类别控制效果。监控指标除了生成器损失和判别器损失更重要的是看判别器在真实样本上的分类准确率以及生成样本的类别一致性。我一般每 10 个 epoch 生成一批固定噪声和固定标签的图像肉眼观察类别是否对应。如果发现生成的数字和标签对不上先检查标签拼接顺序有没有搞反再检查分类损失有没有正确反向传播。4. 训练不收敛与模式崩溃ACGAN 常见问题排查4.1 判别器分类准确率始终上不去现象训练几十个 epoch 后判别器在真实样本上的分类准确率还在 50% 左右生成图像模糊且类别混乱。原因最常见的是学习率设置不当。判别器分类头是新增的如果学习率和卷积主干一样可能更新太快或太慢。另一个原因是feature_dim太小共享特征不足以同时支撑真假判别和分类。解决把判别器学习率降到 1e-4 或 2e-4生成器学习率保持 2e-4。如果还不行把feature_dim从 128 提到 256或者给分类头单独设一个更大的学习率。我遇到过一种情况是 BatchNorm 的 momentum 默认 0.1 导致统计量波动太大改成 0.8 后分类准确率明显提升。4.2 生成器只生成某一类图像现象不管输入什么标签生成器输出的都是同一类数字比如全是“1”。原因这是典型的模式崩溃在条件生成里的表现。判别器分类头太强生成器发现只要生成某一类就能骗过分类损失于是放弃其他类别。也可能是loss_G_cls权重太低生成器不关心类别是否正确。解决把loss_G_cls的权重从 1 调到 2 或 3同时给判别器加 Dropout比如 0.3削弱它的分类能力。另外检查一下fake_labels是不是每个 batch 都重新随机采样了如果固定不变生成器会只学那几个标签。4.3 损失震荡剧烈生成图像质量忽好忽坏现象损失曲线上下跳动生成的图像有时清晰有时全是噪声。原因判别器和生成器更新频率不平衡或者优化器动量参数不合适。ACGAN 比普通 GAN 多了一个分类损失梯度尺度可能差一个量级。解决把优化器从 Adam 换成 RMSprop或者把 Adam 的betas从 (0.5, 0.999) 改成 (0.5, 0.9)。另一个办法是每训练判别器 2 次才训练生成器 1 次让判别器先稳定下来。我一般会先跑 5 个 epoch 只训练判别器等分类准确率上到 70% 再开始交替训练。4.4 显存溢出或训练速度极慢现象batch_size 设到 64 就 OOM或者每个 epoch 要跑十几分钟。原因生成器的全连接层参数量太大或者数据加载num_workers设得太小。解决把latent_dim从 100 降到 64feature_dim从 128 降到 64能省不少显存。另外把num_workers设成 CPU 核数的一半pin_memoryTrue也能加速数据搬运。如果还是慢检查一下是不是在 CPU 上跑device torch.device(cuda if torch.cuda.is_available() else cpu)这行别忘了。4.5 生成的图像有网格状伪影现象生成图像上出现规律性的方格或条纹。原因转置卷积的 kernel size 和 stride 不匹配导致重叠区域计算不均匀。常见于kernel_size4, stride2, padding1的组合理论上输出尺寸是输入的两倍但边界处理容易出问题。解决把转置卷积换成nn.Upsample加普通卷积或者调整 kernel size 为 3、stride 为 2、padding 为 1、output_padding 为 1。我一般会在生成器最后加一个nn.Conv2d做平滑能明显减少网格伪影。5. 进阶技巧用类别插值和条件验证确认模型真的学到了训练完之后怎么确认模型不是“假装”学会了类别控制我常用的方法是做类别插值和条件验证。类别插值是在 embedding 空间里对两个不同类别的标签向量做线性插值观察生成图像是否平滑过渡。如果模型真的学到了类别语义插值中间应该出现两类混合的形态而不是突变。# 假设 label_emb 是生成器里的 Embedding 层 label_a torch.tensor([0]).to(device) # 类别 0 label_b torch.tensor([1]).to(device) # 类别 1 emb_a G.label_emb(label_a) emb_b G.label_emb(label_b) alphas torch.linspace(0, 1, 10).to(device) for alpha in alphas: emb_interp emb_a * (1 - alpha) emb_b * alpha z torch.randn(1, latent_dim).to(device) x torch.cat([z, emb_interp], dim1) x G.fc(x).view(1, 256, 3, 3) img G.deconv(x) # 保存或展示 img这段代码里alphas从 0 到 1 变化生成图像应该从类别 0 逐渐过渡到类别 1。如果中间出现完全无关的类别说明 embedding 空间没有连续性模型可能只是记住了几个离散点。另一个验证方法是固定噪声 z只改变标签看生成图像是否按标签变化。如果换标签后图像几乎不变说明生成器忽略了标签输入需要回头检查拼接顺序和分类损失。还有一个实用技巧是给判别器分类头加标签平滑label smoothing把真实标签从 1 改成 0.9能防止判别器过度自信生成样本的多样性会更好。我一般在训练后期最后 20 个 epoch才加标签平滑前期不加让判别器先学清楚类别边界。从那以后我每次跑 ACGAN 都会先做一轮 5 个 epoch 的判别器预热再检查标签拼接顺序最后才开完整训练。这个习惯帮我省了很多次重新跑一天的时间。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

Skill框架实战:从会写代码到指挥AI写代码的认知转变

Skill框架实战:从会写代码到指挥AI写代码的认知转变

1. 从"会写代码"到"会指挥AI写代码"的认知转变Skill框架这个词最近在开发者圈子里被反复提起,但很多人第一次听到时的反应是:这不就是把提示词包装了一下吗?我一开始也这么想,直到在一个真实项目里被狠狠教育…

2026/10/13 8:18:35 阅读更多 →
Claude Code 实战教程:从安装到跑通第一个真实任务

Claude Code 实战教程:从安装到跑通第一个真实任务

1. 这个工具到底能干什么,为什么值得花时间学 第一次听说 Claude Code 的时候,我以为它只是又一个"AI 帮你写代码"的套壳工具。真正用下来才发现,它的定位和普通的代码补全插件完全不是一回事——它是一个跑在终端里的智能体&#…

2026/10/13 8:18:35 阅读更多 →
纯Java实现Agent编排:RuoYi-Vue-Plus集成AI引擎实战

纯Java实现Agent编排:RuoYi-Vue-Plus集成AI引擎实战

1. 为什么要在 RuoYi-Vue-Plus 里塞进一个 AI 编排引擎 1.1 从"后台管理系统"到"AI 应用底座"的定位转变 RuoYi-Vue-Plus 这套脚手架在国内 Java 圈子里算是老熟人了。多租户、数据权限、代码生成、工作流、分布式锁、缓存管理,基本上一个中后…

2026/10/13 8:18:35 阅读更多 →

最新新闻

跨境电商独立站运营实战:从选品到流量获取

跨境电商独立站运营实战:从选品到流量获取

1. 项目背景与市场机会跨境电商独立站近年来成为许多创业者的首选赛道,相比平台电商模式,独立站具有更高的利润空间和品牌溢价能力。根据行业数据显示,成功运营的独立站平均毛利率可达50-70%,远高于亚马逊等第三方平台的15-25%。这…

2026/10/13 8:59:49 阅读更多 →
分布式探针Agent-Reach:端到端网络可达性监控的架构与实践

分布式探针Agent-Reach:端到端网络可达性监控的架构与实践

1. 项目概述1.1 核心需求解析做运维或者后端开发的朋友一定不陌生,线上系统出问题十次有八次是从"某个服务连不上"开始的。尤其这套架构一旦铺到微服务、多云多机房、边缘节点这种分布式环境里,节点之间互相依赖、调用链路盘根错节&#xff0c…

2026/10/13 8:59:49 阅读更多 →
高可靠性继电器选型指南:从厂家资质到寿命验证的完整要点

高可靠性继电器选型指南:从厂家资质到寿命验证的完整要点

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 8:59:49 阅读更多 →
计算机网络概述:不是入门课,而是数据交付的元逻辑

计算机网络概述:不是入门课,而是数据交付的元逻辑

1. 为什么“计算机网络01——概述”不是入门课,而是整座知识大厦的地基?你有没有试过:学完“OSI七层模型”,能默写出每一层名字和典型协议,但一看到Wireshark里密密麻麻的TCP重传包就发懵;背熟了“子网掩码…

2026/10/13 8:59:49 阅读更多 →
IP地址库离线解析实战:从2019快照到批量归属查询

IP地址库离线解析实战:从2019快照到批量归属查询

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 8:59:49 阅读更多 →
Python Tkinter GUI开发全指南:从窗口搭建到打包发布

Python Tkinter GUI开发全指南:从窗口搭建到打包发布

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 8:58:45 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →