ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Pytorch实战DCGAN:CelebA人脸生成从原理到代码

Pytorch实战DCGAN:CelebA人脸生成从原理到代码 简介基于Pytorch在CelebA数据集上训练DCGAN生成对抗网络的项目源码包面向深度学习初学者与希望提升生成模型实战能力的中级开发者适合毕业设计、课程设计或技术复现。资源覆盖从搭建生成器与判别器结构、CelebA数据集预处理、损失函数与优化器配置、训练轮数与性能指标监控到加载预训练模型生成人脸图像并分析评估的完整链路。包内共12个文件包含4个Python脚本、5张训练过程与生成效果图、1个动态生成演示动画、1份说明文档及1个模型权重文件压缩包整体102.4MB。通过项目源码可直观理解对抗训练原理与DCGAN改进细节并能在本地复现名人脸图像生成效果现有代码结构与说明文档也便于读者二次开发与调试。已有399人学习适合需要从理论走向实践并希望在其他图像生成任务中迁移DCGAN方法的开发者。1. GAN与DCGAN为什么是PytorchCelebA这套组合最稳第一次把DCGAN在CelebA上跑通看到64×64的模糊人脸从纯噪声里轮廓浮现时你会立刻意识到GAN类生成模型和之前做的分类网络完全是两种生物——分类看准确率GAN看的是生成样本的质感和多样性。这个标题里的组合GAN、Pytorch、CelebA、DCGAN是入门生成对抗网络最保险的一条路线CelebA数据量大、人脸结构规律生成结果视觉反馈直观DCGAN结构清晰、对新手友好五层卷积加转置的套路不容易写错Pytorch的自动求导和动态图机制又能让你把全部精力放在生成器和判别器的对抗逻辑上而不是被计算图定义绑住手脚。适合学过基础卷积网络、想上手GAN却还没跑通一个完整项目的读者。这篇笔记按真实落地顺序展开环境、数据、模型、训练、排错、进阶照着走一遍就能得到自己训出来的人脸。2. Pytorch环境搭建与CelebA数据集准备2.1 用Anaconda配置Pytorch环境GPU版本从零到跑先把环境问题解决掉。很多时候模型写得没错翻车全是环境引起的——CUDA版本对不上、Pytorch装成了CPU版、conda源超时这三个问题能卡掉一半新手。我自己的习惯是用Anaconda创建独立环境避免把base环境搞乱。下面是完整命令conda create -n dcgan python3.10 -y conda activate dcgan # 安装GPU版PyTorch先用下面命令确认本机CUDA版本 nvidia-smi conda install pytorch torchvision torchaudio pytorch-cuda12.1 -c pytorch -c nvidia python -c import torch; print(torch.__version__, torch.cuda.is_available())最后一行如果输出类似2.x.xcu121 True说明GPU版Pytorch环境已经通了。如果你的显卡较老或者驱动只支持CUDA 11.8把上面命令里的pytorch-cuda12.1换成pytorch-cuda11.8即可驱动向下兼容但算力低于3.0的卡就别折腾了老老实实用CPU版。注意“安装pytorch是不是必须装有gpu”这个问题常被问——不是必须。CPU也能训DCGAN但64×64的CelebA在CPU上跑一个epoch要十几分钟甚至更久调参体验很差。如果你只有CPU建议先把数据集裁剪到5000张图验证流程跑通再考虑借GPU或上云。关于安装源国内用户如果直接执行上面conda命令容易超时可以把conda源换成清华或阿里镜像再用-c pytorch指定官方渠道装pytorch。不管用什么源装完一定要执行最后那条检查命令别急着往下走——这一步多花两分钟后面能省两小时。2.2 CelebA数据集的下载与本地目录结构设计CelebA是香港中文大学整理的约20万张名人脸部图片每张图带40个属性标签和一些关键点标注。做DCGAN入门一般只用图片属性标签用不上所以下载img_align_celeba.zip就够了——压缩包大约1.5GB。下载入口在官方站点有时也需要从镜像站拿具体地址以你能访问到的为准。下载完成后把压缩包解压到项目目录下。我一般这样组织目录mkdir -p dcgan_celeba/data cd dcgan_celeba # 假设zip已经下载到本目录 unzip img_align_celeba.zip -d data/ # 解压后图片都在 data/img_align_celeba/ 下 ls data/img_align_celeba | head -5 ls data/img_align_celeba | wc -l解压出来的文件名形如000001.jpg原图是178×218的人脸照片并不是正方形。后面数据加载时要统一缩放裁剪到64×64。注意一个细节解压后先确认图片完整别刚解压就删压缩包——我吃过一次亏解压到一半磁盘满了没检查就删了zip结果只能重新下。如果磁盘空间紧张可以只用其中一部分图片比如在构造Dataset时只取前5万张或者在生成文件列表时用head -n 50000截断。训练效果会略差但流程完整性不受影响这个取舍在资源有限时很划算。2.3 数据加载器的实现从图片路径到Pytorch Dataset接下来写数据加载。CelebA解压后是一个扁平目录没有train/val子目录所以torchvision.datasets.ImageFolder用不上它要求按类别分子目录。直接用Dataset类自己写更清爽一是不用把图片复制一遍二是后续想加属性标签、做人脸修复项目都有现成入口。import os import torch from torch.utils.data import Dataset from PIL import Image from torchvision import transforms class CelebADataset(Dataset): def __init__(self, img_dir, transformNone): self.img_dir img_dir self.img_paths [os.path.join(img_dir, f) for f in os.listdir(img_dir) if f.endswith(.jpg)] self.transform transform def __len__(self): return len(self.img_paths) def __getitem__(self, idx): img Image.open(self.img_paths[idx]).convert(RGB) if self.transform is not None: img self.transform(img) return img transform transforms.Compose([ transforms.Resize(64), transforms.CenterCrop(64), transforms.ToTensor(), transforms.Normalize(mean[0.5, 0.5, 0.5], std[0.5, 0.5, 0.5]), ]) dataset CelebADataset(img_dirdata/img_align_celeba, transformtransform) dataloader torch.utils.data.DataLoader( dataset, batch_size128, shuffleTrue, num_workers4, pin_memoryTrue, drop_lastTrue)这里最关键的参数是Normalize(0.5, 0.5, 0.5)ToTensor先把像素从0到255缩放到[0,1]然后均值0.5、标准差0.5的归一化会把数值映射到[-1,1]。这个必须和生成器输出层的tanh对应因为tanh的输出范围也是[-1,1]两者对不上判别器学到的特征就乱了。drop_lastTrue也值得说明数据集有202599张图除以128会剩一个小尾巴不丢弃会导致最后一个batch大小和前面不一致在判别器里可能引发BatchNorm统计量跳动。训练阶段丢弃最后不完整batch是DCGAN的标准做法。pin_memoryTrue是配合GPU训练的小优化数据在内存里锁页拷贝到显存的速度会快一些。3. DCGAN模型架构生成器与判别器的Pytorch实现3.1 DCGAN的五个关键设计原则为什么Conv取代了全连接DCGAN这份工作真正有价值的地方是把GAN从“能跑”变成了“能稳定训练”。它给生成器和判别器定了五条设计原则缺一条训练就开始玄学波动。这里直接把最核心的几条过一遍后面实现时随时对照。第一用带步长的卷积代替池化层判别器用stride2的卷积下采样生成器用stride2的转置卷积上采样让网络自己学下采样/上采样的映射而不是用固定的max pooling。第二去掉全连接层全连接参数多且容易过拟合把噪声向量reshape后直接用卷积处理。第三除了生成器输出层和判别器输入层其他卷积层后都接BatchNorm这能显著缓解训练初期的震荡。第四生成器除输出层用tanh外其余层用ReLU判别器全部用LeakyReLUslope取0.2。第五生成器的输出范围与输入图像归一化范围一致都要落在[-1,1]。这套gan网络结构图看起来很简洁噪声z先经过一层线性变换扩维reshape成4×4×1024再经过四层转置卷积逐步升到64×64×3。判别器反过来从3×64×64降到1×1×1的logit输出。整体参数量不大一张单卡就能训。为什么CPU甚至低显存机器也能跑核心原因就是这里的通道数设计比较克制。生成器最宽的层是1024通道的4×4特征图这个尺寸在显存里占不了太多。如果你显存有限把通道数整体砍半1024→512512→256以此类推也能训只是生成质量会糙一些。3.2 生成器实现100维噪声到64×64人脸的转置卷积生成器输入是100维的标准正态分布噪声z输出是3×64×64的人脸图。下面代码是我实际训练用的版本和经典DCGAN结构基本一致import torch import torch.nn as nn class Generator(nn.Module): def __init__(self, latent_dim100, ngf64): super().__init__() self.latent_dim latent_dim self.fc nn.Linear(latent_dim, ngf * 8 * 4 * 4) self.conv nn.Sequential( nn.BatchNorm2d(ngf * 8), nn.ReLU(True), nn.ConvTranspose2d(ngf * 8, ngf * 4, 4, 2, 1, biasFalse), nn.BatchNorm2d(ngf * 4), nn.ReLU(True), nn.ConvTranspose2d(ngf * 4, ngf * 2, 4, 2, 1, biasFalse), nn.BatchNorm2d(ngf * 2), nn.ReLU(True), nn.ConvTranspose2d(ngf * 2, ngf, 4, 2, 1, biasFalse), nn.BatchNorm2d(ngf), nn.ReLU(True), nn.ConvTranspose2d(ngf, 3, 4, 2, 1, biasFalse), nn.Tanh(), ) def forward(self, z): x self.fc(z) x x.view(-1, 64 * 8, 4, 4) return self.conv(x)逻辑说明第一层线性变换把100维向量映射到2048维64×8×4×4再reshape成4×4×2048的特征图随后四层转置卷积把空间尺寸从4→8→16→32→64通道数从512→256→128→64→3。每一轮上采样都让特征图面积翻倍通道数减半最后输出三通道的RGB图用tanh把数值压回[-1,1]。参数说明ngf64是生成器的基础通道数值越大生成能力越强显存占用和训练时间也越高。biasFalse在卷积层里是必要的因为后面紧跟BatchNormBN自带可学习的平移项卷积层再学一个bias就是冗余参数还容易干扰BN的归一化统计。latent_dim100是DCGAN的标准设置不要随意改小太小会让隐空间容量不足生成的人脸容易出现几张面孔反复出现的现象。3.3 判别器实现64×64人脸到真伪概率的卷积网络判别器是一个和二分类CNN完全一致的网络输入3×64×64的图片输出一个标量logit再经过sigmoid得到“这张图是真的”的概率。它的任务就是尽力区分输入来自真实CelebA还是生成器输出class Discriminator(nn.Module): def __init__(self, ndf64): super().__init__() self.conv nn.Sequential( nn.Conv2d(3, ndf, 4, 2, 1, biasFalse), nn.LeakyReLU(0.2, inplaceTrue), nn.Conv2d(ndf, ndf * 2, 4, 2, 1, biasFalse), nn.BatchNorm2d(ndf * 2), nn.LeakyReLU(0.2, inplaceTrue), nn.Conv2d(ndf * 2, ndf * 4, 4, 2, 1, biasFalse), nn.BatchNorm2d(ndf * 4), nn.LeakyReLU(0.2, inplaceTrue), nn.Conv2d(ndf * 4, ndf * 8, 4, 2, 1, biasFalse), nn.BatchNorm2d(ndf * 8), nn.LeakyReLU(0.2, inplaceTrue), nn.Conv2d(ndf * 8, 1, 4, 1, 0, biasFalse), ) def forward(self, x): return self.conv(x).view(-1)注意这里和生成器的两个不同点。第一判别器的第一层卷积不带BatchNorm——因为输入是真实图像或生成图像像素分布差异大BN的均值方差估计会被批次组成带偏后三层再接BN。第二最后一层用4×4卷积核、步长1、无padding把4×4特征图直接压成1×1输出没有经过sigmoid因为我们要在训练时配合BCEWithLogitsLoss使用这个损失函数内部自带sigmoid数值上比“sigmoid后接BCELoss”稳定得多。3.4 初始化与训练前的网络检查模型结构写完先别急着训练。DCGAN原论文特别指出所有权重用均值0、标准差0.02的正态分布初始化。Pytorch默认初始化对GAN这种对抗训练不够友好不手动初始化会出现训练初期loss剧烈抖动。加一个初始化函数def weights_init(m): if isinstance(m, (nn.Conv2d, nn.ConvTranspose2d)): nn.init.normal_(m.weight.data, 0.0, 0.02) elif isinstance(m, nn.BatchNorm2d): nn.init.normal_(m.weight.data, 1.0, 0.02) nn.init.constant_(m.bias.data, 0) netG Generator() netD Discriminator() netG.apply(weights_init) netD.apply(weights_init) # 检查输出尺寸 z torch.randn(2, 100) print(G out:, netG(z).shape) # torch.Size([2, 3, 64, 64]) print(D out:, netD(netG(z)).shape) # torch.Size([2])这段检查代码建议每个改过结构的人都跑一下。转置卷积的输出尺寸计算很容易出错kernel size、stride、padding三个参数任何一个写错输出就不是64×64如果输出尺寸和真实图片不一致数据加载器那边根本拼不到一个batch里训练。在训练脚本里加上这个shape断言能拦住一大半低级错误。4. 训练流程与GAN的损失函数让生成器和判别器打到平衡4.1 GAN的损失函数二元交叉熵与logits的关系先说清楚训练目标。GAN的训练是一个极小极大博弈判别器D想最大化真实图的判断概率、最小化生成图的判断概率生成器G想最小化判别器对生成图的判断概率。数学形式是经典的min-max目标但实际落地时几乎所有成熟代码都不直接用这个原始目标来训练G而是把G的loss做翻转改成最大化“生成图被D判为真”的概率这叫非饱和损失梯度更好传生成质量也更高。在Pytorch里实现这两个环节核心就是nn.BCEWithLogitsLoss。这个loss把sigmoid和二元交叉熵合在了一起输入logits而不是概率内部做了数值上更稳的优化。给真实图打标签1、生成图打标签0是D的监督信号G的监督信号是“让生成图被D判为1”所以G的标签永远是1。实际操作上有两个细节容易搞混。第一个是soft labels不直接用0和1而是真实图标签用0.9、生成图标签用0.1甚至把标签加少量随机噪声这能防止D对真实图过于自信从而提供更平滑的梯度。第二个是G的梯度不回流到DD更新时用fake.detach()切断生成图到生成器的梯度否则反向传播会顺藤摸瓜把D的梯度一路传到G的参数上一次更新就同时改了两个网络训练必然乱掉。4.2 训练循环的完整代码交替更新判别器和生成器下面是一段可以直接放进训练脚本的循环代码单卡训练每个step做了三件事先训D真实图batch和生成图batch再训G然后记录loss。顺序固定不要调换。import torch import torch.nn as nn from torchvision.utils import save_image device torch.device(cuda if torch.cuda.is_available() else cpu) netG Generator().to(device) netD Discriminator().to(device) netG.apply(weights_init) netD.apply(weights_init) criterion nn.BCEWithLogitsLoss() optimG torch.optim.Adam(netG.parameters(), lr0.0002, betas(0.5, 0.999)) optimD torch.optim.Adam(netD.parameters(), lr0.0002, betas(0.5, 0.999)) real_label 0.9 # soft label fake_label 0.1 fixed_noise torch.randn(64, 100, devicedevice) for epoch in range(20): for i, imgs in enumerate(dataloader): imgs imgs.to(device) cur_batch imgs.size(0) # ---- 训练判别器 ---- optimD.zero_grad() real_target torch.full((cur_batch,), real_label, devicedevice) out_real netD(imgs) lossD_real criterion(out_real, real_target) z torch.randn(cur_batch, 100, devicedevice) fake_imgs netG(z) fake_target torch.full((cur_batch,), fake_label, devicedevice) out_fake netD(fake_imgs.detach()) lossD_fake criterion(out_fake, fake_target) lossD lossD_real lossD_fake lossD.backward() optimD.step() # ---- 训练生成器 ---- optimG.zero_grad() out_fake netD(fake_imgs) # 不detach梯度传到G lossG criterion(out_fake, real_target) # 目标是让D认为假图是真的 lossG.backward() optimG.step() if i % 100 0: print(fEpoch {epoch} iter {i} LossD {lossD.item():.4f} LossG {lossG.item():.4f}) # 每个epoch用固定噪声生成一组图片观察训练进度 netG.eval() with torch.no_grad(): sample netG(fixed_noise) save_image(sample, foutput/epoch_{epoch:02d}.png, nrow8, normalizeTrue, range(-1, 1)) netG.train()逻辑说明判别器在这个step里看到两批数据——真实图片batch和生成器刚产出的假图片batch分别计算loss再相加这样一个step里D学到的是“真实图判真、假图判假”的组合信号。生成器更新时假图片的张量来自上一步netG(z)没有detach所以反向传播能跨过判别器把梯度送给生成器更新参数。每轮epoch结束时把固定噪声生成的图片保存下来这些图连起来看就是生成质量逐渐变好的过程。参数说明这里所有超参数都用DCGAN原版的推荐值——lr0.0002、betas(0.5, 0.999)。betas里的0.5是关键Adam默认的0.9会累积一阶动量让训练震荡0.5能让生成器在对抗过程中更快响应判别器的变化。保存图片时的normalizeTrue, range(-1, 1)会把[-1,1]的生成值映射回[0,1]再存成png不然你保存的图片会是一张几乎全黑的图。这也是新手最容易忽略的细节之一。4.3 训练参数的选择学习率、beta1、batch size除了上面代码里的固定值训练时你需要根据loss表现动态调整的还有batch size。batch size不是越大越好。在GAN里batch size影响的是判别器梯度的稳定性和生成器的模态覆盖能力。经验上DCGAN在CelebA上用128比较主流显存紧张时降到64也能训练但如果降到32以下每批数据里正负样本太少判别器loss会非常不稳定生成图像的多样性也会下降。如果显卡显存只有4GB左右可以把ngf和ndf从64降到32batch size调到64训练显存占用大约2GB出头。图像分辨率这块不建议从64改到128分辨率翻倍意味着生成器每层特征图面积翻四倍显存占用和训练时间都会暴涨入门阶段先把64×64的生成质量做扎实。另外训练轮数epoch不需要太多。CelebA上15到20个epoch生成的人脸就已经“像个脸”了。判断模型是否练到位看的不是loss降到了多少而是每epoch保存下来的图片里人脸是否清晰、是否有多样性。loss只是辅助指标。4.4 训练过程中的监控指标与可视化GAN的loss曲线不能像分类任务那样解读。D的loss常年低位不一定是好事——如果D loss长期在0到0.1之间说明判别器太强了生成器已经无法继续从它那里学到有效梯度典型表现就是生成图片几乎不变。我自己的习惯是打印两个比值lossD_real和lossD_fake的差值。正常情况两个值都在0.5上下波动因为soft label把目标变成了0.9/0.1所以稳定值会略高于0.5。如果lossD_real快速掉到接近0而lossD_fake仍然很高说明D对真实图片过拟合这时候需要给真实图片的标签加更多噪声。另一个实用技巧是用epoch训练中途保存的图片网格做判断不要只看数字。图片从模糊色块慢慢变成有五官的人脸就是训练正常的信号如果图片一直是雪花噪点或同一张脸反复出现再去看loss找原因。5. 避坑指南GAN训练里的常见问题与排查5.1 损失函数不下降判别器太强或生成器梯度消失现象训练十几个epochD的loss掉到0.05以下G的loss停在1到2之间不再变化生成的图片要么是模糊色块要么是不断重复的同一张脸。原因判别器被喂了太多“轻松的”正确样本真实图片和假图片差异太明显D很快就分得清两者反向传播给G的梯度接近0G完全学不到东西。这在训练初期特别常见尤其是数据集准备阶段归一化范围没匹配上的时候。解决第一步检查Normalize的均值和标准差是否正确、生成器输出是否用tanh。如果这两处没问题给真实标签加更大的噪声真实标签从0.9改成torch.rand(cur_batch, devicedevice) * 0.2 0.8也就是在0.8到1.0之间随机。同时把判别器学习率降到生成器的四分之一比如G保持0.0002D改成0.00005。还有一个保底方案是每轮让G多训两步、D少训一步在代码里改成先训D一次再训G两次。5.2 模式坍塌生成的人脸千篇一律现象每个epoch保存的9×9网格图里大多数图片看起来像同一个人只是角度或表情略有差别多样性远小于训练数据。原因生成器找到了一个“万能解”——某张能让D给出高分的伪造脸于是不再探索隐空间中的其他区域。这是GAN最经典的翻车现场叫模式坍塌在CelebA这种单一类别数据集上尤其容易出现因为全部人脸都长得很像G偷懒的成本很低。解决套路很多最常用的有三个。第一给D的输入图片做随机增强比如随机水平翻转、随机裁剪每epoch的增强都会让D的决策边界变模糊G不敢死磕单一模式。第二把batch里真实图片的hardness提上去——做法是在D的训练里用上一次迭代的G生成的图而不是当前迭代的增加D分类难度。第三如果已经坍塌得很严重干脆把D的卷积层后BN去掉一层再从头训降低D的拟合能力把平衡向G那边推。5.3 Loss变成NaN数值不稳定或学习率过高现象训练没到一半loss打印出来是nan生成的图片也全是噪点或黑色。原因最常见的原因是学习率太高或者batch太小导致某一层权重更新幅度过大。另一个容易忽略的原因是转置卷积层在4×4小尺寸特征图上的梯度爆炸——小尺寸特征图参数量大反向传播梯度经过多次累积后数值溢出。还遇到过一种情况是数据集里有损坏的图片文件Image.open读到坏图返回空数据喂进网络后直接污染梯度。解决先用torch.utils.data.DataLoader的drop_lastTrue排除不完整batch的问题再逐层排查数据。把训练循环对GPU稳定性的监测打开每隔固定步数检查参数里是否有NaN一旦出现就能定位是哪个网络先爆的做法是torch.isnan(netG.conv[0].weight).any()。定位到网络层后把学习率降低一个数量级比如从0.0002降到0.00002或者给G的转置卷积加weight_norm通常能救回来。5.4 显存不足与训练过慢OOM和GPU利用率上不去现象训练脚本一跑就报CUDA out of memory或者每步训练耗时极长GPU利用率不到50%。原因最常见的直接原因是batch size设置过大或者num_workers太高导致内存先爆。更隐蔽的原因是pin_memoryTrue用在了内存本来就紧张的机器上锁页内存会额外占用大量RAM。如果用的是Windowsnum_workers超过4还会触发数据加载线程的spawn限制反而更慢。解决显存不足先看当前占用——nvidia-smi确认是否被其他进程占了显存。排除后把batch size从128降到64或32并把ngf、ndf从64降到32合起来能省一大半显存。训练慢的另一个提升方向是用混合精度Pytorch的torch.cuda.amp.autocast在显存和速度上都有收益GAN的D和G在autocast环境下训练是稳定的。from torch.cuda.amp import autocast, GradScaler scaler GradScaler() with autocast(): out_real netD(imgs) lossD_real criterion(out_real, real_target) scaler.scale(lossD_real).backward() scaler.step(optimD) scaler.update()这套写法只比原来的训练循环多几行代码但对显存占用和训练速度的改善特别明显。在模型结构、损失函数都不动的前提下训练速度能快30%以上。不过这个优化要在代码能稳定跑通之后再加新手期先别把混合精度和训练问题混在一起排查否则两个痛点叠加会让定位问题的难度翻倍。6. 训练完的模型怎么用隐空间插值与生成质量验证训练收尾后第一件事不是急着调参而是先玩一下隐空间插值这是验证生成器到底学没学到“人脸流形”的最直观方法。在z空间里取两个随机向量中间线性插值N步把每一步decode成图片连起来看能看清人脸从一个身份平滑过渡到另一个身份。这说明隐空间是连续且语义有结构的import torch from torchvision.utils import save_image def interpolate(netG, z1, z2, steps10, pathinterp.png): alphas torch.linspace(0, 1, steps).view(-1, 1) z (1 - alphas) * z1 alphas * z2 # 逐步混合两个噪声向量 with torch.no_grad(): imgs netG(z) save_image(imgs, path, nrowsteps, normalizeTrue, range(-1, 1)) z1 torch.randn(1, 100) z2 torch.randn(1, 100) interpolate(netG, z1, z2, steps10)如果插值图里人脸在中间出现了模糊、扭曲甚至五官错乱说明生成器还在“背”训练样本流形拟合得不够平滑回去再训几个epoch通常能改善。如果插值图干净平滑就可以往更有意思的方向延伸了——比如“gan图像修复”这个方向把你的人脸图中间区域挖掉在隐空间里搜索一个z使生成图与遮挡区域外的像素尽可能一致把补全的像素贴回去。思路本质上是一个优化问题在固定生成器的前提下用梯度下降更新z。另一个值得一试的方向是feature matching把判别器中间层的特征作为匹配目标训练G时不仅要求输出骗过D还要求生成图的中间特征与真实图的中间特征均值接近这个技巧在CelebA上能明显改善多样性。跑完这些验证再回头看你会发现DCGAN真正教会你的不是那一层层的转置卷积而是“两个网络互相博弈、谁也不能太强”的平衡感。我自己的习惯是每次改完结构都在固定噪声上重新保存一组图片用这组图对比版本的优劣——肉眼评判比loss曲线可靠得多。这些经验都是靠一次次改参数换来的希望你这次能少走一点弯路希望帮到你。本文还有配套的精品资源点击获取
返回列表