VAE 与隐空间表示#
本页内容5 个部分
查看概览图
四项 loss 各修一种病,
少一项就塌一块
SD 的 VAE 常结合重建、感知、对抗与 KL 损失;KL 是正则项,不是后续扩散的必要条件。
模糊和「塑料感」不是同一个病
-
L1 + LPIPS 修
L2 与模糊在多解条件回归中,逐像素均值可能抹平细节;可精确重建的任务不必模糊,L1 也不保证清晰。 -
GAN 修
纹理不真实高频回来了但分布不对:边缘锐利但皮肤像蜡像——统计特征(毛孔尺寸、密度、随机性)不像真实材质。
vs 离散 VQ
三个损失项各更新一部分
-
①
重建损失
更新 encoder + decoder(通过 STE)让系统学到有意义的编码和重建 -
②
Codebook 更新
只更新 codebook 向量 e_k让 codebook 追着 encoder 输出走(系数 1.0,全力追) -
③
Commitment
只更新 encoder让 encoder 输出靠近 codebook(系数 0.25,轻轻靠)
EMA 提供独立于梯度优化器的码本更新与平滑,效果取决于 decay 和采样。但 N_k 和 m_k 同步衰减意味着 dead code 原地不动——也不会自愈。一个从一开始就远离所有 z_e 的码,永远等不到第一次被选中。
组件阅读路线 · ELBO · 重参数化 · SD VAE 编解码器 · 向量量化
1. VAE 基础原理#
1.1 从 AE 到 VAE:为什么要概率化#
普通 autoencoder 把图像压缩到一个确定性的隐向量 ,只关心重建。问题:训练集中没出现的 区域是空白的,在隐空间随便采样一个点解码出来基本是噪声——隐空间没有"分布",只有一堆离散的训练点。这对生成模型而言是致命的。
VAE 的解法:让编码器输出一个分布 而不是一个点,并用 KL 散度把所有 都拉向同一个先验 。这样隐空间被"填满"成一个连续光滑的高斯,任意采样都能解码出有意义的图像。
VAE 的核心思想(一句话)
学一个编码器 把图像映射到隐变量 ,学一个解码器 从 重建图像。用 KL 散度约束 接近标准正态 ,这样隐空间连续、平滑、可插值。
1.2 ELBO:VAE 在优化什么#
VAE 最大化 Evidence Lower BOund(ELBO):
是先验。最大化 ELBO = 最大化重建质量 + 最小化隐分布与标准高斯的距离
两项的含义:
- 重建项:让解码器从 尽可能还原原图。实际中用 MSE 或 L1 loss
- KL 项:让编码器输出的 接近 。有闭式解:
是隐变量维度。每个维度独立计算,不需要采样估计。
1.3 重参数化技巧#
编码器输出 和 ,采样 。但随机采样不可导,梯度传不回编码器。
重参数化:把随机性外包给独立噪声 :
和 是确定性函数,梯度可以直接回传到编码器参数
面试追问:为什么输出 而不是 ?
- 必须 > 0,直接输出 需要额外的非负约束(如 softplus)
- 可以是任意实数,网络输出不受约束,数值更稳定
- 计算 时:
1.4 KL 正则化的作用#
KL 项的作用:让编码器输出的隐空间分布接近标准高斯 。这样扩散模型在 步加噪后也是 ,采样起点才有意义。
如果没有 KL 正则,编码器可能把不同图像映射到隐空间中互不相干的区域,导致隐空间"不连续"——扩散模型在这种空间中插值和去噪都不自然。
2. KL-VAE:Stable Diffusion 中的落地#
Stable Diffusion 社区把它用的 VAE 叫"KL-VAE",但其实就是上一节的标准 VAE(Kingma & Welling, 2014)——这个名字只是为了和后面会讲的 VQ-VAE 做区分,强调用的是 KL 散度正则化(连续隐空间),而非 VQ-VAE 的离散 codebook。
2.1 SD VAE 的编解码器架构#
① Encoder vs ② Decoder(对称结构)
Encoder(下采样 / 压缩)
输入图像
512 × 512 × 3
初始 Conv 3×3
3 → 128 ch
512 × 512 × 128
Down Block 1
ResNet × 2
512 × 512 × 128
↓ Downsample ×½
256 × 256 × 128
Down Block 2
ResNet × 2
256 × 256 × 256
↓ Downsample ×½
128 × 128 × 256
Down Block 3
ResNet × 2
128 × 128 × 512
↓ Downsample ×½
64 × 64 × 512
Down Block 4
ResNet × 2 (不下采样)
64 × 64 × 512
Middle Block
ResNet → Attn → ResNet
64 × 64 × 512
输出层
GroupNorm + Conv 3×3
512 → 8 ch
64 × 64 × 8
μ 均值
64×64×4
log σ² 方差
64×64×4
重参数化 z = μ + σ·ε
隐变量 z 输出
64 × 64 × 4
→ 送入扩散模型
共享
Middle Block
(对称的最深处)
Decoder(上采样 / 还原)
隐变量 z 输入
64 × 64 × 4
来自扩散模型去噪结果
初始 Conv 3×3
4 → 512 ch
64 × 64 × 512
Middle Block
ResNet → Attn → ResNet
64 × 64 × 512
Up Block 1
ResNet × 2 (不上采样)
64 × 64 × 512
Up Block 2
ResNet × 2
64 × 64 × 512
↑ Upsample ×2
128 × 128 × 256
Up Block 3
ResNet × 2
128 × 128 × 256
↑ Upsample ×2
256 × 256 × 128
Up Block 4
ResNet × 2
256 × 256 × 128
↑ Upsample ×2
512 × 512 × 128
输出层
GroupNorm + SiLU + Conv 3×3
128 → 3 ch
512 × 512 × 3
重建图像 x̂
512 × 512 × 3
RGB 像素图像
③ ResNet Block 内部细节
输入 x
H × W × C_in
GroupNorm
按通道组归一化,不依赖 batch size
SiLU 激活
x · sigmoid(x),平滑非线性
Conv 3×3
C_in → C_out | padding=1
GroupNorm
SiLU 激活
Conv 3×3
C_out → C_out
+
输出
H × W × C_out
Skip Connection
C_in = C_out → Identity
C_in ≠ C_out → Conv 1×1
ResNet Block 核心要点
- 📌 残差学习:F(x)+x,网络只学"增量"而非全量映射,更容易优化
- 📌 梯度高速公路:skip 路径提供直接梯度通路,缓解深层优化困难;不保证所有方向的梯度都不消失
- 📌 GroupNorm:按通道组归一化,不依赖 batch size,适合小 batch 训练
- 📌 Conv 1×1 投影:通道数变化时对 skip 路径做维度对齐,保证相加合法
具体维度流程#
# 编码
x: (B, 3, 512, 512) # 输入 RGB 图像
↓ Encoder
μ: (B, 4, 64, 64) # 均值
logσ²: (B, 4, 64, 64) # 对数方差
z: (B, 4, 64, 64) # 采样 z = μ + σ·ε
# 解码
z_0: (B, 4, 64, 64) # 去噪后的隐变量
↓ Decoder
x_hat: (B, 3, 512, 512) # 重建图像
# 实际中 SD 会对 z 做 scaling:z = z * 0.18215
# 这个 magic number 让 z 的方差接近 1
VAE 质量 = 生成质量上限
VAE 解码器的重建能力决定了最终图像的清晰度上限。即使 U-Net 去噪完美,如果 VAE 解码器重建模糊,最终图像也会模糊。这就是为什么 SDXL 升级了 VAE——更好的解码器直接提升了全局画质。
2.2 SD VAE 的完整损失函数#
SD 的 VAE 不只用 ELBO 的两项,而是四项损失协同训练:
| 损失项 | 作用 | 如果去掉会怎样 |
|---|---|---|
| L1 重建 | 像素级对齐 | 图像整体偏移、颜色不对 |
| LPIPS 感知 | VGG 特征空间对齐,保留结构和纹理 | 像素对齐但模糊(平均化高频细节) |
| GAN 对抗 | patch 判别器逼迫输出"真实"纹理 | 重建清晰但纹理不自然、缺锐度 |
| KL 正则 | 约束隐空间分布接近 | 隐空间不连续,扩散模型无法在其中有效操作 |
为什么需要 GAN 判别器?
纯 L1/L2 + KL 训练的 VAE 重建是模糊的——因为 L2 对所有可能输出取期望,会"平均化"高频细节。加入 patch discriminator 后,解码器必须生成判别器也认为"真实"的纹理,重建锐度大幅提升。这种 VAE+GAN 混合训练在 VQGAN(Esser et al., 2021)中首次提出,后被 SD 直接采用。
概念区分:高频细节 vs 纹理
"高频"和"纹理"在文献里经常被混用,但严格来说不是同一回事——而且 L1/LPIPS/GAN 解决的是不同层面的问题。
- 高频细节是信号处理概念:图像像素值在小范围内快速变化的成分(傅里叶频谱外圈)。锐利的边缘、单根头发丝、棋盘格线、甚至随机噪声——只要"快速变化"都算。低频则是缓变成分:大块单色、平滑渐变、整体光照。
- 纹理是视觉感知概念:物体表面看起来"是什么材质"的视觉模式。木纹、布料经纬、皮肤毛孔、砖墙颗粒、金属划痕——它有结构、有统计规律、和材质语义绑定。
关系:纹理几乎都由高频组成,但高频不一定是纹理。一根锐利边缘是高频但不是纹理(只是边,没有面);随机白噪声是高频但不是纹理(无结构);皮肤毛孔既是高频也是纹理。
| 失败模式 | 问题 | 谁负责修 |
|---|---|---|
| L2 模糊 | 高频被平均掉了:L2 对每个像素独立取均值,逼近所有合理重建的像素均值,高频部分(快速振荡)在均值里互相抵消变成 0。这是数学事实,跟"是不是纹理"无关。 | L1(中位数更稳健)+ LPIPS(在 VGG 特征空间度量距离,对模糊敏感) |
| 纹理不真实 | 高频回来了但分布不对:脸部边缘很锐利,但皮肤看起来像塑料/像蜡像——高频成分在但统计特征(毛孔尺寸、密度、随机性)不像真实材质。 | GAN(patch discriminator 学的就是"什么样的高频分布看起来像真实材质") |
反例:早期 ESRGAN 出现"过锐化"——边缘极其锋利,高频拉满,但人脸看起来不真实,因为皮肤纹理的统计被破坏了。GAN loss 调好之后才能两者兼顾。这也是为什么 SD VAE 三种损失要叠加:L1 管像素对齐,LPIPS 管"特征级"的高频保留(顺便对纹理也敏感,因为 VGG 学过纹理),GAN 管纹理分布是否符合自然图像统计。
2.3 Scaling Factor 0.18215#
SD 1.x/2.x 的代码中随处可见:
z = vae.encode(x).latent_dist.sample() * 0.18215 # 编码后缩放
x = vae.decode(z / 0.18215) # 解码前反缩放
这个 magic number ,即训练集经 VAE 编码后隐变量标准差的倒数。乘上它让 的方差接近 1,与扩散过程的 假设对齐。
SDXL 起改用可学习的 scaling factor(作为模型参数存储),不再硬编码。SD 3 的 16 通道 VAE 同样使用学到的 scaling。
2.4 各版本 VAE 差异#
| 版本 | latent 通道 | 空间压缩 | 来源 | 备注 |
|---|---|---|---|---|
| SD 1.x / 2.x | 4 | 8× | CompVis KL-f8 | 社区所有 1.5 微调模型共用同一个 VAE |
| SDXL | 4 | 8× | 改进版,更低重建误差 | 社区常用的 "SDXL VAE fix" 进一步修复色偏 |
| SD 3 / Flux | 16 | 8× | 全新训练 | 更多通道 → 保留更多细节,但 latent 维度更高 |
16 通道 vs 4 通道
SD 3 / Flux 把 latent 通道从 4 扩到 16,隐空间维度从 增长到 (4× 增加)。好处是 VAE 重建质量更高(更多通道 = 更多信息保留),代价是扩散模型需要处理更高维的隐变量。DiT 的 scaling 能力使得这个代价可以接受。
2.5 VAE 的训练与微调实践#
- 独立预训练:VAE 与 U-Net/DiT 分开训练,先把 VAE 训好再开扩散训练。
- 扩散训练时冻结:VAE 参数在扩散训练时通常不更新。微调 LoRA/DreamBooth 也只改 U-Net/DiT,不动 VAE。
- 社区 VAE 替换:部分社区 checkpoint 附带改进的 VAE 权重(如 "blessed VAE"),只替换解码器端以减少色偏或模糊。这是可行的,因为 VAE 的隐空间分布由编码器 + KL 正则决定,解码器可以独立升级。
- 视频 VAE 扩展:视频生成(如 SVD、CogVideoX)通常需要把 2D VAE 扩展为 3D VAE(加入时序卷积),在视频数据上微调以学习时间维度的压缩。
3. VQ-VAE:离散化的替代路线#
到这里 KL-VAE 的故事讲完了。但 VAE 这条路还有另一个分支:VQ-VAE,用离散 codebook 取代连续高斯。它的提出动机不是"为了离散而离散",而是为了解决标准 VAE 的一个老毛病——后验坍缩。
3.1 后验坍缩:VAE 的固有缺陷#
VAE 的 loss 由两项组成,梯度流向不同:
- 重建损失 :梯度经过 decoder → → encoder,训练整条链
- KL 散度 :只涉及 encoder 输出的 ,梯度只给 encoder,把 推向先验
正常训练时两项制衡:KL 把 encoder 往先验推,重建损失把 encoder 往"编码有意义信息"的方向拉。
后验坍缩(posterior collapse):当 decoder 太强时(比如自回归 decoder,训练时用 teacher forcing 能看到 ground truth 前缀),重建效果好不好跟 无关,制衡被打破:
- 重建损失对 的梯度 ≈ 0 → encoder 收不到来自重建损失的训练信号
- 只剩 KL 项在训练 encoder → encoder 直接输出 来最小化 KL
- 不管输入是什么, 都坍缩成同一个先验分布 → 不携带任何关于 的信息
为什么和连续隐空间有关:连续空间里 是连续参数,KL 的梯度每步都能把它们往 推一点点,平滑过渡毫无阻力。一旦 decoder 不需要 ,没有任何结构性障碍阻止坍缩。
3.2 VQ-VAE 的核心思路#
VQ-VAE(van den Oord et al., 2017):用离散 codebook 替代连续分布,从结构上堵死坍缩的退路。
- 隐空间变成一本"码本"(codebook),包含 个可学习的向量
- encoder 输出连续向量后,强制量化为最近的 codebook 条目——decoder 只能看到 个离散向量之一
为什么离散化能解决后验坍缩?
- 没有 KL 项:VQ-VAE 的 loss 里不存在"把 往先验推"的力,坍缩的驱动力被消除
- 信息必须经过离散瓶颈:decoder 的唯一输入就是选中的 codebook 向量,没有别的路可以绕过。不存在"平滑地忽略 "——你选了 code #42 和 code #87,decoder 必须对不同 code 做出不同响应
3.3 结构:编码 → 量化 → 解码#
输入图像 x (256×256×3)
│
▼
[Encoder CNN]
│
▼
连续特征 z_e (32×32×256) ← 每个空间位置是一个 256 维向量
│
▼ 对每个位置,L2 最近邻查找
[Codebook] K=8192 个 256 维向量
│
▼
量化特征 z_q (32×32×256) ← 每个位置替换为最近的 codebook 向量
│
▼
[Decoder CNN]
│
▼
重建图像 x̂ (256×256×3)
量化层的核心操作:对特征图的每个空间位置(32×32 = 1024 个),独立找 codebook 中距离最近的向量替换:
3.4 梯度问题与 STE#
问题: 是离散选择操作,不可微。真实的 几乎处处为 0( 微小变动不改变选中的 index),梯度链在量化层断开——decoder 的重建损失无法回传到 encoder。
Straight-Through Estimator(STE):前向用量化值 ,反向时跳过量化层,把 decoder 的梯度直接复制给 encoder。实现方式:
前向:(用量化值)。反向: 让 梯度为零,PyTorch 认为 ,
STE 不是数学上精确的梯度(真实梯度是 0,STE 用 1 代替),而是一个"有用的近似"。注意 STE 只影响重建损失的梯度回传——另外两项损失各自有 stop gradient,本来就不需要穿过量化层。
3.5 损失函数#
= stop gradient(停止梯度传播),
三项损失,各自更新不同的参数:
| 损失项 | 更新谁 | 作用 |
|---|---|---|
| ① 重建损失 | encoder + decoder(通过 STE) | 让系统学到有意义的编码和重建 |
| ② Codebook 更新 | 只更新 codebook 向量 | 让 codebook 追着 encoder 输出走(系数 1.0,全力追) |
| ③ Commitment | 只更新 encoder | 让 encoder 输出靠近 codebook(系数 0.25,轻轻靠) |
⚠️ 为什么 ② 和 ③ 要拆成两项?
两项形式相似,但用 stop gradient 分别控制更新方向和力度。不能合并成一个 不加 stop gradient,因为:encoder 同时接收重建损失和对齐损失的梯度,PyTorch 的学习率是对整个参数组的,没法区分"这个梯度来自哪个 loss"。拆开后用 可以选择性缩放——重建损失全力训练 encoder,对齐损失只轻推,防止 encoder 为了靠近 codebook 而牺牲重建质量。
3.6 Codebook 更新机制:从 loss 到 EMA#
上一节的第 ② 项 loss 理论上能更新 codebook,但实际工程里几乎没人用——原 VQ-VAE 论文的附录就推荐了一个更稳的替代:指数滑动平均(EMA)更新。这一节单独把它讲透。
为什么 ② 项 loss 不好用#
- 学习率耦合:② 项的梯度走优化器,意味着 codebook 的更新步长被绑死在 encoder/decoder 的学习率上。但这两者最优学习率往往不同——encoder 想小一点稳,codebook 想快速逼近质心。
- 更新慢:每步只能挪 一小步,要等很多 batch 才能爬到合理位置。
- 噪声大:单 batch 梯度本身就抖,直接灌到 codebook 上波动明显。
- 跟其他 loss 抢梯度:走 autograd 路径意味着 ②③ 两路在 encoder 处合并,互相干扰。
EMA 的核心思路#
抛弃梯度,直接做**"在线版 K-means"**:对每个码 ,把它定义为"所有曾经被分配到它的 的(加权)均值"。
但有个工程问题:训练横跨无数 batch,我们不可能存下所有历史 然后每步重算均值。解法是用两个滑动平均量,在线维护这个均值:
- :"有效计数"——过去被分配到 的样本总数(按时间加权)
- :"有效向量和"——过去被分配到 的所有 之和(按时间加权)
- 当前质心 = 加权和 ÷ 加权计数:
每一 batch 的更新规则:
= 当前 batch 里被分到 的 个数; 是衰减率(过去信息的保留比例)
一个具体的数值演算#
假设 ,初始 , , 。看连续 3 个 batch:
Batch 1:3 个 被分到 ,分别是
- ,
- ← 正好是这一 batch 的均值
Batch 2:0 个 被分到 (本步是死码)
- ← 慢慢衰减
- ← 也慢慢衰减
- ← 原地不动!
Batch 3:2 个 被分到 :
- ,
- ← 朝 batch 3 均值 移了一点,但因为历史里还有 batch 1 的影响,没完全跟过去
三个关键观察:
- 活码更新极快:第一步就直接到了 batch 1 的均值,完全不需要"梯度慢慢爬"——这是 EMA 比 loss-② 快几个数量级的根本原因。
- 无人选中的码原地不动:Batch 2 里 和 都按同一个比例 衰减,比值 数值上保持不变。这正是死码的根源——EMA 不会让死码"漂移",但也根本不会让它复活,它只会原地等待下一次有数据落过来。
- 历史和新数据自动加权:Batch 3 收到新数据后没有完全跳过去,而是按 自动混合了历史均值和新均值。这是 EMA 比"硬重置成 batch 均值"稳定的原因——单一异常 batch 不会把 codebook 抖飞。
为什么要拆成 和 两个量?#
也许你会问:为什么不直接 (其中 是当前 batch 里被分到 的 的均值)?
因为这样所有 batch 的权重相同。但实际上一个 batch 里可能有 100 个 分到 ,另一个 batch 里只有 1 个——前者应该对质心位置有更大影响。 / 拆开维护可以自动按"被分配次数"加权,等价于"把所有历史 拿过来按时间衰减后求加权均值",这是单一 EMA 做不到的。
EMA 解决了什么、没解决什么#
| 解决 | 没解决 | |
|---|---|---|
| EMA 更新 | 学习率耦合、更新慢、噪声大、跟其他 loss 抢梯度 | 死码问题—— 和 同步衰减意味着 dead code 的位置原地不动,但也不会自愈。一个从一开始就远离所有 的码,永远等不到第一次被选中,永远是死的 |
这就是为什么实际工程里的标准配方是 EMA + Code Reset:EMA 负责把活码更新得又快又稳,Reset 负责消灭 dead code。下一节专门处理后者。
3.7 Codebook 死码问题#
codebook 有 个向量,但实际常只有 10-20% 被使用。初始化时离 encoder 输出较远的向量永远不被选中 → 没有更新 → 永远不被选中,恶性循环。上一节我们看到 EMA 让活码更新得更快更稳,但对 dead code 无能为力——EMA 的更新规则要求 ,从未被选中的码 不增、 衰减,质心位置原地冻结。本节看几种真正能让 dead code 复活或避免它产生的方案。
解决方案#
| 方案 | 做法 | 优点 | 缺点 |
|---|---|---|---|
| Code Reset | 监控每个码的 (EMA 已经在维护),发现 长期接近 0 的死码,直接把这个 重新初始化到当前某个被频繁选中的 附近 | 真·死码复活术;和 EMA 天然兼容(共用 做监控信号);工程实现简单 | 需要额外监控逻辑;reset 频率/阈值是超参 |
| Codebook Splitting | 初始少量 code(如 =64),训练中对每个码加减小扰动一分为二,逐步扩展到目标数量 | 逐步扩展码本有助于利用率;不能保证每个码都被使用 | 训练流程更复杂;codebook 大小不是固定的 |
| Gumbel-Softmax | 用可微的软加权替代硬选择(训练时加权混合所有 code,推理时 argmax 选一个) | 软分配提供更多梯度通路,但仍可能发生利用率不均或塌缩 | 训练时不是真正离散,训练/推理行为有 gap |
| FSQ | 不用 codebook,直接对每个维度标量量化(如 ), 维 = 种隐式组合 | 没有可学习码向量的死码更新问题,也无需 codebook loss;组合码利用率仍需观测 | 表达力受限于量化级数;隐式 codebook 不可学习、不可解释 |
3.8 VQ-VAE 在生成模型中的位置#
- DALL-E(2021):用 VQ-VAE 的 codebook 作为"视觉词汇表"(8192 个视觉 token),把图像变成 token 序列,用自回归 Transformer 在文本 + 视觉 token 上联合建模
- 视频生成:VideoGPT 等用 3D VQ-VAE 把视频编码为离散 token 序列,再用 Transformer 自回归生成
- 现代趋势:Stable Diffusion 把离散 codebook 换回了连续的 KL 正则化(KL-VAE),这里讨论的是连续 latent 上的 diffusion / flow matching;也存在离散扩散模型。但离散 tokenization 在语言-视觉统一建模中仍有生命力(Chameleon、Emu 等)
VQ-VAE 与 KL-VAE 的关系
VQ-VAE 本身不是扩散模型,但它是 Latent Diffusion(Stable Diffusion)的重要前驱。SD 的 autoencoder 正是从 VQ-VAE 的设计演化而来——只是把离散 codebook 换回了连续的 KL 正则化,LDM 也可以在 VQ 自编码器的连续嵌入表示上训练,不能把 KL 当成唯一选择。理解 VQ-VAE 有助于理解为什么 Latent Diffusion 要先做图像压缩、以及 codebook 设计的各种权衡。两条路线本质上是一个权衡:连续 + 平滑 vs. 离散 + 可 token 化。
4. 理解检查#
4.1 QSD 的 VAE 和普通 VAE 有什么区别?#
参考答案
结构上区别不大——都是编码器输出 ,重参数化采样 ,解码器重建。核心区别在训练损失:普通 VAE 只用重建+KL,SD 的 VAE 额外加了感知损失(LPIPS)和对抗损失(patch discriminator)。这让 SD VAE 的重建质量远超普通 VAE,但训练复杂度也更高。另外 SD VAE 的编解码器用的是更深的 ResNet 架构(非简单 MLP),且对 2D 图像有针对性设计(多级下采样/上采样 + skip connection)。
4.2 QKL 正则对 Latent Diffusion 为什么重要?#
参考答案
KL 正则约束编码器后验的尺度和分布,帮助权衡重建与潜空间规整性;但 latent diffusion 不要求干净 latent 已服从标准高斯。充分加噪的终端 才需要接近采样先验,反向模型学习的是实际 latent 数据分布。LDM 原论文同时研究 KL 与 VQ 正则化的自编码器,因此“没有 KL 就不能做 latent diffusion”不成立。
4.3 Q为什么 VAE 重建总是比原图模糊?#
参考答案
两个原因:(1) 信息瓶颈—— 维压缩到 维,48× 压缩必然丢失信息,主要丢的是高频细节;(2) KL 正则的代价——KL 项迫使编码器不要"过度编码"(否则隐空间不连续),某种程度上限制了编码器的信息承载量。这就是为什么 SD 3 增加到 16 通道——更多通道 = 更多信息保留 = 更清晰重建。
**面试总结:**SD 的 VAE(KL-VAE)= 标准 VAE + 感知损失 + 对抗损失 + 更深的 ResNet 架构。它是独立预训练、扩散训练时冻结的。隐空间的质量(连续性、信息保留)直接决定了最终生成图像的质量上限。
原始资料:Auto-Encoding Variational Bayes、VQ-VAE、Latent Diffusion。
延伸阅读#
- Stable Diffusion 的完整流程:文本、隐变量和去噪模型怎样组成一次生成流程?
- 视频生成的表示与注意力:时间维度给压缩、注意力和计算成本增加了哪些约束?