资讯动态

GAN入门:生成器判别器对抗训练与PyTorch实战解析

发布时间:2026/10/14 5:38:42 来源:尧图企业网站定制
我第一次跑通GAN的时候盯着训练日志里逐渐成形的数字愣了好几秒。生成式对抗网络GAN的Basic Idea其实一句话就能说完让一个生成器去临摹图片让一个判别器去分辨真伪两者互相较劲最后生成器产出的图片连判别器都认不出来。但这句话落到数学公式和代码里会有很多细节值得掰开揉碎。这是某门公开课第74讲的核心主题——先讲清楚GAN的基本想法再说明生成器和判别器如何协作、如何对抗。这篇文章适合刚学完分类和回归、想接触生成式模型的人也适合看过公式但还没动手写过完整GAN代码的读者。我会先拆解整体设计再逐段解释目标函数然后给一份能在本地跑起来的最小PyTorch实现最后聊一聊我在调GAN时踩过的坑。1. 不只是生成图片GAN要解决的核心难题1.1 生成问题为什么比分类问题难分类任务里模型只需学到一个决策边界把不同类别的样本分开。训练信号非常直接标签错了就调整直到输出概率靠近正确答案。可生成任务完全不同。模型要输出一整张图784个像素或者更高的分辨率怎么定义“好不好”如果拿像素差距当标准用L2损失去训练模型最后会把所有可能的结果平均掉出来的图像是一团模糊的“平均脸”。这是我早期用自编码器生成图像时最直观的感受网络学会了压低损失但生成的图片质感太差边缘模糊细节全是浆糊。问题的根源在于真实数据的分布是复杂的高维分布像素级误差根本衡量不了“看起来真不真实”。人类判断一张图是否真实依靠的是几十年积累的视觉经验而不是某个明确公式。与其费力设计一个完美的损失函数不如让模型自己学习这个判断标准。GAN的思路正是这样把“像不像”这个评价任务交给一个网络——判别器让它动态判断当前生成的样本是否逼真。一个更接近日常的类比是你让一个新手画师去临摹名画如果只检查“颜料颜色差多少”“笔触位置差多少”他永远只能画出四平八稳却毫无神韵的仿品。但如果你找一位经验丰富的鉴赏家站在旁边每次临摹完都指出“这里的气质不对、那边的结构失真”画师反而会越画越像。GAN里的判别器扮演的就是这位不断给出反馈的鉴赏家。1.2 对抗思想画师与鉴赏家的博弈理解GAN我建议先放下公式想象一个画师和一个鉴赏家。画师拿着一堆随机的草稿噪声向量目标是画出足以乱真的画让鉴赏家认不出鉴赏家看过很多真迹目标是把画师的临摹品从真迹里挑出来。每轮流程是画师交出新画鉴赏家判断真假并给出反馈画师根据反馈修改技法鉴赏家再升级识别能力。两个人不停地较劲画师的画技会越来越强鉴赏家的眼光也越来越毒。这就是对抗学习。生成器是画师负责从随机噪声生成样本判别器是鉴赏家负责判断输入样本是真实数据还是生成数据。重点在于两边都在动态进化。生成器变强要求判别器也必须变强判别器变强反过来倒逼生成器继续提升。最理想的结果不是某一方胜利而是生成器技高一筹让鉴赏家只能给出50%的真伪概率——这时候生成分布已几乎等同于真实分布。而公开课Basic Idea里那张经典示意图本质就是在展示“噪声分布通过生成器后逐渐逼近真实分布”这个过程。这个思想最迷人的地方在于它完全绕过了手工设计评价标准。你不需要告诉模型“眉毛弧度应该更自然光影过渡应该更柔和”只需要给它一个能不断进化的评价者。生成器只要足够努力就有希望骗过评价者而评价者为了不被骗又会变得更加敏锐。双方在互相追逐中共同逼近真实数据的统计特征。1.3 公开课里的Basic Idea到底讲了什么这门公开课的第74讲定位是入门。它先把“如何让机器学会生成新图片”这个问题摆上台面然后给出GAN的框架生成器输入噪声、输出图片判别器输入图片、输出概率用目标函数定义双方博弈再通过交替训练一步步迭代。课程里没有急着讨论复杂变体很多地方点到为止比如目标函数为什么长那样训练循环为什么必须交替更新等等。我当初看完视频后公式大致懂了但写代码时依然卡在损失计算和梯度传递上。所以下面两节我把课程里点到为止的内容展开结合代码实现的方式重新讲一遍。你可以把它当成课堂笔记的补充也可以直接当一份操作手册来用。尤其是“为什么生成器要用全1标签”“为什么判别器更新时要切断梯度”这类问题光看公式很难想明白但放到代码里一看就透。2. 核心细节解析生成器、判别器与目标函数2.1 生成器从随机噪声到图像生成器的输入通常是低维噪声向量 z常见做法是从标准正态分布采样维度取100。这个向量没有任何视觉含义它更像是一个“随机种子”决定了这张图最终长成什么样。生成器网络要做的事是把这100个数映射到与真实样本同维度的输出上。以MNIST为例真实图片展平后是784维所以生成器最后一层输出784维再经过Tanh激活函数把数值约束在[-1, 1]区间。噪声维度的选择有讲究。维度太低能表达的隐变量信息不够样本多样性受限维度太高网络需要更大的容量来消化收敛变慢训练难度增加。100维是很多入门实验验证过的常用值。另外不同z会得到不同输出这说明生成器学会的不是简单记录而是把高维分布提炼进了网络参数里。训练完成后还可以在z空间做插值比如把两个人脸图片对应的z线性混合生成结果是平滑过渡的脸。这个特性也是后续很多生成编辑技术的基础。从信息论的角度看生成器本质上在做一件事把不可控的高维随机噪声转化为可控的高维数据样本。训练前这个映射关系是混乱的训练后映射关系里沉淀了真实数据的结构性规律。判断生成器是否学会不能只看它生成的单张图是否清晰还要看它能否覆盖真实数据的多样性。2.2 判别器真伪判定的设计判别器是一个二分类器输入图片同样展平成向量输出0到1之间的概率代表“这图片看起来有多像真图”。训练目标是尽可能给真实图片高分给生成图片低分。这里的“真图”指的是训练集里的样本“生成图”由当前生成器产生。判别器的设计会直接影响整个训练节奏。容量太小学不出真假之间的差异生成器得不到有效反馈容量太大判断能力过强生成器梯度消失难以进步。在实际工程里常见做法是让判别器和生成器的参数量处于同一量级并使用带负梯度的激活函数比如LeakyReLU避免负区间完全失活导致判别器对生成样本“无感”。需要特别说明的是判别器不是一次性训练到位它是和生成器同步升级的。第二轮的“假图”已经明显比第一轮更逼真为了识别它们判别器必须不断适应。正是这种双重递进让训练过程远复杂于普通二分类任务。普通分类任务的数据分布固定不变模型只是去拟合一个静态边界GAN里每一次参数更新都会改变下一轮数据的分布所以优化更像一场动态追逐。2.3 一段公式看懂生成与判别的博弈目标函数是GAN最核心的数学表达课程里会给出来但很多人第一眼觉得抽象。下面拆开讲[ V(D, G) E_{x \sim p_{data}}[ \log D(x) ] E_{z \sim p_z}[ \log(1 - D(G(z))) ] ]第一项里x来自真实数据分布D(x)表示判别器给真实样本打的分。对于判别器来说这一项越大越好因为log D(x)在D(x)接近1时最大。第二项里z来自噪声分布G(z)是生成器产出的假样本。判别器希望D(G(z))尽量接近0这样log(1-D(G(z)))会接近0第二项更大。换句话说判别器想识破所有假样本。那生成器的目标是什么它希望整个V尽可能小。由于第一项不受生成器控制它只能去影响第二项。当D(G(z))接近1的时候log(1-D(G(z)))趋近负无穷V就会被压低。所以生成器的意图就是让判别器对假样本给出高分。玩家想做什么对应的数学行为判别器让真图得分高、假图得分低最大化整个V生成器让假图得分高骗过判别器最小化整个V这里有一个实际训练中的关键技巧。理论上生成器要用loss log(1 - D(G(z))) 做最小化但训练早期判别器很容易识破生成器D(G(z))会非常接近0。此时log(1-D(G(z)))的导数极其微小生成器几乎收不到梯度陷入“学不动”的状态。所以实际代码里生成器的loss通常改成最大化log D(G(z))也就是直接用“判别器给出的假图得分”作为生成器的损失让生成器去提高这个分数。这样做并没有改变博弈的本质却让生成器在早期也能获得充足梯度。很多初学者在代码里看到G的标签全是1感到困惑理解了这个替代目标就明白了。2.4 交替训练两个玩家的均衡点训练流程看起来很简单从真实数据里抽一批样本从噪声分布里抽一批z生成假图先固定生成器参数更新判别器让它学会区分这一轮的真假再固定判别器参数更新生成器让它针对当前判别器的弱点改进然后重复。每轮判别器都在适应新的生成样本每轮生成器都在寻找新的弱点进行攻击。这里的“交替”不是可有可无的工程细节而是算法设计的一部分。如果同时更新两个网络两边会各自朝着自己的目标猛冲很容易训练崩溃。交替更新则相当于一个双人舞每一步都让对方先展示当前的能力你再据此调整自己的动作。判别器展示的是当前识破水平生成器据此改进仿制水平反过来生成器展示当前仿制水平判别器再提升识别精度。另一个很重要的事实是GAN不是单目标优化而是双玩家的博弈。所以传统深度学习里“训练loss不断下降到接近0”的经验在这里经常失效。理想情况是达到均衡状态生成分布等于真实分布判别器输出恒为0.5也就是完全猜不出真假。但对大多数任务而言这个均衡点很难安稳到达训练过程更像两个玩家在互相追逐loss曲线上下波动非常正常。理解这一点能少走很多弯路。3. 手把手实操PyTorch实现一个最小GAN3.1 环境准备与MNIST数据加载代码部分我以PyTorch为例。你只需要本机有Python 3.x和PyTorch再装torchvision用于加载MNIST。MNIST是入门首选单张图28x28展平后只有784维信息量小CPU上跑十几分钟就能看到效果试错成本极低。我建议第一次实现时不要直接上高分辨率数据集否则排查问题会非常痛苦。import torch import torch.nn as nn import torchvision import torchvision.transforms as transforms transform transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,)) ]) dataset torchvision.datasets.MNIST(root./data, trainTrue, transformtransform, downloadTrue) dataloader torch.utils.data.DataLoader(dataset, batch_size64, shuffleTrue)Normalize((0.5,), (0.5,))这一步把像素值从[0,1]映射到[-1,1]目的是和生成器最后一层的Tanh输出对齐。如果生成器用Sigmoid输出那就不需要Normalize但实践里Tanh对应[-1,1]更常见。因为真实图像像素的均值通常在0.5附近Tanh的零中心特性让网络更容易训练。还有一个值得养成的习惯在训练开始前生成一个固定噪声种子比如fixed_noise torch.randn(64, z_dim)每隔固定步数用同样的噪声生成图像。不同z之间没有可比性同一组z的前后变化才能直观反映生成器是否在进步。这个技巧在调试时非常实用后面可视化部分还会提到。3.2 搭建生成器网络class Generator(nn.Module): def __init__(self, z_dim100, img_dim784): super().__init__() self.net nn.Sequential( nn.Linear(z_dim, 256), nn.ReLU(inplaceTrue), nn.Linear(256, 512), nn.ReLU(inplaceTrue), nn.Linear(512, img_dim), nn.Tanh() ) def forward(self, z): return self.net(z)只有两层隐藏层原因是MNIST难度低模型太复杂反而容易出现训练失衡。参数总量并不大但已经足够把100维随机向量映射到784维图像。隐藏层用ReLU提升非线性表达能力输出层用Tanh约束输出区间。如果之后你换到更复杂的数据集可以把Linear换成ConvTranspose2d做上采样但思路完全一样。在模型初始化上我建议先用默认初始化跑跑出问题后再考虑更复杂的权重初始化方案。直接在入门阶段套用复杂初始化反而不容易判断训练问题的根源。生成器输出的新鲜样本刚开始肯定很粗糙这是正常的意味着网络参数还没有被训练信号盘活。3.3 搭建判别器网络class Discriminator(nn.Module): def __init__(self, img_dim784): super().__init__() self.net nn.Sequential( nn.Linear(img_dim, 512), nn.LeakyReLU(0.2, inplaceTrue), nn.Linear(512, 256), nn.LeakyReLU(0.2, inplaceTrue), nn.Linear(256, 1), nn.Sigmoid() ) def forward(self, x): return self.net(x)判别器结构上和生成器对称参数量接近能防止某一方过于强势。激活函数选择LeakyReLU而非ReLU是因为ReLU在负区间输出恒为0会让梯度无法回传判别器对部分生成样本失去反应LeakyReLU在负区间保留了0.2倍的小梯度信息流动更顺畅。Sigmoid把输出压缩成概率形式方便和BCELoss配合使用。如果你想省一次Sigmoid计算也可以在最后一层不写激活改用BCEWithLogitsLoss。但对理解原理来说保留Sigmoid更直观因为你打印出来的数值直接就是概率。等基础跑通之后再折腾“没有Sigmoid的变体版”会更容易接受。3.4 训练循环与损失计算训练主循环是整个实现的重点完整代码如下criterion nn.BCELoss() opt_G torch.optim.Adam(G.parameters(), lr0.0002, betas(0.5, 0.999)) opt_D torch.optim.Adam(D.parameters(), lr0.0002, betas(0.5, 0.999)) epochs 50 z_dim 100 for epoch in range(epochs): for i, (real_imgs, _) in enumerate(dataloader): real_imgs real_imgs.view(-1, 784) batch_size real_imgs.size(0) real_labels torch.ones(batch_size, 1) fake_labels torch.zeros(batch_size, 1) # 1. 更新判别器 z torch.randn(batch_size, z_dim) fake_imgs G(z) d_real D(real_imgs) d_fake D(fake_imgs.detach()) loss_D criterion(d_real, real_labels) criterion(d_fake, fake_labels) opt_D.zero_grad() loss_D.backward() opt_D.step() # 2. 更新生成器 z torch.randn(batch_size, z_dim) fake_imgs G(z) d_fake D(fake_imgs) loss_G criterion(d_fake, real_labels) opt_G.zero_grad() loss_G.backward() opt_G.step() if i % 500 0: print(fEpoch {epoch} Step {i} | D loss: {loss_D.item():.4f} | G loss: {loss_G.item():.4f})这段代码里有三个关键点新手很容易栽在这里。第一更新判别器时用了fake_imgs.detach()意思是切断梯度到生成器的路径。如果不写detachloss_D.backward()会把梯度一路传到生成器参数上然后下一步更新生成器时生成器的梯度里就混入了从判别器损失中带过来的部分训练逻辑直接被搞乱。这属于一定不能漏掉的细节。第二更新生成器时我重新从噪声采样一次生成假图而不是复用上一轮训练判别器用的fake_imgs。因为判别器已经更新过了上一轮的假图不够“新鲜”不能针对当前判别器的弱点给出有效反馈。虽然复用一个batch也能跑但收敛效果通常没那么好。第三生成器的标签是real_labels也就是全1。这与直觉相反但思路是要让生成器生成的假图被判别器判定为真。判别器的标签才是真图1、假图0。如果这两处标签写反了训练会立刻失效生成的图片会变成纯噪声。另外优化器参数里betas(0.5, 0.999)值得单独解释。PyTorch默认的Adam第一动量系数是0.9对GAN的训练来说过高的一阶动量会让梯度更新路径过于平滑容易出现训练振荡。很多GAN实践里会把第一动量降到0.5让更新对瞬时梯度更敏感让对抗双方能更及时地响应对方的变化。3.5 观察生成结果从噪声到数字训练早期大概前几百步输出图像基本是雪花噪声。中期会出现一些模糊的灰块慢慢变成类似数字骨架的形状。后期生成样本逐渐锐化很多数字已经能一眼认出来。这个过程非常直观也是理解GAN有没有在工作的最直接证据。建议每隔几百步拿训练开始前固定的噪声种子生成一批图片保存下来fixed_noise torch.randn(64, z_dim) # 训练循环内部 with torch.no_grad(): sample G(fixed_noise).view(-1, 1, 28, 28) torchvision.utils.save_image(sample, fgan_step_{epoch}_{i}.png)保存图片时要包在with torch.no_grad()里这会关掉自动求导图省内存也防止意外梯度回传。如果你把多张图按顺序拼成GIF还能看到生成器逐步学会构图的过程。用同一组固定噪声的原因很简单不同随机z生成的图没有可比性固定z才能看到同一输入下模型输出是怎么从噪声演化为清晰图片的。在训练快要结束时可以顺手保存一份生成器权重torch.save(G.state_dict(), generator_final.pth) torch.save(D.state_dict(), discriminator_final.pth)这样后续做条件生成、迁移学习时可以直接加载这个权重继续训练不用从零开始。4. 实战中的坑调参记录与排查技巧4.1 判别器太强怎么办训练节奏失衡我第一次跑GAN时最常遇到的情况是判别器loss一路暴跌生成器loss却高得离谱生成的图片始终是一堆噪声。原因通常是判别器学得太快它只看一眼就能分辨真假导致生成器从判别器那里拿到的信息几乎都是“你错了”而且梯度极小没有明确的改进方向。处理办法我会先把判别器学习率从0.0002调到0.0001让判别器慢半步如果还不行再考虑减小判别器的网络容量或者给它加dropout。反过来如果判别器迟迟学不会loss一直徘徊在随机猜的水平可以适当调高判别器容量或减少生成器的更新频率。总之要让双方处在“一个能追上一个不至于被甩开”的节奏里。还有一个附带经验让判别器弱一点生成器反而能有更多探索空间不至于被一个过于严苛的判官吓得只会输出安全但无意义的样本。所以我的原则是宁可让判别器弱一点也别让它强到碾压。判别器弱的时候生成器至少能从有限的反馈里找到改进方向。4.2 模式崩溃生成图片缺少多样性模式崩溃是GAN最典型的问题之一。表现是你采样很多个不同的z生成器却总是输出同一类图片比如MNIST上只生成几个相似的数字。原因是生成器发现了一条“捷径”——只要把输出集中到少数几个能骗过判别器的样本附近就能维持较低的损失没必要去覆盖完整的数据分布。多样性不是判别器的考核指标判别器只关心单张图像不像真的。缓解手段里我实测下来最简单有效的是降低判别器学习率给生成器更多探索空间其次是增大batch size让判别器每轮看到更多不同样本不容易把“局部假样本”当全局标准再就是增大噪声维度给生成器更多不同起点。实践里定期用多张固定z批量采样并观察多样性是判断模式崩溃是否发生的必要步骤只看单张生成图完全发现不了问题。如果想要更系统的手段可以了解minibatch discrimination、unrolled GAN等方案它们的核心思想都是让判别器在判断时参考一批样本的统计信息而不是只看单张图。不过入门阶段从学习率和batch size入手通常已经能解决大部分问题。4.3 别只盯着Loss曲线我见过很多同学第一次训练GAN每隔几十步就打印loss看到曲线不下降就慌了。其实GAN的loss曲线和分类任务的loss曲线完全不是一个逻辑。判别器loss高可能是判别器正在努力适应却还没成功判别器loss低可能是判别器太强导致生成器学不到东西。甚至判别器loss长期稳定在某个接近0.693的水平也不一定代表失败。这个0.693的来历很简单如果判别器对任意输入都输出0.5那么BCELoss单个样本的损失是-log(0.5)大约0.693。所以当判别器loss在这个值附近时说明判别器接近随机猜。可能的原因很多判别器还没学会判断、生成器生成的样本已经足够以假乱真或者训练节奏出了问题。这时候直接看生成图才是最快的判断方式。我的Debug顺序是先看生成样本清不清晰、有没有多样性再看判别器和生成器的loss曲线形态最后才去看梯度、权重等更底层的指标。这个顺序能让你在多数情况下快速定位问题而不是被loss数值带偏。毕竟GAN的目标是生成足够真实的样本loss只是博弈过程的副产物不是最终评价标准。4.4 一份能直接抄的超参数清单我把调参经验整理成一张表适合MNIST这种入门数据集。如果你换成更复杂的数据集数值可以做微调但整体方向不会有太大变化。参数建议值说明优化器AdamGAN实践里的默认选择lr_G0.0002让生成器有足够梯度前进lr_D0.0001 ~ 0.0002偏低一些防止判别器碾压生成器betas(0.5, 0.999)第一动量调低训练更顺batch_size64 或 128太小容易训练振荡z_dim100常用起点隐藏层256 ~ 512先用小网络跑通除了参数我强烈建议养成的调试习惯是一次只改一个变量。如果你同时调了学习率和网络深度效果变差了根本说不清是哪一步造成的。把基线实验跑通后每次只改一项记录生成样本的变化这样的日志才真正有参考价值。我自己的经验是很多所谓“玄学”问题本质上都是“同时改了两个参数但不知道哪个起作用”造成的混乱。5. 从Basic Idea到进阶GAN的应用与变体5.1 条件生成按指令生成原始GAN的生成是不可控的你输入一个随机z并不知道会生成哪个数字。如果想生成指定数字就要引入条件信息这就是条件GANCGAN的思路。做法很简单把类别标签比如one-hot向量和噪声z拼接起来一起输入生成器判别器在判定真假时也会同时接收这个条件信息。生成器必须学会“在给定条件下生成对应类别的图片”否则判别器会直接判定为假。这个改动在代码上只是torch.cat的事情但带来的能力提升非常大。从“生成一张手写数字”变成“生成一张指定的数字七”从“生成一张脸”变成“生成一张戴眼镜的微笑的脸”。很多产品级图像生成任务本质上都是条件生成只是条件从标签换成了文本、姿态、深度图等更复杂的输入。训练条件GAN时有一件值得注意的事条件信息必须同时传给生成器和判别器且格式要统一。如果生成器接外科标签、判别器内嵌标签两边对“条件”的理解就会错位训练很难收敛。我见过不少人在这里栽跟头看起来只是个小改动却直接影响整个训练的有效性。5.2 经典变体快速梳理入门后你会看到一堆GAN变体我简单梳理几条主线帮你在概念上建立坐标系。DCGAN把生成器和判别器换成卷积结构并提出了一批实践诀窍比如用卷积替代全连接、用批归一化、用Adam优化器等让GAN训练更可控。它相当于给GAN换了一套更适合图像任务的骨架。WGAN则针对原始GAN的一个深层问题当生成分布和真实分布几乎不重叠时很多距离指标会梯度消失训练难以推进。WGAN改用Wasserstein距离作为优化目标从数学层面缓解了训练崩溃问题。它的实现也比想象中简单主要改动集中在损失函数和裁剪约束上。CycleGAN专注于图像翻译任务比如马变斑马、素描变彩色照片核心创新是引入循环一致性损失让两个域之间的转换不需要成对样本。StyleGAN更偏高质量人脸生成通过风格控制和层级合成让生成图像的分辨率和可控性都到了新的高度。变体核心改动解决的核心问题DCGAN卷积结构 训练诀窍图像任务效率与训练可控性WGANWasserstein距离替代分布不重叠时的梯度消失CycleGAN循环一致性损失无成对数据的图像翻译StyleGAN风格控制与层级合成高质量、可控的人脸生成我的建议是先看懂Basic Idea然后按DCGAN、WGAN、CGAN的顺序各跑一个小实验。你会在实验里发现所谓变体都只是在“生成器、判别器、损失函数、网络结构”这四个位置做文章。理解了这个框架任何新GAN论文在你眼里都会变得井井有条。5.3 入门后的练手建议如果你已经跑通了上面的最小实现下一步我建议做三件事。第一改网络结构把生成器或判别器多加一层Linear观察效果是否变化。这类改动能帮你建立容量和训练难度的直觉。第二换数据用FashionMNIST替换MNIST图像结构更丰富你会很快感受到生成难度上升带来的训练差异。第三把固定噪声产生的图片保存成序列图做成GIF直观看到模型的学习过程。这个可视化的价值远超想象因为它会直接告诉你生成器是在稳定演化的还是来回乱跳。如果还有精力就尝试改成条件GAN先只在MNIST上控制生成数字类别。我当初做完这个实验后对“生成可控样本”这个概念的理解就彻底落地了。之后再去看文本生成图像、图像翻译这些进阶任务你会发现它们仍然在沿用同一个思路生成器负责输出判别器负责反馈条件信息负责控制。框架没变变的是数据类型、网络结构和损失细节。最后一点个人体会最后分享一点我在实际操作中的体会。我一开始看GAN的公式总以为它和普通神经网络的loss一样训到后面就自然收敛。实际跑起来才发现平衡生成器和判别器更像是在调试一台需要同步磨合的机器。后来我养成了一个习惯先固定生成器调判别器到刚好能分辨但又不完全碾压的状态再固定判别器调生成器到能持续骗过它。每次只改一个变量记录生成样本变化。这个小习惯让我少走了很多弯路。另外如果训练一开始判别器就强得离谱试着把它的学习率调低一点给生成器留出空间。先在小数据集上跑通再上复杂任务这条路走起来会顺畅很多。GAN说难也确实难但说简单也简单——抓住生成器、判别器、目标函数、交替训练这四个锚点剩下的都是可以在实践中慢慢修正的工程细节。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑