DCGAN 实战:PyTorch 1.13 实现 64x64 人脸生成,FID 分数低于 30 DCGAN 实战PyTorch 1.13 实现 64x64 人脸生成FID 分数低于 30人脸生成一直是计算机视觉领域最具挑战性的任务之一。传统方法往往难以捕捉人脸细节的复杂分布而深度卷积生成对抗网络DCGAN通过对抗训练机制能够生成高度逼真的人脸图像。本文将带你从零实现一个完整的DCGAN项目使用PyTorch 1.13生成64x64分辨率的人脸图像并确保FID分数低于30。1. 环境准备与数据加载1.1 安装依赖首先确保你的环境已安装PyTorch 1.13及以上版本。推荐使用Anaconda创建虚拟环境conda create -n dcgan python3.8 conda activate dcgan conda install pytorch1.13.1 torchvision0.14.1 -c pytorch pip install numpy pandas matplotlib scikit-learn1.2 数据集选择与预处理我们使用CelebA数据集包含超过20万张名人面部图像。下载后解压到data/celeba目录然后进行预处理import torchvision.transforms as transforms from torchvision.datasets import ImageFolder transform transforms.Compose([ transforms.Resize(64), transforms.CenterCrop(64), transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) ]) dataset ImageFolder(rootdata/celeba, transformtransform) dataloader torch.utils.data.DataLoader(dataset, batch_size128, shuffleTrue, num_workers4)关键预处理步骤统一缩放到64x64分辨率中心裁剪保证人脸居中像素值归一化到[-1, 1]范围使用批量大小为128加速训练2. DCGAN模型架构设计DCGAN的核心创新在于将CNN引入GAN框架。以下是生成器(G)和判别器(D)的详细实现2.1 生成器网络生成器接收100维随机噪声通过转置卷积逐步上采样import torch.nn as nn class Generator(nn.Module): def __init__(self, ngpu1): super(Generator, self).__init__() self.ngpu ngpu self.main nn.Sequential( # 输入是Z, 进入全连接 nn.ConvTranspose2d(100, 512, 4, 1, 0, biasFalse), nn.BatchNorm2d(512), nn.ReLU(True), # 状态大小 (512,4,4) nn.ConvTranspose2d(512, 256, 4, 2, 1, biasFalse), nn.BatchNorm2d(256), nn.ReLU(True), # 状态大小 (256,8,8) nn.ConvTranspose2d(256, 128, 4, 2, 1, biasFalse), nn.BatchNorm2d(128), nn.ReLU(True), # 状态大小 (128,16,16) nn.ConvTranspose2d(128, 64, 4, 2, 1, biasFalse), nn.BatchNorm2d(64), nn.ReLU(True), # 状态大小 (64,32,32) nn.ConvTranspose2d(64, 3, 4, 2, 1, biasFalse), nn.Tanh() # 输出状态大小 (3,64,64) ) def forward(self, input): return self.main(input)关键设计要点使用转置卷积实现上采样每层后接BatchNorm稳定训练输出层使用Tanh将像素值约束到[-1,1]避免在全连接层使用池化操作2.2 判别器网络判别器是标准的CNN分类器但使用LeakyReLU激活class Discriminator(nn.Module): def __init__(self, ngpu1): super(Discriminator, self).__init__() self.ngpu ngpu self.main nn.Sequential( # 输入 (3,64,64) nn.Conv2d(3, 64, 4, 2, 1, biasFalse), nn.LeakyReLU(0.2, inplaceTrue), # 状态大小 (64,32,32) nn.Conv2d(64, 128, 4, 2, 1, biasFalse), nn.BatchNorm2d(128), nn.LeakyReLU(0.2, inplaceTrue), # 状态大小 (128,16,16) nn.Conv2d(128, 256, 4, 2, 1, biasFalse), nn.BatchNorm2d(256), nn.LeakyReLU(0.2, inplaceTrue), # 状态大小 (256,8,8) nn.Conv2d(256, 512, 4, 2, 1, biasFalse), nn.BatchNorm2d(512), nn.LeakyReLU(0.2, inplaceTrue), # 状态大小 (512,4,4) nn.Conv2d(512, 1, 4, 1, 0, biasFalse), nn.Sigmoid() ) def forward(self, input): return self.main(input).view(-1)关键设计要点使用带步长的卷积代替池化LeakyReLU斜率设为0.2防止梯度消失最后一层Sigmoid输出概率值去除全连接层直接使用卷积输出3. 训练策略与技巧3.1 损失函数与优化器使用二元交叉熵损失和Adam优化器# 初始化网络 netG Generator().to(device) netD Discriminator().to(device) # 定义优化器 optimizerD optim.Adam(netD.parameters(), lr0.0002, betas(0.5, 0.999)) optimizerG optim.Adam(netG.parameters(), lr0.0002, betas(0.5, 0.999)) # 损失函数 criterion nn.BCELoss() # 固定噪声用于可视化 fixed_noise torch.randn(64, 100, 1, 1, devicedevice)3.2 训练循环实现采用交替训练策略先更新判别器再更新生成器for epoch in range(num_epochs): for i, (real_images, _) in enumerate(dataloader): # 训练判别器 netD.zero_grad() real_images real_images.to(device) batch_size real_images.size(0) label torch.full((batch_size,), real_label, devicedevice) output netD(real_images) errD_real criterion(output, label) errD_real.backward() noise torch.randn(batch_size, 100, 1, 1, devicedevice) fake netG(noise) label.fill_(fake_label) output netD(fake.detach()) errD_fake criterion(output, label) errD_fake.backward() errD errD_real errD_fake optimizerD.step() # 训练生成器 netG.zero_grad() label.fill_(real_label) output netD(fake) errG criterion(output, label) errG.backward() optimizerG.step()关键训练技巧判别器训练时冻结生成器参数生成器训练时冻结判别器参数使用不同的标签值real_label1, fake_label0每100批次可视化生成结果3.3 FID分数监控FID(Frechet Inception Distance)是评估生成质量的重要指标from torchvision.models import inception_v3 from scipy.linalg import sqrtm def calculate_fid(real_images, fake_images): # 加载预训练Inception-v3模型 inception_model inception_v3(pretrainedTrue, transform_inputFalse).to(device) inception_model.eval() # 提取特征 with torch.no_grad(): real_features inception_model(real_images)[0].view(real_images.size(0), -1) fake_features inception_model(fake_images)[0].view(fake_images.size(0), -1) # 计算统计量 mu1, sigma1 real_features.mean(0), torch_cov(real_features, rowvarFalse) mu2, sigma2 fake_features.mean(0), torch_cov(fake_features, rowvarFalse) # 计算FID diff mu1 - mu2 covmean sqrtm(sigma1 sigma2) fid diff.dot(diff) torch.trace(sigma1 sigma2 - 2*covmean) return fid目标是将FID控制在30以下这需要充分训练通常需要50-100个epoch使用足够大的批量至少64定期检查FID避免过拟合4. 模型调优与问题解决4.1 常见问题分析问题现象可能原因解决方案生成图像模糊判别器过强降低判别器学习率模式崩溃生成器多样性不足增加噪声维度训练不稳定学习率过高使用更小的学习率(如0.0001)生成图像有 artifacts网络容量不足增加滤波器数量4.2 高级调优技巧标签平滑防止判别器过度自信real_labels torch.FloatTensor(batch_size).uniform_(0.9, 1.0) fake_labels torch.FloatTensor(batch_size).uniform_(0.0, 0.1)梯度惩罚改进Wasserstein GANdef gradient_penalty(D, real_samples, fake_samples): alpha torch.rand(real_samples.size(0), 1, 1, 1).to(device) interpolates (alpha * real_samples (1 - alpha) * fake_samples).requires_grad_(True) d_interpolates D(interpolates) gradients torch.autograd.grad( outputsd_interpolates, inputsinterpolates, grad_outputstorch.ones_like(d_interpolates), create_graphTrue, retain_graphTrue, only_inputsTrue, )[0] gradients gradients.view(gradients.size(0), -1) penalty ((gradients.norm(2, dim1) - 1) ** 2).mean() return penalty学习率调度后期微调schedulerD torch.optim.lr_scheduler.StepLR(optimizerD, step_size30, gamma0.1) schedulerG torch.optim.lr_scheduler.StepLR(optimizerG, step_size30, gamma0.1)5. 结果分析与应用经过充分训练后我们的DCGAN能够生成高质量的人脸图像。以下是典型生成样本实际应用中可以考虑以下方向数据增强为分类任务生成更多训练样本图像编辑通过潜在空间插值实现属性编辑隐私保护生成匿名化人脸替代真实照片艺术创作生成不存在的人物肖像完整项目代码已开源在GitHub包含预训练模型和详细使用说明。在实际部署时建议使用ONNX格式导出模型以提高推理效率torch.onnx.export(netG, # 模型 torch.randn(1,100,1,1), # 输入样本 dcgan.onnx, # 保存路径 export_paramsTrue, # 导出参数 opset_version11, # ONNX版本 do_constant_foldingTrue, # 优化 input_names[input], # 输入名 output_names[output]) # 输出名