首页 / 资讯中心 / 文章详情

GAN稳定训练全解析:从梯度消失到模式崩塌的实战指南

GAN稳定训练全解析:从梯度消失到模式崩塌的实战指南 ★ FEATURED ARTICLE
1. 为什么 GAN 这么难训练先搞懂问题根源做生成对抗网络的人八成都被同一个问题折磨过训练不收敛、损失函数乱跳、生成图片全是噪声或者更经典的——模式崩塌生成器翻来覆去只会输出那么几张图。很多人把这归咎于“GAN 本来就难调”其实这个说法只说对了一半。1.1 一对冤家的零和博弈GAN 的本质是让两个神经网络互相“对抗”。生成器负责造假判别器负责打假两者在反复博弈中共同进化。你想想这个场景生成器是个伪造高手每次造出来的假画都要骗过判别器这个鉴赏家判别器越火眼金睛生成器就被逼得手艺越精。理想状态下两者旗鼓相当最后生成器造出的东西以假乱真判别器彻底分不清真假。但问题恰恰出在这个“旗鼓相当”上。一个跑得快另一个就会追不上一个太强另一个就彻底摆烂。实际操作中常见的情况是判别器一骑绝尘把生成器的每个输出都精准识破梯度传回去直接消失生成器从此学习不到任何有用信号。1.2 隐藏的“梯度消失”陷阱传统的 GAN 使用交叉熵作为损失函数这在训练早期有个致命隐患。当判别器轻松击败生成器时它给出的损失值会非常稳定反传回去的梯度几乎为零。生成器接收不到有效反馈训练就僵住了。换句话说问题越是容易判别器太容易分辨真假训练反而越难推进。更麻烦的是交叉熵对应的 JS 散度在两个分布完全不重叠时梯度会退化为零。然而内容生成领域的真实数据分布往往高维且稀疏生成样本和真实样本的重叠区域在训练初期极小梯度消失几乎必然发生问题几乎避无可避。1.3 稳定训练的突破口所以所谓“稳定训练 GAN”本质上是解决两个问题一是防止判别器过于强大导致梯度消失二是防止生成器找到某个“偷懒”的捷径输出单一模式蒙混过关。这两点贯穿整个调参过程理解了它们后面所有技巧才看得懂。接下来我会用 TensorFlow2、Keras 和 Python从零搭一个可运行的 GAN并分享那些能够真正提升稳定性的实操细节。这篇只讲基础方案把框架和稳定训练的核心理念讲透后续再聊更高级的变体。2. 环境准备与数据选型少踩一个坑省一小时正式写代码前先把环境和数据准备好。这里多花十分钟后面调试至少省一小时。2.1 搭建 TensorFlow2 环境我用的是 TensorFlow 2.10 及以上版本Keras 已经作为内置 API 集成直接import tensorflow as tf就能拿到 Keras 相关接口无需单独安装。pip install tensorflow2.10.* pip install matplotlib numpy有条件用 GPU 就尽量用 GPU不光是速度快慢的问题。CPU 上训练 GAN一个 epoch 可能要跑几分钟反复调参时每次改动都要等很久这个等待会打断思路很容易让人失去耐心乱调参数。我一开始在 CPU 上调参改了学习率之后等十分钟才看到效果整个人都被磨得没有脾气。有 GPU 至少能把反馈周期缩短到十几秒试错成本低很多。没有 GPU 的同学也别灰心这篇用的数据集很小CPU 也能跑只是需要耐心。2.2 为什么选 Fashion-MNIST 而不是 MNIST数据我用 Fashion-MNIST不是传统的 MNIST 手写数字。原因有两个第一MNIST 太简单了简单到很多技巧失效。字符结构极度规则生成器随便学点皮毛就能出像样的结果掩盖了稳定性问题。Fashion-MNIST 包含 T 恤、裤子、外套、裙子、鞋、包等 10 类衣物的灰度图纹理和轮廓变化更丰富生成器要真正学到结构特征这个“难度梯度”恰好卡在能暴露问题、又不至于让人绝望的位置。第二图片是 28x28 灰度单通道计算量小迭代速度快特别适合调试。拿它做稳定训练的实验场性价比极高。数据加载直接使用 TensorFlow 内置接口import tensorflow as tf (train_images, _), (_, _) tf.keras.datasets.fashion_mnist.load_data() train_images train_images.reshape(train_images.shape[0], 28, 28, 1).astype(float32) train_images (train_images - 127.5) / 127.5 # 归一化到 [-1, 1]归一化到 [-1, 1] 这一步很多人不注意但它很关键。生成器最后一层用 tanh 激活输出天然在 [-1, 1] 区间判别器的输入要跟它匹配所以真实图片也必须缩放到相同区间否则判别器直接根据“数值大小”就能区分真假训练就成了自欺欺人。这里还要注意除以 127.5 而不是 255这样能把数据中心拉到 0 附近让梯度传播更平稳。BUFFER_SIZE 60000 BATCH_SIZE 256 train_dataset tf.data.Dataset.from_tensor_slices(train_images).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)Batch Size 选 256是个折中方案。太大每个 batch 的梯度方向被平均得太厉害判别器对细节差异不敏感太小梯度噪声大训练曲线抖得厉害。256 在 Fashion-MNIST 上实测最稳。3. 从零搭建第一个 GAN完整可运行的代码骨架搭好了直接进入核心环节。我会把生成器、判别器和训练循环完整贴出来然后逐段解释每个设计决策背后的逻辑。所有代码基于 TensorFlow 2 的 Keras 接口运行环境是 Python 3.8 及以上版本。3.1 生成器从噪声到图像的“伪造者”生成器的任务是把一段随机噪声映射成一张图像。听起来抽象你可以把它理解成一个逆向的卷积网络普通图像分类是把二维像素压缩成一维类别生成器则是把一维噪声扩展成二维图像。这个过程用转置卷积Conv2DTranspose实现。def make_generator(): model tf.keras.Sequential([ tf.keras.layers.Dense(7*7*128, use_biasFalse, input_shape(100,)), tf.keras.layers.BatchNormalization(), tf.keras.layers.LeakyReLU(alpha0.2), tf.keras.layers.Reshape((7, 7, 128)), tf.keras.layers.Conv2DTranspose(64, (5, 5), strides(2, 2), paddingsame, use_biasFalse), tf.keras.layers.BatchNormalization(), tf.keras.layers.LeakyReLU(alpha0.2), tf.keras.layers.Conv2DTranspose(1, (5, 5), strides(2, 2), paddingsame, use_biasFalse, activationtanh) ]) return model第一次看到这个结构的人常问为什么第一层是 Dense为什么不直接用卷积因为输入是 100 维的噪声向量没有空间结构必须先用全连接层把它拉伸成有空间维度的特征图后面才能做卷积操作。7x7x128 这个尺寸不是拍脑袋定的它是从输出尺寸 28x28 反推的28 / 2 1414 / 2 7两个 stride2 的转置卷积正好把 7x7 放大到 28x28。每个卷积层后面接三个东西BatchNormalization、LeakyReLU、以及除最后一层外的无 bias 设置。BatchNorm 的作用是对中间特征做标准化让每一层的输入分布相对稳定缓解内部协变量偏移这在生成器里尤其重要因为生成器的输入是随机噪声分布本身就不稳定。LeakyReLU 的 alpha 取 0.2意思是负数区间保留 20% 的梯度而不是像 ReLU 那样直接置零——生成器如果大量神经元输出负数ReLU 会让梯度完全断掉LeakyReLU 保证梯度始终有通道回流。最后一层用 tanh 激活把输出压缩到 [-1, 1]与预处理后的真实图片数值区间一致。3.2 判别器打假专用的“质检员”判别器结构上很像图像分类网络但有两个关键差异值得注意。def make_discriminator(): model tf.keras.Sequential([ tf.keras.layers.Conv2D(64, (5, 5), strides(2, 2), paddingsame, input_shape(28, 28, 1)), tf.keras.layers.LeakyReLU(alpha0.2), tf.keras.layers.Dropout(0.3), tf.keras.layers.Conv2D(128, (5, 5), strides(2, 2), paddingsame), tf.keras.layers.LeakyReLU(alpha0.2), tf.keras.layers.Dropout(0.3), tf.keras.layers.Flatten(), tf.keras.layers.Dense(1) ]) return model第一个差异是判别器完全不用 BatchNorm。这个后面细说GAN 稳定训练的第一原则就是“生成器和判别器的处理要区别对待”。第二个差异是每个卷积后面都跟 Dropout。判别器的任务是“记忆”真实图片和生成图片的分布差异这个任务对它来说太容易了容易到它过拟合然后瞬间把生成器碾压。Dropout 强行给判别器加入噪声让它的判断不那么“自信”给生成器留出成长空间。注意判别器最后一层没有激活函数直接输出 logits配合from_logitsTrue的交叉熵损失函数使用。3.3 训练循环一个 batch 内完成两次更新这是整个 GAN 训练中最容易写错的环节。正确顺序是先训练判别器再训练生成器但两者用的是同一个 batch 的假图片。如果分开用不同 batch判别器先看到的假图片和生成器训练时的假图片不一致就会产生严重的“时序错位”训练曲线的震荡会非常厉害。cross_entropy tf.keras.losses.BinaryCrossentropy(from_logitsTrue) def discriminator_loss(real_output, fake_output): real_loss cross_entropy(tf.ones_like(real_output), real_output) fake_loss cross_entropy(tf.zeros_like(fake_output), fake_output) return real_loss fake_loss def generator_loss(fake_output): return cross_entropy(tf.ones_like(fake_output), fake_output)生成器损失里把假图片的标签设成 1这个操作新人看着会困惑明明生成的是假的为什么给它真标签原因在于生成器的目标不是“骗过自己”而是“骗过判别器”。如果标签设成 0生成器就去学习“如何生成假图片”而不是“如何生成真图片”。只有设成 1梯度方向才会推动生成器逼近真实图片的分布。训练循环用tf.GradientTape分别记录两次梯度再分别应用generator_optimizer tf.keras.optimizers.Adam(learning_rate0.0002, beta_10.5) discriminator_optimizer tf.keras.optimizers.Adam(learning_rate0.0002, beta_10.5) tf.function def train_step(real_images): noise tf.random.normal([BATCH_SIZE, 100]) with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape: generated_images generator(noise, trainingTrue) real_output discriminator(real_images, trainingTrue) fake_output discriminator(generated_images, trainingTrue) gen_loss generator_loss(fake_output) disc_loss discriminator_loss(real_output, fake_output) gradients_of_generator gen_tape.gradient(gen_loss, generator.trainable_variables) gradients_of_discriminator disc_tape.gradient(disc_loss, discriminator.trainable_variables) generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables)) discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables))两个优化器都用 Adam学习率 0.0002beta_1 取 0.5。这是稳定训练的第二个关键点。默认的 Adambeta_1 是 0.9这个值在常规分类任务中表现很好但在 GAN 里梯度的一阶动量积累得太快容易造成训练震荡。0.5 相当于把历史梯度的“记忆”砍半让当前梯度的影响力更大参数更新更“跟手”收敛曲线更平滑。tf.function装饰器把训练步编译成静态计算图能显著加速。不过加了之后调试时无法在中间打印建议先在纯动态模式下跑通确认无误再加这个装饰器。用tf.GradientTape而不是 Keras 的model.compilemodel.fit是因为损失计算涉及生成器和判别器的交叉更新compile 方式很难优雅地表达这种“对抗”逻辑。手写训练循环看起来繁琐但每一步都在掌控中对理解 GAN 的训练机制非常有帮助。3.4 完整的训练主循环完整跑通还需要一个循环。每个 epoch 结束后固定生成一批噪声种子用同一个噪声向量生成图片保存下来对比效果。固定种子很重要否则每次看的都是不同的随机噪声生成的结果无法判断生成器是否在某张具体图片上取得了进步。import matplotlib.pyplot as plt def train(dataset, epochs): for epoch in range(epochs): for image_batch in dataset: train_step(image_batch) if (epoch 1) % 10 0: generate_and_save_images(epoch 1) def generate_and_save_images(epoch): noise tf.random.normal([16, 100]) predictions generator(noise, trainingFalse) predictions (predictions 1) / 2 # 把 [-1,1] 映射回 [0,1] 方便显示 fig plt.figure(figsize(4, 4)) for i in range(predictions.shape[0]): plt.subplot(4, 4, i 1) plt.imshow(predictions[i, :, :, 0], cmapgray) plt.axis(off) plt.savefig(image_at_epoch_{:04d}.png.format(epoch)) plt.close() train(train_dataset, 100)显示图片前要把数值从 [-1, 1] 映射回 [0, 1]很多人直接 imshow 结果发现图片“发灰”其实就是忘了解归一化。这一步虽然简单但视觉反馈直接影响你对训练状态的判断做错一步后面全看错。4. 稳定训练的五个关键细节为什么这么做做了会怎样代码跑起来很容易稳不稳定就是另外一回事了。这一节是我反复调试后总结出的五个关键细节含金量不低建议反复看。4.1 标签平滑给判别器一点“糊涂”标签平滑Label Smoothing是稳定 GAN 最有效、最常用的小技巧之一。做法很简单把真实图片的标签从 1 改成 0.9假图片的标签从 0 改成 0.1或保持 0。代码里长这样def discriminator_loss(real_output, fake_output): real_loss cross_entropy(0.9 * tf.ones_like(real_output), real_output) fake_loss cross_entropy(tf.zeros_like(fake_output), fake_output) return real_loss fake_loss原理也不难懂判别器的目标是二分类如果给它“满分”的标签它会学得过于自信决策边界非常尖锐。一旦边界太尖锐生成器稍有一点偏离就会被判别器致命打击梯度瞬间爆炸。把标签从 1 换成 0.9等于告诉判别器“这些图片并不完美你只是比较确定它们是真实的”决策边界就平滑了梯度不会骤变训练过程就稳定下来了。我试过 0.9/0.0 的组合和 0.9/0.1 的组合前者更容易理解也更好调后者在特定数据集上略稳但差异不明显。先试 0.9/0.0如果还震荡再考虑 0.9/0.1。4.2 生成器用 BatchNorm判别器别用这个“区别对待”可以说是 GAN 工程经验的精髓之一。生成器里每一层卷积后都接 BatchNorm因为它的输入是随机噪声分布变化剧烈BatchNorm 能帮你把中间层的输入分布拉回“正常范围”让梯度传播稳定。可以理解成给生成器铺了一层“平滑的路面”参数更新不容易走偏。但判别器完全相反。它接收的是真实图片和生成图片输入分布相对固定不需要额外的标准化。如果加了 BatchNorm每个 batch 的均值方差都不同判别器的输出会跟着抖动这种抖动会传染给生成器的梯度更新导致训练曲线忽上忽下。另一个实际原因是设计选项BatchNorm 在 batch size 较小时形成小批量统计噪声很大GAN 里的判别器本来就在和生成器对抗再引入这种不确定性很难判断当前是“谁”在变强。去掉 BatchNorm用 Dropout 反而更好。Dropout 也加了噪声但它是加在神经元激活值上的对整体输出的扰动比 BatchNorm 温和。4.3 优化器参数Adam 的 beta_1 一定要调前面代码里 Adam 的 beta_1 设成 0.5这里展开讲讲为什么。Adam 的更新公式可以通俗理解为每一步的方向由当前梯度一阶动量和历史梯度方向共同决定beta_1 就是历史梯度的影响权重。默认 0.9 意味着历史梯度占 90% 的权重更新方向基本由过去一段时间的梯度均值决定。这在稳定目标上没问题但在 GAN 这种“目标不断移动”的场景里过去的梯度方向可能已经过时了——判别器在变、生成器在变旧方向会拖累更新导致震荡。beta_1 0.5相当于只保留一半历史记忆当前梯度的影响力更重参数更新更敏捷。如果你发现生成器更新太激进、损失震荡可以试试调到 0.4 甚至更低如果训练过于“迟钝”则往回调。观察目标是生成图片的质量曲线不是损失函数值。学习率 0.0002 也是经典选择比默认的 0.001 小很多。GAN 对学习率极其敏感稍大一点就振荡小步慢跑反而更快到达目标。这个“慢即是快”的感觉GAN 玩家都懂。4.4 噪声维度100 够用别贪多生成器输入的噪声维度是 100这是 DCGAN 论文里的常用配置。有人喜欢用 512 或 1024觉得“信息量更大”能力更强但实际操作中噪声维度增加会带来两个问题一是生成器的参数量大幅增加过拟合的几率变大二是高维噪声空间里大部分维度对生成结果的影响微弱浪费了参数不说训练稳定性反而下降。100 维在 Fashion-MNIST 这类简单数据集上完全够用。你可以把它想象成 100 个“旋钮”每个旋钮控制生成图片的某个属性——轮廓、纹理、亮度等。对于一个 28x28 的灰度图100 个自由度已经远超描述它所需的维度。等做到更复杂的数据集时再考虑提升不迟。4.5 判别器的训练频率别让打假者太强默认策略是每个 batch 里判别器和生成器各更新一次。但如果发现判别器的损失持续降低、生成器的损失持续升高这种情况意味着判别器太强了生成器一败涂地可以在同一 batch 里先更新判别器两次再更新生成器一次相当于给生成器更多“追赶”机会。反过来的情况也存在生成器损失极低几乎稳定在某个很小的值但生成的图片质量很差这意味着生成器发现了一个“欺骗判别器”的捷径模式崩塌的前兆。这时反而要让判别器多训练几次增强判假能力。这种“此消彼长”的动态调节说穿了就是观察生成效果倒推网络强弱不断调整训练比例和参数数值具体调法需要反复实验。5. 常见问题与排查技巧实录每次在群里看别人调 GAN问的问题翻来覆去就那么几个答案其实也大同小异。下面直接给一份“症状-原因-解法”速查表遇到问题对着查就行。5.1 问题速查表症状可能原因解决方案生成图片全是噪声训练不足或生成器学习率过高先跑够 50 个 epoch把学习率降到 0.0001 再试生成图片全部一样模式崩塌生成器找到单一捷径判别器多训几次检查是否用了标签平滑查噪声是否重复判别器损失趋近于 0判别器完胜梯度消失打开标签平滑调低判别器学习率加强 Dropout训练曲线剧烈震荡学习率过大或 beta_1 太大把 beta_1 调回 0.5学习率降低一个量级损失值下降但图片变差判别器过拟合生成器在退步给判别器增加 Dropout让它少训几次图片有规律性棋盘纹转置卷积重叠导致换用上采样普通卷积把 stride 从 2 改成 1 配合 padding5.2 如何判断训练是否正常别死盯损失值GAN 的损失值没有绝对参考意义这是新人最容易踩的坑。分类任务的损失值 0.1 说明好0.8 说明差但 GAN 不是这样。判别器损失和生成器损失永远在拉锯你看到的数值取决于当前谁占上风不代表模型好坏的绝对标准。正确做法是固定一个噪声种子每 N 个 epoch 生成同一批图片直接看生成结果的变化曲线。训练正常时图片会从纯噪声逐步变得有轮廓、有结构、越来越清晰。训练不正常时图片可能在某个阶段突然退化成单一模式或一直停留在模糊状态。图片才是金标准损失值只作参考。如果两者矛盾永远以图片为准。5.3 我在实际训练中遇到的真实问题记录记录一次真实训练在 Fashion-MNIST 上第一次完整跑 GAN跑到 30 个 epoch 时损失曲线看起来很漂亮判别器损失稳定在 0.7 左右生成器损失在 1.2 左右但生成的图片全是扭曲的轮廓看不出任何衣物特征。后来排查发现是标签没有平滑判别器对“真”和“假”的分类太过自信决策边界过于尖锐生成器虽然损失在降低但方向偏了。开了标签平滑后前两个 epoch 生成质量没明显变化到第 60 个 epoch 后突然“觉醒”开始出现轮廓清晰的 T 恤和鞋子。另一个问题是 batch size 从 256 改成 64 后训练曲线开始剧烈抖动。原因在于 batch 太小梯度噪声太大。特别是判别器用了 Dropout 之后小 batch 上 Dropout 的随机性被放大判别器对不同 batch 的判断尺度不一致导致生成器的梯度极其不稳定。换回 256 后问题消失。这个细节验证了 batch size 确实和 Dropout 存在耦合关系调 GAN 时改参数要综合考虑。5.4 两个长期有用的调试技巧久病成医分享两个长期有用的调试技巧。第一个是固定噪声种子做 A/B 对比训练过程中固定同一份噪声向量每 20 个 epoch 保存一次生成结果这样你可以直观看到同一个“起点”的变化判断生成器是在稳步提升还是反复横跳。如果同一份噪声在不同 epoch 生成的图片差异巨大说明训练不稳定如果只是缓慢变化说明稳定但可能学得慢。第二个是给判别器做“盲测”训练到一半时拿真实图片和生成图片混在一起用当前判别器给它们打分。如果真实图片的得分显著高于生成图片说明判别器还有很强的区分能力生成器还需要继续学如果两者得分接近说明生成器追上了。这个做法比看损失值直观得多能帮你准确判断“现在是谁更强”这一核心状态。6. 损失曲线怎么读突破“读了但没懂”的困局很多人训练时打开 loss 曲线就开始盯但盯很久也说不清“这曲线到底正不正常”。这一节我把常见曲线形态和对应含义讲透下次你再看到曲线就能自己判断了。6.1 正常训练的曲线长什么样一个健康的 GAN 训练过程损失曲线看起来并不“漂亮”——不一定是平滑下降更多是动态平衡。判别器损失会先上升因为生成器在进步判假难度在增加生成器损失会先下降但不稳定。整个过程里两条曲线像拔河一样来回拉扯但整体趋势是判别器损失维持在一个中间水平比如 0.5-0.8生成器损失逐步下降。我见过很多初学者看到判别器损失从 0.2 涨到 0.6 就慌了以为训练出问题了。其实这恰恰说明生成器在变强。判别器损失越低说明它越容易分辨真假这对生成器反而是坏事——它说明生成器水平还不行。真正需要警惕的是两种极端判别器损失趋近于 0几乎全对生成器完全没学到东西或者生成器损失趋近于 0找到了欺骗判别器的捷径模式崩塌的前兆。6.2 典型曲线的诊断价值折线图会有很典型的分歧形态比如判别器损失快速归零、生成器损失快速上升的组合说明判别器“碾压”生len器。这种时候那几步操作可以对号入座——调低判别器学习率或者给生成器更大的梯度权重。反过来一种“看起来很好但实际很糟”的曲线也值得注意两个损失都在快速下降且生成器损失压得很低看起来像双赢——但生成质量极差。这种是生成器摸到捷径路线的典型表现它找到了一些特定模式去骗过判别器而不是真正逼近“真实图片分布”。判别器被带偏学到的判据不是“图片真不真”而是“某些固定 pattern 存不存在”。遇到这种情况先按住指针检查噪声是否重复、判别器是否过拟合再考虑加样本多样性惩罚。6.3 损失曲线是仪表盘不是方向盘说到底损失曲线只是仪表盘不是方向盘。它告诉你当前各部件状态但不是你调整方向的决定性依据。真正的决定权在生成图片质量——如果图片在逐渐变好哪怕损失曲线难看一点也无妨如果图片停滞不前就算损失曲线再平滑漂亮也一样没有意义。所以每次训练我都会在同一个固定噪声的生成图片序列旁边同步记录关键超参数。这样快速归因效果很好图片变清晰的时候我在用什么学习率、什么损失策略图片退步的时候我改了什么参数。长期积累会形成自己的调参手感。7. 从最小可用到效果可看的三个试验性调整当你把基础版跑通图片已经有模糊轮廓但还不够真实时就到了微调阶段。这里提供三个经过验证的调整方向按顺序试验。7.1 给生成器加一个辅助任务第一个调整方向是给生成器加一个辅助分类任务。Fashion-MNIST 有标签信息但之前没用到可以要求生成器在输出图片的同时预测这张假图的类别。这样生成器的损失函数就有两部分对抗损失 分类损失相当于逼着生成器不仅要骗过判别器还要学会区分不同类别泛化能力大幅增强生成的样本多样性通常也会有明显提升。实现要点生成器末尾分两条支路一条输出图像一条输出类别 logits。训练时用真实标签监督分类支路梯度同时回流到共享卷积层。7.2 把生成器换成上采样结构第二个调整方向是改换生成器的“手”法。前面用的转置卷积容易产生棋盘格伪影如果你生成的图片上看到规律性网格纹理那不是图像本身的结构而是转置卷积重叠导致的伪影。比转置卷积更平滑的方案是“上采样 普通卷积”组合先通过 UpSampling2D 把特征图放大再用普通卷积平滑处理。代价是参数量增加和训练更慢但图像质量通常更干净。相对而言Fashion-MNIST 这类简单灰度图出现的伪影不算严重可以先不做换到复杂彩色图时再用这个方案。7.3 用截断技巧偷看生成空间第三个调整方向是在训练结束后做“截断技巧”。它不改变训练过程只改变采样方式生成图片时先采样噪声 z如果 z 的某个维度值偏离均值太远比如超过 2 个标准差就重新采样。这么做能过滤掉生成器不太擅长的“极端输入区域”。实际效果是图片整体更稳定、更锐利但代价是多样性稍微下降。这个技巧对探索生成器能力非常合适也适合做模型效果演示但不适合直接拿来衡量模型的真实生成能力因为多样性的损失不容易察觉。我的习惯是训练过程中看完全随机采样选型做演示时用截断采样各取所需。8. 我个人在实际操作中的几条体会写到最后说点掏心窝的话。GAN 训练是我接触过的深度学习方向里最考验“手感”的领域之一。同样的代码、同样的参数换一个数据集可能结果天差地别甚至同一个数据集换一次随机种子结果都不同。这种不确定性一开始让我非常烦躁后来看开之后它反而成了这门技术的魅力所在——你永远在跟一个不断变化的系统共舞靠的不只是理论还有经验和对细节的敏感度。我个人的几条经验供你参考第一调参要“一次只动一个变量”。初学者最容易犯的错误是同时改学习率、改 batch size、改网络层数结果训练变差了也不知道是哪一步导致的。我在一个项目里因为同时调了学习率和标签平滑花了整整两天才定位到问题出在学习率上。所以每次只动一个变量记录结果观察至少 20 个 epoch再决定下一步。这是一个近乎强迫症的流程但确实能救你一命。第二时间盒设置和面对“模型失效”的心态。给一次调参设置时间盒如果一个方向跑了 1-2 小时后完全没有迹象收敛果断放弃这个方向不要因为“已经跑了很久”就舍不得。另一个建议是保持冷静GAN 训练失败率非常高一开始十次有八次是“合理”的不要因为几次失败就怀疑自己。把每一步调整做成实验记录失败记录到最后往往是你调参手感提升最快的时候。第三把“能不能稳定训练”和“生成质量高不高”分开看待。稳定训练是一个工程问题——梯度是否稳定、损失是否可控、模式崩塌是否发生生成质量是一个能力问题——模型结构够不够强、训练数据够不够好。这两者经常被混为一谈但解法完全不同。如果是稳定性的问题调参就能解决如果是能力问题就得改结构、换损失、扩数据。先分清你遇到的是哪个问题再动手解决。下一篇会写条件生成CGAN让生成器学会按指定类别生成图片代码结构会更复杂但思路依然建立在今天这套稳定训练框架上。在那之前先把这篇里的基础建议跑熟图片质量就够你拿得出手了。
阅读完成 · 觉得有帮助?
咨询建站