PyTorch搭建DCGAN在MNIST效果好但自定义轨迹数据集效果差调优
DCGAN自定义轨迹数据集生成失效调优方案
问题背景
基于PyTorch搭建标准DCGAN,使用包含20000条简单路径的数据集训练,目标是生成机器人轨迹数据。
- 同一网络在MNIST数据集上训练20轮后生成效果达标,生成器、判别器损失均在0.7左右进入平台期


- 相同训练配置下,自定义轨迹数据集生成效果极差,判别器、生成器损失同样卡在0.7左右进入平台期


注:D、G损失同时稳定在0.7左右不代表训练收敛。该数值是判别器对所有样本输出0.5概率(完全无法区分真假)时的BCELoss理论值(-ln0.5≈0.693),本质是训练进入无有效梯度的无效均衡。MNIST任务中网络先学到数字轮廓再进入该均衡,因此生成效果达标;轨迹任务中网络未学到有效路径特征就提前进入该均衡,因此生成结果失效。
现有代码实现
判别器代码
class Discriminator(nn.Module): def __init__(self, channels_img, features_d): super(Discriminator, self).__init__() self.disc = nn.Sequential( # input: N x channels_img x 64 x 64 nn.Conv2d( channels_img, features_d, kernel_size=4, stride=2, padding=1 ), nn.LeakyReLU(0.2), # _block(in_channels, out_channels, kernel_size, stride, padding) self._block(features_d, features_d * 2, 4, 2, 1), self._block(features_d * 2, features_d * 4, 4, 2, 1), self._block(features_d * 4, features_d * 8, 4, 2, 1), # After all _block img output is 4x4 (Conv2d below makes into 1x1) nn.Conv2d(features_d * 8, 1, kernel_size=4, stride=2, padding=0), nn.Sigmoid(), ) def _block(self, in_channels, out_channels, kernel_size, stride, padding): return nn.Sequential( nn.Conv2d( in_channels, out_channels, kernel_size, stride, padding, bias=False, ), nn.BatchNorm2d(out_channels), nn.LeakyReLU(0.2), ) def forward(self, x): return self.disc(x)
生成器代码
class Generator(nn.Module): def __init__(self, channels_noise, channels_img, features_g): super(Generator, self).__init__() self.net = nn.Sequential( # Input: N x channels_noise x 1 x 1 self._block(channels_noise, features_g * 16, 4, 1, 0), # img: 4x4 self._block(features_g * 16, features_g * 8, 4, 2, 1), # img: 8x8 self._block(features_g * 8, features_g * 4, 4, 2, 1), # img: 16x16 self._block(features_g * 4, features_g * 2, 4, 2, 1), # img: 32x32 nn.ConvTranspose2d( features_g * 2, channels_img, kernel_size=4, stride=2, padding=1 ), # Output: N x channels_img x 64 x 64 nn.Tanh(), ) def _block(self, in_channels, out_channels, kernel_size, stride, padding): return nn.Sequential( nn.ConvTranspose2d( in_channels, out_channels, kernel_size, stride, padding, bias=False, ), nn.BatchNorm2d(out_channels), nn.ReLU(), ) def forward(self, x): return self.net(x)
训练循环代码
opt_gen = optim.Adam(gen.parameters(), lr=LEARNING_RATE_GEN, betas=(0.5, 0.999)) opt_disc = optim.Adam(disc.parameters(), lr=LEARNING_RATE_DISC, betas=(0.5, 0.999)) criterion = nn.BCELoss() for epoch in range(NUM_EPOCHS): # Target labels not needed! <3 unsupervised # for batch_idx, (real, _) in enumerate(dataloader): for batch_idx, real in enumerate(dataloader): real = real.to(device) noise = torch.randn(BATCH_SIZE, NOISE_DIM, 1, 1).to(device) fake = gen(noise) ### Train Discriminator: max log(D(x)) + log(1 - D(G(z))) disc_real = disc(real.float()).reshape(-1) loss_disc_real = criterion(disc_real, torch.ones_like(disc_real)) disc_fake = disc(fake.detach()).reshape(-1) loss_disc_fake = criterion(disc_fake, torch.zeros_like(disc_fake)) loss_disc = (loss_disc_real + loss_disc_fake) / 2 disc.zero_grad() loss_disc.backward() opt_disc.step() ### Train Generator: min log(1 - D(G(z))) <-> max log(D(G(z)) output = disc(fake).reshape(-1) loss_gen = criterion(output, torch.ones_like(output)) gen.zero_grad() loss_gen.backward() opt_gen.step() # Print losses occasionally and print to tensorboard if batch_idx % 100 == 0: print( f"Epoch [{epoch}/{NUM_EPOCHS}] Batch {batch_idx}/{len(dataloader)} \ Loss D: {loss_disc:.4f}, loss G: {loss_gen:.4f}" ) with torch.no_grad(): fake = gen(fixed_noise) # take out (up to) 32 examples img_grid_real = torchvision.utils.make_grid( real[:BATCH_SIZE], normalize=True ) img_grid_fake = torchvision.utils.make_grid( fake[:BATCH_SIZE], normalize=True ) writer_real.add_image("Real", img_grid_real, global_step=step) writer_fake.add_image("Fake", img_grid_fake, global_step=step) step += 1
调优方案
按优先级从高到低排查修改:
- 数据预处理对齐
生成器最后一层为nn.Tanh(),输出范围固定为[-1,1],必须将轨迹训练数据通过(data - 0.5)/0.5归一化到相同区间,禁止归一化到[0,1]或其他范围,否则判别器可直接通过数值范围区分真假样本,快速进入无效均衡。
可视化阶段先关闭make_grid的normalize=True参数,直接查看原始输出的像素分布,避免被自动拉伸后的可视化结果误导,确认真实数据和生成数据的数值范围完全对齐后再开启归一化展示。 - 训练策略调整
- 加入标签平滑:真实样本标签设为0.9,生成样本标签设为0.1,避免判别器输出过度接近0/1导致梯度消失。
- 替换损失函数:去掉判别器最后一层的
nn.Sigmoid(),将nn.BCELoss()替换为nn.BCEWithLogitsLoss(),提升数值稳定性。 - 调整学习率:将生成器、判别器的学习率从MNIST常用的2e-4下调到1e-4,避免训练震荡。
- 动态调整训练频次:训练初期每训练1次生成器,训练2次判别器;若判别器损失跌破0.3,立刻改为每训练2次生成器训练1次判别器,保证两者训练强度匹配,避免判别器过早失效。
- 网络结构适配
轨迹数据是单通道稀疏线条,像素占比远低于MNIST数字,标准DCGAN的多次2倍下采样会快速丢失细粒度路径特征:- 将判别器前2个卷积块的步长从2改为1,降低下采样倍率,保留细粒度线条特征。
- 将噪声维度
NOISE_DIM从默认100提升到128~256,给生成器足够的编码空间表达不同轨迹形态。 - 若生成结果仍存在块状噪声,将判别器部分BatchNorm层替换为InstanceNorm,缓解稀疏数据的BN统计量偏移问题。
内容的提问来源于stack exchange,提问作者p1unge
相关产品推荐
相关产品推荐

