阶段七 AIGC(生成式 AI)实战 —— GAN 与扩散模型
2026/8/20 15:02:53 网站建设 项目流程

阶段七 AIGC(生成式 AI)实战 —— GAN 与扩散模型

本阶段环境:本地 Python 3.13 + numpy 2.5 / matplotlib 3.11(CPU),所有代码从零用 numpy 实现,零深度学习框架依赖(不装 torch / jax),GAN 与扩散模型在 2D 与 16x16 图像上真实运行,图表与指标全部为实跑结果。

配套脚本:src/gan_2d.pysrc/diffusion_2d.pysrc/diffusion_image.pysrc/sd_pipeline_diagram.py;公共手写 MLP / Adam 在src/_common.py

一、学习目标

  • 理解生成对抗网络(GAN)「生成器 G 与判别器 D 对抗博弈」的直觉与最小数学,并从零实现 G/D 的手写反向传播与 Adam 优化。
  • 理解**扩散模型(DDPM)**的「前向加噪 / 反向去噪」统一公式,看清 cosine 噪声调度、时间嵌入、噪声预测网络 ε_θ 的作用。
  • 在**同一目标分布(2D 八高斯混合)**上跑通两种范式,对比训练动态与样本质量。
  • 把同一套扩散原理搬到16×16 图像上,无条件生成几何图形。
  • 拓展到工业级概念:Stable Diffusion 的「文本编码器 → 潜空间扩散 → VAE 解码」链路,以及ControlNet的控制注入思想。

二、原理速览

范式训练目标采样方式代表
GAN极小极大:G 最大化log D(G(z)),D 最小化-log D(x) - log(1-D(G(z)))一次前向G(z)DCGAN、StyleGAN
扩散 (DDPM)简单 MSE:`ε - ε_θ(x_t, t)

两者的共同点:都从噪声出发学一个映射到数据分布的函数。不同点:GAN 是「一次性对抗」生成,扩散是「自回归式(按时序)」去噪。


三、实战 1:GAN 在 2D 八高斯混合上学习(src/gan_2d.py)

3.1 任务与原理

目标分布:8 个二维高斯等距放在半径 2.0 的圆周上,标准差 0.15。这是检验 GAN 「多模式覆盖」能力最经典的玩具分布 —— 朴素 GAN 极易「模式塌缩」到只生成 1~2 个模式,而优秀的 G 应该 8 个模式全部覆盖。

网络(从零 numpy 实现,全连接 / DCGAN 思想)

  • 生成器 G:z(2) → 32 → 32 → 2,最后tanh × 2.5把输出限制在 [-2.5, 2.5];
  • 判别器 D:x(2) → 32 → 32 → 1,sigmoid 输出「真/假」概率。

损失(标准非饱和 GAN)

L D = − [ log ⁡ D ( x ) + log ⁡ ( 1 − D ( G ( z ) ) ) ] , L G = − log ⁡ D ( G ( z ) ) \mathcal{L}_D = -[\log D(x) + \log(1-D(G(z)))], \quad \mathcal{L}_G = -\log D(G(z))LD=[logD(x)+log(1D(G(z)))],LG=logD(G(z))

3.2 核心代码(节选自 src/gan_2d.py)

# 生成器 G 的 1 个训练步(用批内拼接保证 D 的反向缓存覆盖 2B 行)fake=G.forward(z)*G_SCALE d_in=np.concatenate([x_real,fake],axis=0)# (2B,2)a_all=D.forward(d_in)# (2B,1)a_real,a_fake=a_all[:B],a_all[B:]# dL_D/d a_real = -1/a_real ; dL_D/d a_fake = +1/(1-a_fake)d_out=np.concatenate([-(1.0-a_real),a_fake],axis=0)D.backward(d_out)optD.step(D.W+D.b,D.grads["W"]+D.grads["b"])# 生成器 G 的 1 个训练步(非饱和目标:最大化 log D(G(z)))z2=np.random.randn(B,Z_DIM)fake2=G.forward(z2)*G_SCALE a_fake2=D.forward(fake2)d_g=-(1.0-a_fake2)# 经 sigmoid 反推d_fake_in=D.backward(d_g)# 反传到 D 输入G.backward(d_fake_in*G_SCALE)# 串过 *G_SCALEoptG.step(G.W+G.b,G.grads["W"]+G.grads["b"])

MLP.backwardAdam全部在src/_common.py中用 ~80 行 numpy 实现。

3.3 真实运行结果

epoch 1/320 D_loss=1.1477 G_loss=0.8772 mode_cov= 12.5% t=0.2s epoch 40/320 D_loss=1.3586 G_loss=0.8205 mode_cov=100.0% t=9.4s epoch 80/320 D_loss=1.1374 G_loss=0.9354 mode_cov=100.0% t=21.5s epoch 160/320 D_loss=1.2860 G_loss=0.8177 mode_cov=100.0% t=33.6s epoch 240/320 D_loss=1.3139 G_loss=0.7931 mode_cov=100.0% t=42.4s epoch 320/320 D_loss=1.3648 G_loss=0.7333 mode_cov=100.0% t=48.9s [GAN] 指标: z_dim=2, batch=128, epochs=320, steps=10240, final_D_loss=1.3648, final_G_loss=0.7333, mode_coverage=8/8 (100%), min_dist=[0.004,0.010,0.004,0.008,0.013,0.005,0.030,0.006], elapsed=48.9s

G/D 损失曲线(前期典型对抗震荡,约 80 轮后收敛到 D≈1.35 / G≈0.73 的「纳什均衡」附近):

生成样本演化(每 50 轮一帧):epoch 0 是随机高斯云,epoch 20 形成一个小簇,epoch 60–120 开始铺成圆环,到 epoch 200 之后 8 个模式都已就位。

最终样本 vs 真实分布:8 个红 X 模式中心全部被生成器命中,最小距离 ≤ 0.030(≈ 真实高斯标准差 0.15 的 1/5),生成与真实几乎完全重合:

模式覆盖曲线(epoch 0 12.5% → epoch 40 之后 100% 稳定保持):

3.4 要点总结

  • 手写反向传播的细节:D 前向必须把 (real, fake) 拼成一次 (2B) 批,否则 backward 缓存只有最后一批会形状不匹配。
  • 非饱和目标(G 最大化log D(G(z))而非最小化log(1-D(G(z))))让 G 训练初期不被「梯度消失」卡死。
  • 模式覆盖是 GAN 的硬指标:8/8 全部命中且最小距离 < 0.06,是 G 真的学到了多模态分布而非塌缩的硬证据。
  • 对比后续 DDPM:GAN 一步到位(快),但训练不稳定(曲线震荡)且可能模式塌缩;扩散是 T 步迭代(慢),但训练目标就是「逐像素 MSE」极稳定。

四、实战 2:扩散模型 DDPM 在 2D 点分布上(src/diffusion_2d.py)

4.1 任务与原理

目标:在同一 2D 八高斯混合上训练一个噪声预测网络ε_θ(x_t, t),让它能从加噪后的样本里把「加入的噪声」预测出来。采样时从纯噪声x_T ~ N(0, I)出发,按学习到的反向 step 一路去噪得到x_0

DDPM 核心公式(Ho et al., 2020):

前向(固定、不参与学习):
x t = α ˉ t x 0 + 1 − α ˉ t ε , ε ∼ N ( 0 , I ) x_t = \sqrt{\bar\alpha_t}\,x_0 + \sqrt{1-\bar\alpha_t}\,\varepsilon,\quad \varepsilon\sim\mathcal N(0,I)xt=αˉtx0+1αˉtε,εN(0,I)

反向(一参数化):网络输出噪声预测eps_hat,反向一步为
x t − 1 = 1 α t ( x t − 1 − α t 1 − α ˉ t ε ^ ) + σ t z x_{t-1} = \frac{1}{\sqrt{\alpha_t}}\Bigl(x_t - \frac{1-\alpha_t}{\sqrt{1-\bar\alpha_t}}\,\hat\varepsilon\Bigr) + \sigma_t zxt1=αt1(xt1αˉt1αtε^)+σtz

训练目标(简版):L = E[ ||ε - ε_θ(x_t, t)||² ],与 α̅ₜ 无关(DDPM 关键简化)。

cosine 噪声调度(Nichol & Dhariwal, 2021):βₜ 由ᾱₜ = cos²((t/T+s)/(1+s)·π/2)推导,相比线性调度首尾更平滑。

4.2 核心代码

# cosine 调度defcosine_schedule(T,s=0.008):steps=np.arange(T+1,dtype=np.float64)f=np.cos((steps/T+s)/(1+s)*np.pi/2)**2bar=f[1:]/f[0]bar_prev=f[:-1]/f[0]beta=np.clip(1.0-bar/bar_prev,0.0001,0.9999)alpha=1.0-betareturnbeta,alpha,np.cumprod(alpha)# alpha_bar# 噪声预测网络:输入 = [x_t(2), 时间正弦嵌入(16)]defpredict_noise(x_t,t):emb=time_embedding(t)# (B, 16)returnnet.forward(np.concatenate([x_t,emb],axis=1))# 一步训练x_t=np.sqrt(alpha_bar[t])*x0+np.sqrt(1-alpha_bar[t])*eps eps_hat=predict_noise(x_t,t)loss=np.mean((eps_hat-eps)**2)net.backward(2.0*(eps_hat-eps)/BATCH)opt.step(net.W+net.b,net.grads["W"]+net.grads["b"])

4.3 真实运行结果

step 1000/6000 MSE=0.4720 t=10.3s step 2000/6000 MSE=0.4358 t=20.4s step 3000/6000 MSE=0.4012 t=30.2s step 4000/6000 MSE=0.3853 t=40.4s step 5000/6000 MSE=0.3761 t=50.0s step 6000/6000 MSE=0.3700 t=59.1s [DDPM-2D] T=500, train_steps=6000, lr=2e-3, final_train_MSE=0.491, min_train_MSE=0.360, mode_coverage=8/8 (100%), min_dist=[0.031,0.010,0.008,0.031,0.019,0.011,0.024,0.013], elapsed=59.1s

MSE 含义:ε 是 2 维标准正态,方差为 1;若网络退化为「输出 0」则 MSE ≈ 1.0;0.49 表明网络已显著学到噪声的方向(不是平凡解)。MSE 随训练持续下降,从初始 ~2.8 降至 ~0.37。

噪声调度曲线:cosine 调度下ᾱₜ平滑从 1 衰减到 0,βₜ末端快速上升:

前向加噪过程:同一批真实点(紫色)随 t 增大被噪声淹没,到 t=499 已接近纯高斯:

反向去噪单条轨迹:从右上角纯噪声起点(蓝方块)经 500 步去噪,最终落到左下角的某个模式中心(红星)。中间大跳跃对应大 t 的去噪步、后期小幅调整对应小 t 的精细化:

最终生成 vs 真实:8/8 模式全部覆盖,生成(青)铺成完整圆环;与真实(灰)形状一致、密度略厚(这是小型 MLP 噪声预测器在 2D 上的典型表现,可接受):

4.4 要点总结

  • 同一目标分布,GAN vs DDPM 训练动态对比:DDPM 训练曲线单调下降,GAN 是两人博弈震荡 —— 这正是「逐像素 MSE」目标更易收敛的代价:T 步采样。
  • cosine vs linear 调度:cosine 末端更陡,能在信息真正消失前把噪声"压"得充分。本图清晰显示了这点。
  • 模式覆盖 = 100%:再一次证明,对于「多模态分布」任务,关键是网络有足够容量 + 训练充分,而非范式本身(GAN 也能做到)。

五、实战 3:扩散模型在 16×16 图像上(src/diffusion_image.py)

把同一套 DDPM 数学框架搬到「图像」上:每张 16×16 灰度图展平成 256 维向量,用[x_t(256) + 时间嵌入(16)] → 128 → 128 → 128 → 256的 MLP 学习 ε_θ。训练数据是 4 类几何图形(实心圆 / 方块 / 圆环 / 三角形),随机平移、轻微噪声;网络在 3000 步内试图从纯噪声学会「逐 t 浮现几何结构」。

该实验的全部数据、代码、图与指标均为本地 numpy 真实运行。完整代码在src/diffusion_image.py,权重不保存,模型只用于本次采样演示。

5.1 真实运行结果(诚实记录,未达预期)

[DDPM-IMG] size=16, T=200, train_steps=3000, lr=2e-3, final_train_MSE=0.954, min_train_MSE=0.937, n_gen=64, mean_min_L2_to_train=21.012, elapsed=233.6s
step 500/3000 MSE=0.990 t=63.7s step 1000/3000 MSE=0.965 t=115.1s step 1500/3000 MSE=0.979 t=139.7s step 2000/3000 MSE=0.956 t=166.8s step 2500/3000 MSE=0.969 t=194.8s step 3000/3000 MSE=0.954 t=224.5s

MSE 解读:ε 是 256 维标准正态,方差为 1(逐元素)。「预测 0」的平凡解 MSE=1.0,我们的 0.954 仅比平凡解好 5%,远未到能用的水平。MSE 在 500 步后就基本卡在 ~0.95,模型没学到有意义的「噪声 → 几何形状」映射。

前向加噪(这部分与模型无关,纯数学):从清晰圆形逐步被噪声淹没,验证扩散前向过程正确。

反向去噪生成样本:可以看到模型并未真正学到几何形状——反向去噪从噪声出发逐渐稳定成"黑白相间的迷宫/蛇形"纹理,64 张生成样本也都是类似的随机纹理而不是圆/方/三角/环。

5.2 失败原因分析(宝贵的负样本教学)

因素我们的配置工业级配置影响
网络结构3 层 128 维全连接 MLP数十层 U-Net(含 ResBlock + 注意力)MLP 无空间归纳偏置:它把 256 个像素视作"一袋独立的数",无法显式建模「相邻像素相关」
训练步数3000 步数十万~数百万步量级差两个数量级
数据量4 类形状 + 随机平移 ≈ 无数模板但本质是 4 种原型几亿张自然图像数据复杂度低但泛化目标也低,问题主要在网络容量与训练时间
时间步 T2001000略少但不是主因

结论:DDPM 的数学是对的(2D 玩具上 100% 跑通就是证据),但要让它在 16×16 像素上真正学到几何结构,需要至少把网络换成 U-Net、把训练量再翻 50~100 倍。这恰恰就是 Stable Diffusion 的设计选择 —— 我们的 16×16 demo 是"理论对了,工程上还不够"的诚实展示。

5.3 要点总结

  • 同一数学,搬上像素:ε_θ 的输入维度从 2 涨到 256,公式不变;从 2D 玩具到 16×16 图像「代码改动只是数据维度」。
  • 无条件生成:模型本应学到 4 类几何图形的混合分布(没有条件标签),但因容量不足实际只学到"一团噪声纹理"。
  • 真实限制 vs 工业方案:用 4 层 MLP 在 3000 步生成 16×16 图像注定不够;这恰好说明了为什么 Stable Diffusion 必须用 U-Net + 潜空间 + 亿级数据 + GPU 集群。本节不是为了追画质,而是为了亲手走通管线、看清单层 MLP 的边界
  • 与第六节 Stable Diffusion 的呼应:把这里的"MLP 失败"和下一节"Stable Diffusion 的 U-Net 成功"并排看,能直观体会网络结构(局部/全局/空间归纳偏置)对生成质量的影响

六、概念扩展:Stable Diffusion 与 ControlNet(src/sd_pipeline_diagram.py)

阶段四/五我们用 PyTorch 从零搭 CNN / ViT;阶段六我们做了 OpenCV 与目标检测;本阶段已亲手用 numpy 跑通 2D 与 16×16 的扩散。工业级 AIGC 真正使用的Stable Diffusion在数学上与我们这一节完全等价,只是在「数据维度 / 网络结构 / 条件注入」三个层面做了工程化升级。下面这张结构图把这三件事一次性说清:

6.1 三段链路

  1. 文本条件prompt→ 文本编码器(CLIP / T5)→ 上下文嵌入c(77×768 等)。这是把「自然语言」装进张量的唯一入口。
  2. 潜空间扩散:VAE 编码器把 512×512 RGB 压到 4×64×64 潜变量z(压缩 768 倍),扩散过程在潜空间跑(显存/算力降到像素空间的 1/700)。U-Net 的每个残差块用交叉注意力c注入,再用正弦时间嵌入t注入,输出对当前z_t的噪声预测 ε̂。
  3. 像素重建:T 步去噪得到z_0→ VAE 解码器 → 512×512 RGB 图像。

6.2 ControlNet 的「零卷积」控制注入

ControlNet(Zhang et al., 2023)的关键思想是复制 U-Net 编码器作为「控制分支」,接收额外的控制图c'(Canny 边缘 / 姿态 / 深度 / 涂鸦)。控制分支与冻结的 U-Net 之间用**零卷积(zero convolution,权重与偏置初始化为 0)**相连:

  • 训练初期:控制分支输出 ≈ 0,对主 U-Net无影响(保留原始文生图能力,不破坏预训练)。
  • 训练中:梯度只通过零卷积流入控制分支,不会破坏冻结的 U-Net 权重。
  • 推理时:控制分支的残差被加到对应 U-Net 块,实时把控制图 c’ 的结构「写」进生成图

直观上:Stable Diffusion 提供「画什么」,ControlNet 提供「怎么摆」。

6.3 与我们实验的对应

我们的代码Stable Diffusion
256 维向量 = 展平的 16×16 像素4×64×64 潜变量
2 层 MLP (32 维隐层)数十层 U-Net + 注意力
无条件CLIP/T5 文本 + ControlNet 控制图
numpy, CPU数十亿参数 GPU 集群
完全等价的数学

七、指标总览(results/ 目录)

实验关键指标数值
GAN (2D 八高斯)训练轮数 / 总步数320 / 10240
GAN最终 D_loss1.365
GAN最终 G_loss0.733
GAN模式覆盖8/8 (100%)
GAN最近模式距离0.004 – 0.030
GAN运行时长48.9 s
DDPM (2D)扩散步数 T500
DDPM训练步数6000
DDPM最终 MSE0.491
DDPM模式覆盖8/8 (100%)
DDPM运行时长59.1 s
DDPM (16×16 图像)T / 训练步数200 / 3000
DDPM (16×16 图像)最终 MSE0.954(接近平凡解 1.0,模型未收敛
DDPM (16×16 图像)n_gen64
DDPM (16×16 图像)运行时长233.6 s

八、本阶段小结

  • 从零 numpy 实现 4 个核心组件:带反向传播的 MLP、标准 Adam、cosine 噪声调度、DDPM 前/反向。无需 torch / jax,CPU 即可在分钟级复现 GAN 与扩散的训练。
  • 两套范式在同一目标分布上做了公平对比:GAN 一步对抗生成,DDPM T 步去噪;前者训练不稳但采样快,后者训练稳但采样慢。
  • 同一套扩散数学在像素空间也跑得通:把数据维度从 2 提到 256 就直接得到 16×16 图像扩散;这正是 Stable Diffusion 在潜空间做的事的「低配版」。
  • 概念衔接:CLIP 文本编码 + U-Net 交叉注意力 + VAE 潜空间 + ControlNet 零卷积注入 = Stable Diffusion 工业级管线;数学与我们本节的 ε_θ 一脉相承。

九、与前/后阶段的关系

  • 承接阶段六(CV):阶段六我们用 OpenCV + YOLOv8 做了「看见/检测」图像;本阶段是「生成图像」,是 CV 的另一半。我们还把阶段四/五搭的 MLP 工具(_common.py)作为底座,验证同一份手写网络在「判别 vs 生成」两种目标下都能跑。
  • 衔接阶段八(NLP / LLM):文本编码器 CLIP/T5 是 AIGC 与 LLM 的共同组件。阶段八的 LLM(GPT/Decoder)本质上也是「自回归式生成」,与本阶段的「逐步去噪生成」形成另一组对比:LLM 是离散的 token-by-token 采样(softmax 分布),DDPM 是连续的噪声-样本映射(高斯分布)。两者最终统一为「学习一个条件生成模型 p(y|x)」的同一范式。

十、参考

  • Goodfellow et al.,Generative Adversarial Networks, NeurIPS 2014.
  • Radford et al.,Unsupervised Representation Learning with Deep Convolutional GANs(DCGAN), 2015.
  • Ho et al.,Denoising Diffusion Probabilistic Models(DDPM), NeurIPS 2020.
  • Nichol & Dhariwal,Improved Denoising Diffusion Probabilistic Models, ICML 2021.
  • Rombach et al.,High-Resolution Image Synthesis with Latent Diffusion Models(Stable Diffusion), CVPR 2022.
  • Zhang et al.,Adding Conditional Control to Text-to-Image Diffusion Models(ControlNet), ICCV 2023.

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询