
简介这是一份基于 PyTorch 在 CelebA 人脸数据集上训练 DCGAN 的完整实战工程面向深度学习和生成对抗网络初学者、中级开发者重点解决从模型搭建到训练评估的全流程落地问题。资源包含生成器与判别器实现、训练与推理脚本、数据预处理工具并配有预训练模型权重和训练效果图可直接复现实验也能迁移到其他图像生成任务。包体共12个文件以 py 源码为核心辅以 png 训练过程图与生成结果图、GIF 动态效果、MD 说明文档及 pth 模型文件压缩包约102.4MB结构紧凑、上手路径清晰已有399人学习下载。项目按搭建模型、预处理数据、训练调优、结果分析等步骤展开详细展示 DCGAN 结构设计与关键代码尤其适合希望深入理解卷积生成对抗网络原理并快速获得实操经验的学习者通过运行源码与模型可掌握 CelebA 数据集处理、损失函数与优化器配置以及生成图像质量评估的基本方法。1. 这个项目到底在讲什么想真正跑通 GANDCGAN CelebA 是最合适的起点如果你是第一次接触 GAN或者已经读了几篇论文但对「生成器到底怎么把噪声变成人脸」仍然一头雾水那这个标题几乎是绕不开的标准答案GAN网络结构选DCGAN数据集用CelebA框架用Pytorch。总有人一上来就想去复现 StyleGAN、Diffusion结果被显存和调参一堆血泪教训劝退。实际上DCGAN 在 CelebA 上训练出的 64×64 人脸已经能在几小时内让人直观看到「生成对抗」的完整过程——第一轮是噪点第五个 epoch 开始有五官轮廓二十个 epoch 之后能分清性别和表情。这个项目适合三类人想从理论过渡到代码复现的入门者、需要快速完成一个图像生成 demo 用于展示的从业者、以及准备用 GAN 做图像修复或数据增强、想先摸清底线能力的前期调研者。全文会围绕一条完整落地方案展开从 Pytorch 环境搭建、CelebA 下载与预处理到 DCGAN 的网络结构实现、训练循环再到参数设置和翻车现场排查。源码包不需要去网上猜跟着这篇文章把每个模块写出来你自己就是这个项目的作者。2. 生成对抗网络与 DCGAN为什么这个组合能稳定出效果很多人照着别人的代码跑通了但换个人脸数据集就崩成一团模糊。要避免这种情况必须先理解 DCGAN 在 GAN 这个大框架里做了哪些关键改动又为什么偏偏搭配 CelebA。2.1 GAN 的对抗逻辑监督来自判别器而不是标签GAN的博弈可以用一个场景说明白生成器是造假币的判别器是验钞的。造假者不断改进工艺验钞者不断更新防伪手段双方在对抗中同时变强。落到代码层面两个网络共用同一个二分类损失函数 BCE但优化目标完全相反。判别器 D 的目标是最小化 L_D - E[log D(x)] - E[log(1 - D(G(z)))]生成器 G 的目标则是最小化 L_G - E[log D(G(z))]注意看G 的损失里没有真实图像 x 参与。生成器的唯一学习信号来自判别器对它输出图的一个判分。想让 G 变强本质是骗过 D而不是让输出图去对齐某个真实标签。这也是 GAN 训练跟普通监督式图像分类最本质的差别——Pytorch里必须手动交替更新两个优化器不能在一个 backward 里同时完成。实践中有个常见误用有人把生成器的 loss 直接写成bce(fake_out, fake_label)这等于让 G 去学会「把自己判成假的细节」。正确写法是让 G 的目标变成「让 D 把假图判为真」也就是反向使用真实标签。这一点在第四章的训练循环里会再强调。2.2 DCGAN 的四个关键改动稳定训练靠的是卷积与规范化的组合DCGAN 出自 2015 年的论文《Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks》它把 GAN 与 CNN 结合后提出了四个至今仍被广泛借鉴的结构决策每一个都直接影响生成质量去掉池化层生成器用转置卷积做上采样判别器用带步长的卷积做下采样让网络自己学习空间降维与升维的方式。生成器和判别器内部都使用BatchNorm2d防止每层输入分布偏移过大否则网络初始化稍有不合适训练两三轮后就会陷入梯度饱和。生成器内部激活函数用ReLU判别器内部用LeakyReLU斜率通常取 0.2。LeakyReLU在负数区间保留小梯度避免判别器神经元大量死亡。生成器最后一层用Tanh输出让图像像素范围落在 [-1, 1]与预处理时的归一化保持一致。这四个改动单独看都不复杂但组合起来后GAN 训练从「随时会崩的杂技」变成了「有章可循的调参工程」。你会在大量开源项目里看到 DCGAN 的默认参数——Adam 优化器、学习率 0.0002、beta1 设为 0.5——都是围绕这四个结构改动调出来的后面不要轻易乱动。2.3 为什么是 CelebA数据量、人脸对齐与属性标注的平衡点CelebACelebFaces Attributes Dataset包含超过 20 万张名人人脸图像每张都有 40 项属性标注比如性别、眼镜、微笑、发型。图像原始尺寸是 218×178左右不对称。训练 DCGAN 时一般先居中裁剪成 160×160再缩放到 64×64 或 32×32。选这个数据集有几个实际原因。一是规模刚好20 万张图足够让 DCGAN 在几小时内学出清晰人脸也不至于大到下载和处理耗时太长。二是人脸位置基本居中裁剪策略简单不需要额外的人脸检测前置步骤。三是属性标签在后续做条件生成、人脸属性编辑时可以直接复用一个数据集能支撑多个阶段的实验。相比直接用 FFHQ 或其他人脸高清数据集CelebA 的优点是「容错率高」。DCGAN 本身的生成分辨率有限在 64×64 尺寸下CelebA 的人脸五官已经足够清晰训练速度也快。等到将来换 StyleGAN 或 Diffusion 做高清生成再升级更高分辨率的数据集才划算。2.4 为什么用 Pytorch 搭这个组合动态图与 torchvision 的天然适配Pytorch在 GAN 项目里几乎是不需要犹豫的选择。GAN 训练需要两个网络交替前向、反向Pytorch的动态计算图机制允许你在一个循环里随意修改张量形状、中途 detach、多次 backward调试体验远比静态图的框架舒服。另一个直接优势是 torchvision 内置了CelebA数据集的下载与读取接口。你只需要传入root和transform它自动处理目录结构和图片加载省去了手动写 Dataset 类和对齐路径的麻烦。Pytorch生态里的预训练权重、torchvision.models以及tensorboard日志集成也都成熟做实验要比从零搭工程框架省力得多。3. 环境与数据准备从Pytorch环境搭建到可用的 CelebA 数据流这一阶段的目标不是写网络而是建立一条稳定、可重复的数据管线。很多新手在这里踩坑Pytorch版本和 CUDA 不匹配、手动下载的 CelebA 目录不完整、DataLoader 参数没调导致训练时 CPU 占用过高等。下面按顺序解决。3.1 环境搭建Python 版本、Pytorch版本与 CUDA 的对应关系Pytorch的安装应该以官方安装页为准但版本对应关系可以先记在心里。常见组合如下用途PythonPytorch依赖 CUDA 版本学习/调试3.10 / 3.112.3.1 或 2.4.xCUDA 12.1兼容旧环境3.8 / 3.91.13.x 或 2.0.xCUDA 11.6/11.7无 GPU 备选3.92.x 最新版CPU不推荐建议用 conda 建独立环境避免污染系统 Pythonconda create -n dcgan python3.10 conda activate dcgan pip install torch torchvision --index-url https://download.pytorch.org/whl/cu124安装完成后用两行命令验证 GPU 可用性python -c import torch; print(torch版本:, torch.__version__); print(CUDA可用:, torch.cuda.is_available()); print(GPU名称:, torch.cuda.get_device_name(0) if torch.cuda.is_available() else 无)这段命令的torch.cuda.is_available()返回 False 时优先检查驱动和 CUDA 版本不要立刻卸载重装。用nvidia-smi查看驱动支持的 CUDA 版本只要驱动版本大于等于 Pytorch 需要的 CUDA 版本就能用。这里最容易翻车的地方是驱动显示的 CUDA 版本是 12.1但你装了 cu118 的 Pytorch虽然通常也能跑但性能会受影响不如直接用 cu124。3.2 获取 CelebA两种常用路径为了不把时间花在找路径上Pytorch的torchvision.datasets.CelebA可以直接处理整个流程。先定义好数据变换再交给数据集类from torchvision import datasets, transforms transform transforms.Compose([ transforms.CenterCrop(160), transforms.Resize(64), transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) ]) dataset datasets.CelebA( root./data, splitall, transformtransform, downloadTrue )这段代码会自动创建./data/celeba目录下载img_align_celeba图片压缩包和标注文件解压后按结构存好。要注意splitall表示使用全部 20 万张图训练时不必区分 train/val生成任务不像分类任务需要验证集。如果想手动下载必须保持官网的目录结构一致img_align_celeba/放图片Anno/放list_attr_celeba.txt、list_eval_partition.txt。目录结构一旦不对torchvision在读取时就直接抛错误而且错误信息不算友好。血泪经验是第一次手动下载时只下了图片包漏了标注文件结果AttributeError报了半天最后才意识到是数据集不完整。3.3 数据预处理裁剪、缩放与归一化的参数意义CenterCrop(160)把原始 178×218 图像沿着中心裁掉左右不均匀的边缘让脸部居中且保持正方形。Resize(64)统一尺寸因为 DCGAN 的全卷积结构并不强制固定输入大小但BatchNorm会隐式地绑定特征图尺寸训练过程中切换尺寸只会添乱。Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))做的事情是(x - 0.5) / 0.5把像素从 [0, 1] 映射到 [-1, 1]。这个映射必须和生成器输出层的Tanh配对。如果改用ToTensor()默认的 [0, 1] 范围生成器输出也应该换成Sigmoid否则两者的分布不匹配判别器会瞬间找到破绽。3.4 DataLoader 参数batch_size、num_workers 与 pin_memory 的取舍from torch.utils.data import DataLoader dataloader DataLoader( dataset, batch_size128, shuffleTrue, num_workers4, pin_memoryTrue, drop_lastTrue )这里drop_lastTrue容易被忽略但很关键。CelebA 总样本数是 20 万级别不一定能被 128 整除最后一个不完整的 batch 会导致BatchNorm统计量异常对判别器影响尤其明显。num_workers4让数据加载并行但不要盲目调到 16因为 Windows 上 worker 进程开销大反而拖慢训练。pin_memoryTrue在 GPU 训练时把数据暂存锁页内存拷贝到显存的路径更直接单卡训练建议开启。如果你的显存不够 128 的 batch就降到 64DCGAN在这个数据上64 与 128 的生成质量差距不算大但 32 以下时能明显感到判别器不稳定。4. 网络与训练实现把 DCGAN 的关键模块逐一落到 Pytorch 代码网络结构是这套方案的核心也是「项目源码」最值钱的部分。生成器、判别器、超参数、训练循环四块分别讲清楚代码可以直接拷到自己的工程里。4.1 生成器从 100 维噪声到 64×64 人脸的转置卷积结构生成器输入是z_dim100的噪声向量采样自标准正态分布N(0,1)。输出是 3×64×64 的图像张量。结构上用四层ConvTranspose2d把 4×4 特征图逐层放大到 8、16、32、64。import torch.nn as nn class Generator(nn.Module): def __init__(self, z_dim100, ngf64): super().__init__() # 输入 z: (batch, z_dim)先 reshape 成 (batch, z_dim, 1, 1) self.fc nn.Sequential( nn.ConvTranspose2d(z_dim, ngf * 8, 4, 1, 0, biasFalse), nn.BatchNorm2d(ngf * 8), nn.ReLU(inplaceTrue) ) # 4x4 - 8x8 - 16x16 - 32x32 - 64x64 self.upsample nn.Sequential( nn.ConvTranspose2d(ngf * 8, ngf * 4, 4, 2, 1, biasFalse), nn.BatchNorm2d(ngf * 4), nn.ReLU(inplaceTrue), nn.ConvTranspose2d(ngf * 4, ngf * 2, 4, 2, 1, biasFalse), nn.BatchNorm2d(ngf * 2), nn.ReLU(inplaceTrue), nn.ConvTranspose2d(ngf * 2, ngf, 4, 2, 1, biasFalse), nn.BatchNorm2d(ngf), nn.ReLU(inplaceTrue), nn.ConvTranspose2d(ngf, 3, 4, 2, 1, biasFalse), # 3 通道 nn.Tanh() # 输出范围 [-1, 1] ) def forward(self, z): # z: (batch, z_dim) - (batch, z_dim, 1, 1) z z.view(z.size(0), z.size(1), 1, 1) out self.fc(z) out self.upsample(out) return out前向函数里的view操作很关键Linear层也能做同样的事情但用ConvTranspose2d直接把噪声向量视为 1×1 的「特征图」后续转置卷积才能操作。ngf64是生成器基础通道数显存紧张时降到 32但生成图像的清晰度会下降。每个ConvTranspose2d后面都带BatchNorm2d只有最后一层不带这是 DCGAN 论文明确规定的结构。4.2 判别器带步长卷积与 LeakyReLU 的二分类网络判别器的输入是 3×64×64 图像输出是一个标量概率代表真实程度。卷积方向与生成器相反逐步下采样到 4×4最后用单个节点输出二分类结果。class Discriminator(nn.Module): def __init__(self, ndf64): super().__init__() self.downsample nn.Sequential( # 64x64 - 32x32 nn.Conv2d(3, ndf, 4, 2, 1, biasFalse), nn.LeakyReLU(0.2, inplaceTrue), # 32x32 - 16x16 nn.Conv2d(ndf, ndf * 2, 4, 2, 1, biasFalse), nn.BatchNorm2d(ndf * 2), nn.LeakyReLU(0.2, inplaceTrue), # 16x16 - 8x8 nn.Conv2d(ndf * 2, ndf * 4, 4, 2, 1, biasFalse), nn.BatchNorm2d(ndf * 4), nn.LeakyReLU(0.2, inplaceTrue), # 8x8 - 4x4 nn.Conv2d(ndf * 4, ndf * 8, 4, 2, 1, biasFalse), nn.BatchNorm2d(ndf * 8), nn.LeakyReLU(0.2, inplaceTrue), # 4x4 - 1 nn.Conv2d(ndf * 8, 1, 4, 1, 0), nn.Sigmoid() ) def forward(self, img): return self.downsample(img).view(-1, 1)判别器的LeakyReLU(0.2)与生成器的ReLU形成对称差异。生成器希望特征稀疏ReLU 能防止梯度在深层回传时快速衰减判别器则希望负数区域也能有梯度不然假图一旦让某个神经元进入负饱和区该神经元对生成器回传的梯度就断了。注意判别器的第一层也没有BatchNorm这一层直接接原始像素分布稳定性和参赛无关加了反而在早期增加噪声。4.3 超参数表一套可以照抄的 DCGAN 默认参数DCGAN的论文参数经过了多组实验验证。除非你明确知道自己在调什么否则先用下表 baseline参数推荐值说明z_dim100噪声向量维度过低会导致生成多样性差batch_size128显存小则 64小于 32 时 BN 统计不稳定learning_rate0.0002两个网络都用同一个 lrbeta10.5Adam 的第一动量默认 0.9 训 GAN 容易震荡beta20.999保持不变损失函数BCE后续改进可用 LSGAN / WGAN训练轮数20-3064×64 输出20 轮后收益递减优化器Adam两个网络各自一个独立优化器beta10.5是一处容易被忽略的细节。普通分类任务里 Adam 的 beta1 用 0.9 没问题但 GAN 训练中梯度方向变化剧烈动量太大会让优化器「刹不住车」导致生成器在损失面来回震荡。这是 DCGAN 能稳定训练的隐性关键之一。4.4 训练循环先判别器后生成器的固定顺序为何不能反过来下面是训练的核心循环。注意三个原则判别器先更新生成器后更新判别器训练时对生成器的输出使用detach()生成器训练时使用的标签是真实标签。import torch device torch.device(cuda if torch.cuda.is_available() else cpu) G Generator(z_dim100).to(device) D Discriminator().to(device) criterion nn.BCELoss() lr 0.0002 opt_G torch.optim.Adam(G.parameters(), lrlr, betas(0.5, 0.999)) opt_D torch.optim.Adam(D.parameters(), lrlr, betas(0.5, 0.999)) # 为了演示固定 batch_size实际以 DataLoader 中取出的形状为准 batch_size 128 real_label torch.ones(batch_size, 1, devicedevice) fake_label torch.zeros(batch_size, 1, devicedevice) epochs 20 for epoch in range(epochs): for i, (real_img, _) in enumerate(dataloader): real_img real_img.to(device) current_bs real_img.size(0) # ---- 训练判别器 ---- D.zero_grad() real_out D(real_img) d_real_loss criterion(real_out, real_label[:current_bs]) z torch.randn(current_bs, z_dim, devicedevice) fake_img G(z) fake_out D(fake_img.detach()) # 关键detach 阻断生成器梯度 d_fake_loss criterion(fake_out, fake_label[:current_bs]) d_loss (d_real_loss d_fake_loss) / 2 d_loss.backward() opt_D.step() # ---- 训练生成器 ---- G.zero_grad() fake_out D(fake_img) # 这里不 detach梯度要流回 G g_loss criterion(fake_out, real_label[:current_bs]) # 目标让 D 判为真 g_loss.backward() opt_G.step() if i % 100 0: print(f[{epoch 1}/{epochs}][{i}/{len(dataloader)}] fD_loss: {d_loss.item():.4f} G_loss: {g_loss.item():.4f})fake_img.detach()的理由要拆开说清楚。训练判别器时我们希望判别器的梯度只更新 D 的参数不让梯度流回 G。如果不 detach反向传播会同时更新 G 的参数而你此时还没有计算生成器的损失G 的参数会被 D 的损失「顺手」改一遍整个训练节奏就乱了。生成器训练时用real_label而不是fake_label可能读起来反直觉。生成器的任务不是「让假图变假」而是「骗过判别器」所以它的目标是最小化criterion(fake_out, real_label)。也就是让 D 对假图的输出尽量接近 1。这一步也是很多人第一次复现 DCGAN 时间接搞混的地方看起来像是在对抗损失设定时把真假标反了实际训练几轮后生成器会退化成输出模糊的平均脸。训练过程中每 100 个 batch 打印一次 loss。不要只看 loss 绝对值大小要关注两者是否处于「拉锯」状态D_loss 和 G_loss 交替上升下降说明对抗有效D_loss 一路走低到接近 0说明判别器过于强势需要按第五章的方法干预。5. 训练避坑指南DCGAN 最常见的 6 个问题与排查办法GAN训练的不确定性一直是个玄学问题。但 DCGAN 结构合理时大部分翻车现场其实有迹可循。以下按常见的失败现象记录原因和解决顺序。5.1 现象判别器 loss 一路降到 0.02生成器 loss 涨到几十这是最常见也最容易被误读的情况。很多人看到 D_loss 低就以为判别器「很准」实际是对抗失衡的信号。原因通常是判别器容量或学习速度胜过生成器导致D太容易区分真假图G 拿到的梯度几乎全部指向「让图像更像真实样本」而这个梯度信号太强且不稳定生成器学不到有意义的结构。解决思路是削弱 D 或者增强 G。最常见的做法是调低判别器学习率到 0.0001或者给 D 增加 Dropout0.3 左右让它在训练中「看不清」部分特征。另一个有效手段是把 D 的卷积层数减少一层迫使它用更粗糙的特征做判断给 G 留出追赶空间。注意不要同时调两个网络一次只改一边。5.2 现象生成图像始终是同一张脸或者几种固定脸型模式崩溃GAN 训练里被讨论最多的失败模式。现象是生成器发现了一个能被判别器接受的「安全区」于是所有 z 向量在多层转置卷积之后映射到几乎相同的输出。真正糟糕的是这个状态下 G 的损失还在正常下降如果不固定噪声 z 去观察图片很难及时发现。最直接的做法是降低学习率让生成器「走得慢一点」不容易陷入局部最优。更结构化的办法是把损失从 BCE 换成 LSGAN最小二乘损失nn.MSELoss替代nn.BCELoss梯度在距离目标较远时不会饱和能持续推动生成器走出安全区。换损失函数后lr通常不变但要重新观察十几轮。5.3 现象生成图像全是高亮噪声或者整片黑色如果去归一化之后的图像没有五官轮廓只剩细碎纹理或者一块纯色问题往往在生成器的最后一层或预处理环节。先检查G输出层是不是Tanh如果换成了Sigmoid那与预处理的 [-1,1] 范围不匹配再看训练早期第一个 epoch的生成图如果第一次就是纯噪声且后续毫无变化排查权重初始化和BatchNorm是否正常工作。BatchNorm在pytorch里默认使用当前的 batch 统计量如果输入 batch 里恰好包含大量相似图像BN 的统计会偏向这批图的分布导致特征图输出不稳定。一个实用的检查手段是每轮保存一次同一个z的生成图像单独看什么时候开始出现结构如果三个 epoch 后仍然像噪声直接减小lr到 0.0001并确认z采样自torch.randn而不是均匀分布。5.4 现象loss 在某一步突然变成 NaNNaN在 DCGAN 里通常是梯度爆炸而不是模型「坏了」。直接把学习率降到 0.0001 往往能解决同时检查生成器里是否有ConvTranspose2d的biasFalse设置与BatchNorm配合不当。biasFalse是 DCGAN 的结构要求因为 BN 层本身包含可学习的bias参数转置卷积再叠加 bias 会造成参数冗余。另一个会诱发 NaN 的位置是判别器输出经过Sigmoid后概率值极度接近 0 或 1log操作产生无穷梯度。pytorch的BCELoss内部边界处理相对安全但自定义 loss 时要特别小心。最好在训练循环里加一个梯度裁剪torch.nn.utils.clip_grad_norm_(G.parameters(), max_norm5.0) torch.nn.utils.clip_grad_norm_(D.parameters(), max_norm5.0)把每层梯度的 L2 范数限制在 5 以内能显著降低早期训练崩溃的概率。5.5 现象生成图像有明显条带纹理像是「脏玻璃」这类伪影大多来自BatchNorm在小 batch 下的统计噪声。BatchNorm2d在一个 batch 内计算均值与方差如果 batch_size 太小或样本随机性太强BN 的统计量在每步之间波动很大进而让生成器输出的连续空间区域产生「块状条带」。这类问题在 DCGAN 里尤其明显因为它所有层都由转置卷积构成特征图里一个 channel 的细微波动会被直接放大成图像上的条带。调整策略把batch_size提升到 64 以上drop_lastTrue必须开如果 GPU 显存实在不够近期的替代方案是把BatchNorm2d换成InstanceNorm2d它只在单个样本内归一化对 batch 大小不敏感。但注意换掉之后训练可能变慢需要同时降低学习率。5.6 现象训练前几轮正常过了第 10 轮开始质量退化这通常是判别器过拟合了训练集。DCGAN 在 CelebA 上训练到十轮之后D 开始记住训练集中特定脸的出现频率分布G 为了骗过 D反而把生成图像拉向训练集中的「平均脸」。观察到的现象是单张脸的质量越来越高但不同 z 之间的差异越来越小。解决办法不要让一个模型从头训到 30 轮而是每隔 5 轮用固定 z 存一次图比较不同轮次的生成效果在多样性开始下降时的那个 checkpoint 就是最优训练终点。也可以考虑给D增加数据增广比如随机的水平翻转或小幅裁剪但这要求重新调整预处理的尺寸。6. 从 demo 到能交付的小技巧验证训练效果的三种方式与两项低成本改进生成模型不像分类任务没有明确指标可以告诉你「训好了没有」。以下三个习惯能让判断更有依据也是我从这个项目里沉淀下来最值钱的技巧。6.1 固定噪声向量检查训练进展训练循环里准备一份固定z_fixed每 2 个 epoch 生成一张网格图保存。由于输入噪声固定生成的图像变化完全来自网络参数的更新你能直观看到「同一组人脸」如何从噪点逐渐变成清晰五官。只看随机图像容易产生错觉——不同 batch 的噪声不同生成质量的变化会被噪声差异掩盖。# 每两个 epoch 执行一次 z_fixed torch.randn(16, z_dim, devicedevice) with torch.no_grad(): sample G(z_fixed).cpu() # 把 [-1,1] 还原为 [0,1] 再保存 sample (sample 1) / 2 # 用 torchvision.utils.save_image 保存 4x4 网格 save_image(sample, foutput/epoch_{epoch}.jpg, nrow4)这段有两条验证信息一是 16 张图中五官多样性如何二是与上一轮比较时每张脸是否朝更清晰的方向变化。如果发现某张脸连续几轮几乎没变化基本可以判定模式崩溃的前兆已经出现。6.2 低成本改进一标签平滑Pytorch的BCELoss支持平滑标签。一个常用的做法是把真实标签从 1.0 改为 0.9假标签从 0.0 改为 0.1。判别器不再被推向「完全置信」的状态梯度不会在判别能力过强时快速饱和训练稳定性会明显提升。代价是训练时间略长但只要不是做到 100 轮以上影响不大。对GAN训练而言稳定性比收敛速度更重要。6.3 低成本改进二从 BCE 换成 LSGAN 损失nn.MSELoss替代nn.BCELoss是一个几乎零成本的改动但效果立竿见影。LSGANLeast Squares GAN将生成器和判别器的优化目标从交叉熵换成最小二乘当判别器过于自信时损失并不会趋近于 0梯度保持在一个稳定的量级。在 CelebA 数据上这个改动对不同人脸表情的多样性提升也很明显尤其当你发现生成结果中女性面孔远多于男性面孔的时候先用 LSGAN 缓解多样性不足再考虑增加性别标签做条件生成。这个项目做完最大的收获不在代码本身而在一种「用固定条件的输出判断训练健康度」的习惯。生成模型不像分类模型有标准答案只能靠旁观者不断调整观察角度。从 DCGAN 开始养成把每个阶段的结果落地成可对比文件的习惯后面再做gan图像修复、人脸编辑甚至换风格都能少走很多弯路。希望这些调参和排错经验帮到你。本文还有配套的精品资源点击获取