别再只调参了用PyTorch复现DCGAN人脸生成我踩过的5个坑和3个调优技巧当你在Colab上跑完最后一个epoch看着生成器输出的那些扭曲五官是否怀疑自己装了假的PyTorch别急这不过是DCGAN训练路上的常态。真正的问题在于大多数教程只教会你搭建模型骨架却没人告诉你那些让实验起死回生的毛细血管级操作。1. 数据预处理被低估的稳定器CelebA数据集解压后的第一件事不是直接扔进DataLoader。图像尺寸的质数魔咒比想象中更致命——当你的图片尺寸是127x127时转置卷积会悄悄产生1像素的错位。解决方法简单到不可思议# 最佳预处理流水线FFHQ数据集为例 transform transforms.Compose([ transforms.Resize(128), # 强制调整为2的幂次方 transforms.CenterCrop(128), transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)), ])但真正的魔鬼在细节里Normalize的(0.5, 0.5, 0.5)不是随便写的这个参数将像素值映射到[-1,1]区间与生成器最后的tanh激活形成完美闭环批量统计陷阱用ImageFolder加载数据时若子文件夹内图片少于batch_sizeBatchNorm会静默失效2. 损失函数的双面博弈当判别器loss归零而生成器还在震荡时别急着调学习率。DCGAN特有的对抗平衡机制需要重新理解现象本质原因解决方案D_loss→0判别器过强降低D的学习率为G的1/4G_loss周期性飙升梯度冲突改用RAdam优化器两者同步震荡特征空间坍缩在G的输入层添加像素噪声# 更聪明的优化器配置 g_optimizer torch.optim.Adam(generator.parameters(), lr0.0001, betas(0.5, 0.999)) d_optimizer torch.optim.Adam(discriminator.parameters(), lr0.000025, # 刻意保持4倍差距 betas(0.5, 0.999))3. 架构设计的隐形约束论文里的网络结构图其实漏说了三个致命细节转置卷积的padding模式DCGAN原始实现使用padding1的转置卷积但PyTorch默认的padding方式会导致边缘伪影BN层的冻结时机当判别器准确率超过80%时需要冻结生成器的BN层参数LeakyReLU的斜率0.2不是魔法数字对于高分辨率生成需要调整为0.1# 修正后的生成器首层关键修改在output_padding self.init_layer nn.Sequential( nn.ConvTranspose2d(latent_dim, 512, 4, 1, 0, biasFalse), nn.BatchNorm2d(512), nn.ReLU(True) )4. 训练监控的艺术TensorBoard里那些波浪线其实在讲惊悚故事。健康曲线应该长这样判别器loss在0.3-0.7间波动生成器loss呈现锯齿状下降梯度范数保持1e3量级当出现以下情况立即中断训练梯度爆炸权重突然出现NaN值解决方案梯度裁剪模式坍缩生成样本多样性骤降加标签平滑判别器过拟合训练集与验证集FID差值大于15# 实时梯度监控钩子 for name, param in generator.named_parameters(): if param.requires_grad: param.register_hook(lambda grad: torch.clamp(grad, -0.1, 0.1))5. 模型保存的生死时速.pt文件损坏的那天你会明白什么叫绝望。三阶保存策略比想象中重要临时存档每epoch保存一次生成样本和loss中期检查点每50epoch保存完整模型优化器状态最终成品训练完成后额外保存ONNX格式# 智能保存方案 if epoch % 50 0: torch.save({ generator: generator.state_dict(), discriminator: discriminator.state_dict(), optimizerG: g_optimizer.state_dict(), optimizerD: d_optimizer.state_dict(), }, fcheckpoint_{epoch}.pt)三个被验证的调优技巧标签平滑的变种玩法不仅对真实标签做平滑对生成样本也使用0.1-0.3的软标签潜在空间插值正则在batch中混入两个噪声向量的线性插值样本动态学习率衰减当G_loss连续5个epoch不下降时学习率减半# 改进的标签处理 real_labels torch.FloatTensor(batch_size).uniform_(0.9, 1.0) fake_labels torch.FloatTensor(batch_size).uniform_(0.0, 0.1)当你的生成器开始产出可辨认的人脸时试试在潜在空间做球面线性插值——你会发现DCGAN其实偷偷学会了人脸姿态和光照的连续表征。这大概就是对抗训练最迷人的地方模型总比你以为的聪明那么一点点。