【机器学习专栏】4.1 深度学习模型 第一章生成模型与 GAN —— 从判别到生成的飞跃引子“请写出 GAN 的 min-max 优化目标并推导最优判别器下的生成器损失等价于什么散度。”“VAE 中的 ELBO 是如何推导出来的重参数化技巧解决了什么问题”“WGAN 用 Wasserstein 距离替代 JS 散度这解决了什么问题WGAN-GP 的梯度惩罚项为什么比权重裁剪好”“GAN、VAE、扩散模型Diffusion Model三者的核心区别是什么为什么扩散模型现在这么火”这四个问题覆盖了生成模型面试的 80% 的高频考点。从 2023 年以来的大厂面试趋势看Diffusion 的出现并没有让 GAN 和 VAE 退出舞台——面试官反而更喜欢让你对比三者。本文从面试实战出发逐层拆解每个考点背后的原理、推导和追问方向。1.1 GAN生成对抗网络的核心原理1.1.1 从极大似然估计到对抗训练生成模型的目标是学习真实数据分布 p_data(x)。传统方法如极大似然估计直接拟合概率密度但高维数据的密度估计极其困难。GANGenerative Adversarial Network的突破性思路是不显式建模密度函数而是通过博弈让生成分布逼近真实分布。GAN 包含两个网络生成器 G(z; theta_g)从先验噪声 z ~ p(z)通常为标准正态映射到数据空间试图生成以假乱真的样本。判别器 D(x; theta_d)二分类器区分输入来自真实数据还是生成器。两者的关系是零和博弈Zero-sum game目标函数为min_G max_D V(D, G) E_{x~p_data}[log D(x)] E_{z~p_z}[log(1 - D(G(z)))]逐项解读E_{x~p_data}[log D(x)]判别器对真实样本输出概率接近 1该项最大化。E_{z~p_z}[log(1 - D(G(z)))]判别器对生成样本输出概率接近 0即 1-D(G(z)) 接近 1该项最大化生成器希望 D(G(z)) 接近 1即该项最小化。整体是 D 最大化、G 最小化的 min-max 博弈。1.1.2 最优判别器推导手写重点固定生成器 G求最优判别器 D*。将目标函数写成积分形式V(D, G) integral_x p_data(x) log D(x) dx integral_z p_z(z) log(1 - D(G(z))) dz integral_x [ p_data(x) log D(x) p_g(x) log(1 - D(x)) ] dx对每个 x被积函数 f(y) a log y b log(1-y) 在 y 属于 [0,1] 上求最大值df/dy a/y - b/(1-y) 0 y a / (a b)因此最优判别器为D*(x) p_data(x) / (p_data(x) p_g(x))物理意义当 p_data(x) p_g(x)D*(x) 接近 1该区域真实样本占绝对优势判为真当 p_g(x) p_data(x)D*(x) 接近 0两者相等时 D* 0.5即判别器完全无法区分。将 D* 代回 V(G)V(G) integral_x p_data log( p_data / (p_data p_g) ) p_g log( p_g / (p_data p_g) ) dx -log 4 JSD(p_data || p_g)其中 JSD 是 Jensen-Shannon 散度JSD(p || q) 0.5 * KL(p || (pq)/2) 0.5 * KL(q || (pq)/2)结论原始 GAN 等价于最小化 p_data 和 p_g 之间的 JS 散度。1.1.3 为什么原始 GAN 难训练面试核心JS 散度有一个致命缺陷当两个分布的支撑集support不重叠或重叠测度为 0 时JS 散度为常数 log 2梯度为 0。高维空间中p_data 和 p_g 通常位于低维流形上流形假设它们重叠的概率极低。这意味着训练初期生成器输出与真实数据差异巨大两者几乎不重叠 - JS 散度为常数 - 梯度消失判别器太强时可以瞬间区分真假 - JS 散度饱和 - 梯度消失判别器太弱时生成器得不到有效信号 - 训练不稳定这就是 GAN 训练的“两难困境”Dilemma需要判别器足够强来提供梯度信号又不能让判别器太强以至于梯度消失。这种困境催生了后面所有的改进工作。面试追问JS 散度在分布不重叠时为什么梯度为 0答JS 散度由 KL 散度构成而 KL 散度在 p(x) 0 且 q(x) 0 时发散到无穷大在 p(x) 0 且 q(x) 0 时为 0。当支撑集不重叠JS 散度取常数 log 2梯度处处为 0。1.2 从 WGAN 到 WGAN-GP1.2.1 Wasserstein 距离Earth Mover 距离WGAN 的核心贡献是用Wasserstein-1 距离替代 JS 散度W(p_r, p_g) inf_{gamma in Pi(p_r, p_g)} E_{(x,y)~gamma}[ ||x - y|| ]其中 Pi(p_r, p_g) 是所有联合分布 gamma(x,y) 的集合其边缘分布分别为 p_r 和 p_g。直观理解Wasserstein 距离衡量的是将 p_g 的土堆搬运到 p_r 的最小工作量。Wasserstein 距离相比 JS 散度的三大优势性质JS 散度Wasserstein 距离分布不重叠时常数梯度为 0仍能反映距离梯度不为 0连续性不连续跳变处处连续、几乎处处可微收敛指示无法指示收敛进程值持续下降可用作 loss 指标直觉例子假设真实分布 p_r 集中在 (0,0)生成分布 p_g 集中在 (theta, 0)。JS 散度theta ! 0 时为 log 2theta 0 时为 0不连续。Wasserstein 距离|theta|连续且梯度处处为 1提供稳定梯度。1.2.2 WGAN 的 Kantorovich-Rubinstein 对偶直接计算 inf_{gamma} 不可行。通过 Kantorovich-Rubinstein 对偶Wasserstein 距离等价为W(p_r, p_g) (1/K) * sup_{||f||_L K} E_{x~p_r}[f(x)] - E_{x~p_g}[f(x)]其中 f 是K-Lipschitz 函数满足 |f(x1) - f(x2)| K * ||x1 - x2||。WGAN 用判别器称为评论家 Criticf_w 来近似这个上确界目标函数改为min_G max_{D: ||D||_L 1} E_{x~p_r}[D(x)] - E_{x~p_g}[D(x)]注意两项之差没有 log。训练 Critic 最大化真实样本得分、最小化生成样本得分生成器最小化两者的差值。1.2.3 权重裁剪的问题原始 WGAN 通过权重裁剪Weight Clipping强制 Lipschitz 约束每次更新后将 Critic 的权重截断到 [-c, c]。权重裁剪的三个问题容量浪费大部分权重被推到边界值 c 或 -c网络表达能力下降。梯度消失或爆炸c 调大导致梯度爆炸c 调小导致梯度消失。偏好简单函数Critic 被迫学习简单的二值函数无法捕捉复杂分布。1.2.4 WGAN-GP梯度惩罚Gradient PenaltyWGAN-GP 用梯度惩罚项替代权重裁剪L E_{x~p_g}[D(x)] - E_{x~p_r}[D(x)] lambda * E_{x~p_x}[( ||grad_x D(x)||_2 - 1)^2]其中 x 从真实样本 x_r 和生成样本 x_g 的连线上均匀采样x_hat epsilon * x_r (1 - epsilon) * x_g, epsilon ~ U[0, 1]为什么在连线上采样最优 1-Lipschitz 函数在 p_r 和 p_g 之间的区域梯度范数为 1连线采样能高效覆盖该区域。# WGAN-GP 梯度惩罚实现面试手写高频defgradient_penalty(critic,real,fake,lambda_gp10.0):计算 WGAN-GP 梯度惩罚项batch_sizereal.size(0)epsilontorch.rand(batch_size,1,1,1).to(real.device)# 在真实和生成样本之间线性插值interpolatedepsilon*real(1-epsilon)*fake interpolated.requires_grad_(True)# 计算 Critic 对插值样本的输出critic_outputcritic(interpolated)# 计算梯度gradtorch.autograd.grad(outputscritic_output,inputsinterpolated,grad_outputstorch.ones_like(critic_output),create_graphTrue,retain_graphTrue,)[0]# 计算梯度范数并惩罚偏离 1 的部分grad_normgrad.view(batch_size,-1).norm(2,dim1)gplambda_gp*((grad_norm-1)**2).mean()returngpWGAN-GP 训练技巧不使用基于动量的优化器如 Adam推荐 RMSProp 或 SGD实际中 Adam 调好参数也可用Critic 迭代次数通常多于 Generator每更新一次 G更新 5 次 Criticlambda 通常取 10算法约束方式训练稳定性生成质量计算开销Original GAN无差中等低WGAN权重裁剪中等好低WGAN-GP梯度惩罚好好中需二阶导Spectral Norm谱归一化好好中1.3 StyleGAN可控生成的巅峰StyleGANKarras et al., 2019是 GAN 在图像生成领域的重要里程碑基于 Progressive GAN 的架构做了系统性改进。1.3.1 核心创新1. 映射网络Mapping Network与传统生成器的对比组件传统 GANStyleGAN输入处理z 直接输入第一层z - ww 经 AdaIN 调制各层中间潜在空间z 空间正态分布w 空间解耦更好随机性来源仅 zz 独立噪声用于细节2. AdaINAdaptive Instance NormalizationStyleGAN 的核心操作AdaIN(x_i, y) y_{s,i} * (x_i - mu(x_i)) / sigma(x_i) y_{b,i}其中 y_s, y_b 由 w 经仿射变换生成控制特征的尺度和偏置。每个分辨率层都有独立的 w 调制实现分层风格控制粗粒度约等于姿态、细粒度约等于纹理。3. 随机风格注入在每一层引入独立的高斯噪声经学到的缩放因子控制随机细节头发纹理、毛孔等。噪声不影响整体结构只影响随机外观。1.3.2 StyleGAN 风格混合与解耦StyleGAN 支持两种重要的控制手段风格混合Style Mixing使用两个不同的 ww1, w2在不同分辨率阶段生成图像。低分辨率层用 w1 控制粗粒度特征姿态、脸型高分辨率层用 w2 控制细粒度特征颜色、纹理。这种设计证明了 w 空间具有层级解耦特性。截断技巧Truncation Trick在推理时将 w 拉向均值 w_bar所有 w 的平均w w_bar psi * (w - w_bar)psi 属于 [0, 1]控制保真度-多样性trade-offpsi 越小生成质量越高但多样性越低典型值0.7 是好的平衡点1.3.3 StyleGAN2 改进StyleGAN2 主要修复了 StyleGAN1 的水滴伪影droplet artifact问题将 AdaIN 中的 Instance Normalization 替换为权重解调Weight Demodulation改进了路径长度正则化Path Length Regularization使 w 空间更加平滑消除了生成器中的渐进式增长改用残差连接面试追问z 空间和 w 空间有什么区别答z 空间服从高斯分布采样概率高不等于对应的图像质量好且 z 空间各维度耦合严重。w 空间通过 8 层 MLP 映射后分布更解耦disentangled线性插值效果更好。在 w 空间中做算术运算如戴眼镜的人-不戴眼镜的人不戴眼镜的女人戴眼镜的女人更有效。1.4 VAE变分自编码器1.4.1 从自编码器到变分自编码器自编码器Autoencoder由编码器 q_phi(z|x) 和解码器 p_theta(x|z) 组成通过重构损失训练。但标准自编码器的潜在空间 z 没有正则化——输入空间中的微小不连续会导致解码出无意义的结果。VAE 的核心思想是不将 x 编码为单个点 z而是编码为一个分布 q_phi(z|x)并强制该分布接近标准正态先验 p(z) N(0, I)。1.4.2 ELBO 推导手写重点VAE 通过最大化证据下界Evidence Lower Bound, ELBO来近似最大化对数似然 log p_theta(x)log p_theta(x) KL(q_phi(z|x) || p_theta(z|x)) L_ELBO L_ELBO E_{z~q_phi(z|x)}[log p_theta(x|z)] - KL(q_phi(z|x) || p(z))完整推导过程面试高频log p(x) log integral_z p(x, z) dz log integral_z q(z|x) * p(x, z) / q(z|x) dz log E_{z~q(z|x)}[ p(x, z) / q(z|x) ] E_{z~q(z|x)}[ log p(x, z) / q(z|x) ] (Jensen 不等式) E_{z~q(z|x)}[ log p(x|z) * p(z) / q(z|x) ] E_{z~q(z|x)}[log p(x|z)] E_{z~q(z|x)}[log p(z) / q(z|x)] E_{z~q(z|x)}[log p(x|z)] - KL(q(z|x) || p(z))两项的直观理解项含义作用E_{z~q}[log p(xz)]重构损失KL(q(zx)VAE 的损失函数就是负 ELBOL_VAE -E_{z~q_phi(z|x)}[log p_theta(x|z)] KL(q_phi(z|x) || p(z))1.4.3 重参数化技巧Reparameterization Trick问题z 是从 q_phi(z|x) N(mu_phi(x), sigma_phi^2(x)) 中采样的采样操作不可微梯度无法通过 z 回传到 phi。解决方案将随机性从模型参数中分离出来。z mu(x) sigma(x) * epsilon, epsilon ~ N(0, I)现在梯度可以通过 mu 和 sigma 回传到编码器而 epsilon 的随机性在反向传播时被视为常数。# VAE 重参数化实现面试手写高频classVAE(nn.Module):def__init__(self,input_dim784,latent_dim128):super().__init__()# 编码器输出均值和 log 方差self.encodernn.Sequential(nn.Linear(input_dim,512),nn.ReLU(),nn.Linear(512,256),nn.ReLU(),)self.mu_layernn.Linear(256,latent_dim)self.logvar_layernn.Linear(256,latent_dim)# 解码器self.decodernn.Sequential(nn.Linear(latent_dim,256),nn.ReLU(),nn.Linear(256,512),nn.ReLU(),nn.Linear(512,input_dim),nn.Sigmoid(),)defreparameterize(self,mu,logvar):重参数化: z mu sigma * epsstdtorch.exp(0.5*logvar)# logvar log(sigma^2)epstorch.randn_like(std)# eps ~ N(0, 1)returnmueps*stddefforward(self,x):hself.encoder(x)muself.mu_layer(h)logvarself.logvar_layer(h)zself.reparameterize(mu,logvar)reconself.decoder(z)returnrecon,mu,logvardefvae_loss(recon_x,x,mu,logvar):VAE 损失 重构损失 KL 散度# 重构损失二值交叉熵BCEF.binary_cross_entropy(recon_x,x,reductionsum)# KL 散度: KL(q(z|x) || N(0, 1))# 闭式解: -0.5 * sum(1 log(sigma^2) - mu^2 - sigma^2)KLD-0.5*torch.sum(1logvar-mu.pow(2)-logvar.exp())returnBCEKLD1.4.4 VAE 的局限问题原因表现生成模糊最大化 ELBO 等价于在数据点间平均边缘模糊、细节丢失先验不匹配标准正态先验过于简单潜在空间过度正则化后验坍塌KL 项占主导q(zx) 接近 p(z)后验坍塌Posterior Collapse是 VAE 训练中的经典问题当解码器 p_theta(x|z) 足够强如自回归解码器时模型学会忽略 zq(z|x) 退化为 p(z)。缓解方法包括 KL 退火KL Annealing、Free Bits 技巧、beta-VAE加权 KL 项等。1.5 GAN vs VAE vs Diffusion 全面对比扩散模型Diffusion Model是 2020 年以来生成模型领域最大的突破在图像质量上全面超越了 GAN同时保持了 VAE 的训练稳定性。1.5.1 核心原理对比维度GANVAEDiffusion Model核心思路对抗博弈变分推断逐步去噪训练目标min-max 博弈ELBO 最大化噪声预测L2 loss损失函数JS 散度 / WassersteinKL 散度 重构训练稳定性差模式崩溃、梯度消失稳定稳定生成质量高但多样性受限模糊高SOTA多样性低mode collapse高高推断速度快单次前向快单次前向慢约50-1000步似然估计无有ELBO有ELBO 变体潜在空间有z 或 w有z无隐式可控生成难需额外条件中等容易Classifier-free guidance1.5.2 为什么 Diffusion 现在这么火DDPMDenoising Diffusion Probabilistic Models, Ho et al. 2020证明扩散模型可以生成高质量图像但速度慢。后续改进DDIMSong et al., 2021将采样步数从 1000 降到 50-100质量基本不损失。Latent DiffusionRombach et al., 2022 / Stable Diffusion在 VAE 的潜在空间而非像素空间做扩散大幅降低计算量。Classifier-Free GuidanceHo Salimans, 2022通过调整条件强度在多样性和质量间平滑调节。Consistency ModelsSong et al., 2023一步生成速度媲美 GAN。Diffusion 的核心优势训练目标简单纯粹 L2 回归无需对抗训练不依赖判别器不存在博弈均衡问题可通过 classifier-free guidance 精细控制生成方向潜在空间连续且语义丰富适合做编辑1.5.3 关键指标对比# GAN 与 Diffusion 的核心差异在损失函数上对比# 面试中常让分析为什么 Diffusion 训练更稳定# GAN 损失以 WGAN-GP 为例defgan_loss(generator,discriminator,real,z):fakegenerator(z)# 判别器损失最大化 real_score - fake_scored_loss-(discriminator(real).mean()-discriminator(fake).mean())# 生成器损失最小化 discriminator 对假图的得分g_loss-discriminator(fake).mean()returnd_loss,g_loss# VAE 损失defvae_loss(x,recon,mu,logvar):recon_lossF.binary_cross_entropy(recon,x,reductionsum)kl_loss-0.5*torch.sum(1logvar-mu**2-logvar.exp())returnrecon_losskl_loss# Diffusion 损失以 DDPM 为例defdiffusion_loss(model,x_0,noise_scheduler):DDPM 训练损失预测噪声batch_sizex_0.size(0)# 随机采样时间步 tttorch.randint(0,noise_scheduler.num_train_timesteps,(batch_size,),devicex_0.device)# 采样真实噪声noisetorch.randn_like(x_0)# 对原图加噪x_t sqrt(alpha_bar_t) * x_0 sqrt(1 - alpha_bar_t) * noisex_tnoise_scheduler.add_noise(x_0,noise,t)# 模型预测添加的噪声noise_predmodel(x_t,t)# 简单 L2 损失lossF.mse_loss(noise_pred,noise)returnloss三种损失函数的设计差异反映了它们根本哲学的不同GAN间接对抗损失函数本身不保证收敛VAE概率推断使用 KL 散度正则化潜在空间Diffusion直接回归训练过程等价于优化 ELBO 的一个变体1.6 面试官追问链高频 QAQ1原始 GAN 的生成器损失为什么有两种形式答原始 GAN 论文提出了两种生成器损失min_G E_z[log(1 - D(G(z)))]Minimax早期梯度饱和严重max_G E_z[log D(G(z))]Non-saturating梯度不饱和实践中更常用Non-saturating loss 在 D(G(z)) 接近 0 时梯度仍然很大鼓励生成器快速逃离低质量区域。Q2Mode Collapse 的本质是什么WGAN 完全解决了 mode collapse 吗答Mode collapse 的本质是生成器发现了判别器的盲区并反复利用。Wasserstein 距离的连续梯度让生成器难以为作弊——小幅度的分布偏移会立即降低 Critic 得分。但 WGAN 并未完全解决 mode collapse如果生成器收敛到一个局部最优仍然可能只覆盖部分模式实践中 WGAN 的 mode coverage 远好于原始 GAN但不如 VAE结合 mini-batch discrimination、unrolled GAN、MAD-GAN 等方法进一步缓解Q3VAE 生成的图像为什么模糊答三个层面ELBO 的非紧致性log p(x) - ELBO KL(q(z|x) || p(z|x)) 0下界与真实似然之间的 gap 就是后验近似误差。L2 重构损失偏好平均当分布有多个可行输出时如图像边缘的抖动L2 损失的最优解是条件期望即所有可能的模糊平均。先验限制标准正态先验过于简单q(z|x) 被迫接近简单分布无法表达复杂的后验。改进方案VQ-VAE离散潜在空间、NVAE层次化 VAE、VD-VAEvery deep VAE。Q4为什么 Diffusion 模型推理慢有哪些加速方法答DDPM 需要在推理时从 x_T 逐步去噪到 x_0每一步都需要一次网络前向。1000 步乘以每步推理 比 GAN 慢 1000 倍。加速方法DDIM确定性采样相同的训练用非马尔可夫过程将步数降到 50DPM-Solver / DPMSolver用 ODE 求解器的数值方法加速约10-20步高质量生成Consistency Models、Rectified Flow一步生成速度媲美 GANLatent Diffusion在低维潜在空间做扩散而非常见的高维像素空间每步计算量大幅降低Q5手写 GAN 训练循环面试高频代码题# GAN 训练循环模板面试高频deftrain_gan(generator,discriminator,dataloader,n_epochs,latent_dim,device,n_critic5):WGAN-GP 训练循环g_optimoptim.Adam(generator.parameters(),lr1e-4,betas(0.5,0.9))d_optimoptim.Adam(discriminator.parameters(),lr1e-4,betas(0.5,0.9))forepochinrange(n_epochs):fori,(real_imgs,_)inenumerate(dataloader):real_imgsreal_imgs.to(device)batch_sizereal_imgs.size(0)# ---- 训练判别器Criticn_critic 次 ----for_inrange(n_critic):ztorch.randn(batch_size,latent_dim,devicedevice)fake_imgsgenerator(z).detach()d_loss_real-discriminator(real_imgs).mean()d_loss_fakediscriminator(fake_imgs).mean()gpgradient_penalty(discriminator,real_imgs,fake_imgs)d_lossd_loss_reald_loss_fakegp d_optim.zero_grad()d_loss.backward()d_optim.step()# ---- 训练生成器 1 次 ----ztorch.randn(batch_size,latent_dim,devicedevice)fake_imgsgenerator(z)g_loss-discriminator(fake_imgs).mean()g_optim.zero_grad()g_loss.backward()g_optim.step()第二章CNN 经典架构演进 —— 从 AlexNet 到 EfficientNet引子图像分类的发展史“ResNet 为什么能训练这么深残差连接解决了什么问题”“Inception 模块为什么用 1×1 卷积降维”“MobileNet 怎么让模型在手机上跑起来的”“EfficientNet 怎么同时缩放深度/宽度/分辨率”2.1 经典模型对比模型年份ImageNet Top-1 错误率参数量核心创新LeNet-51998-60K第一个 CNNAlexNet201216.4%60MReLU Dropout GPU 并行VGG-1620147.3%138M小卷积核 3×3 堆叠GoogLeNet20146.7%6.8MInception 模块ResNet-5020155.3%25.6M残差连接DenseNet-12120175.0%8M密集连接MobileNetV220186.4%3.4M深度可分离卷积EfficientNet-B720192.7%66M复合缩放2.2 ResNet 残差连接classResidualBlock(nn.Module):def__init__(self,in_channels,out_channels,stride1):super().__init__()self.conv1nn.Conv2d(in_channels,out_channels,3,stride,padding1,biasFalse)self.bn1nn.BatchNorm2d(out_channels)self.conv2nn.Conv2d(out_channels,out_channels,3,padding1,biasFalse)self.bn2nn.BatchNorm2d(out_channels)self.shortcutnn.Sequential()ifstride!1orin_channels!out_channels:self.shortcutnn.Sequential(nn.Conv2d(in_channels,out_channels,1,stride,biasFalse),nn.BatchNorm2d(out_channels))defforward(self,x):outF.relu(self.bn1(self.conv1(x)))outself.bn2(self.conv2(out))outself.shortcut(x)# 残差连接returnF.relu(out)残差解决了什么不是梯度消失而是退化degradation——深层网络训练误差比浅层更高。残差连接 y x F(x) 让网络至少学到恒等映射不会比浅层差。