算法可视化与交互学习平台
VAE:从 latent space 到概率生成VAE: From Latent Space to Probabilistic Generation
从最小二维 latent space 出发,理解 Encoder 如何输出 μ 和 σ、重参数化如何采样 z、Decoder 如何生成结果,以及 reconstruction loss 与 KL loss 如何共同塑造可生成的连续空间。
先抓住 VAE 的核心直觉
VAE 的关键不是把样本压缩成一个固定点,而是学习一个连续、可采样、可生成的 latent space。它把每个样本编码成 latent space 中的一个概率云团,再从这个云团里采样并解码生成数据。
普通 AutoEncoder 可以粗略写成:
VAE 则写成:
这一节会围绕同一条链路展开:Encoder 学会每个样本在 latent space 里的中心 μ 和不确定性 σ,然后通过重参数化采样得到 z,最后由 Decoder 把 z 翻译回数据空间。训练后,我们也可以不输入原始 x,而是直接从 N(0, I) 采样新的 z,再交给 Decoder 生成新样本。
从一个点,到一个概率云团
1. 普通 AutoEncoder 学的是一个点
普通 AutoEncoder 会把输入 x 压缩成一个 latent vector z,再从 z 重建出 x'。这对压缩和重建很有用,但 latent space 可能是一堆离散孤岛:训练样本附近能解码,中间区域不一定有意义。
2. VAE 学的是一个分布
VAE 的 Encoder 不直接输出一个点,而是输出一个高斯分布的参数:中心 μ 和方差相关的 logvar。因此每个样本不再只是 latent space 中的一个点,而是一片有范围的不确定区域。
3. 两个样本手算一次真实 VAE 前向传播
为了不把 Encoder 和 Decoder 当成黑盒,先看一个一维输入、二维 hidden、二维 latent 的最小神经网络。这里的权重是人为指定的,目的不是得到好模型,而是让每一步都可以直接算出来。绿色数字表示这个最小网络里人为指定、实际训练中会由神经网络学习得到的权重/偏置或分布参数,避免它们看起来像突然出现的常量。右侧图表使用完全相同的 μ、σ、ε、z 数值,把这段演算画成两个概率云团。
什么是 Latent heads?
这里的 Latent heads 不是新的算法,而是神经网络里很常见的“输出分支”:Encoder 先用共享隐藏层提取输入结构特征 h,再从同一个 h 分出两个输出层,一个预测 μ,另一个预测 logvar。
之所以需要两个 head,是因为 VAE 的 Encoder 不再输出一个确定点,而是输出一个高斯分布 q(z|x) 的参数。μ head 像定位器,决定这个样本的概率云团放在 latent space 的哪里;logvar head 像不确定性估计器,决定这个云团有多分散。
实际模型通常学习 logvar = log(σ²),而不是直接学习 σ,因为 σ 必须为正,直接学习容易出现负数或数值不稳定。训练时网络输出 logvar,再用 σ = exp(0.5 * logvar) 还原标准差。
先算 Encoder。为了能画出二维云团,我们让 Encoder 先输出二维 hidden,再由两个 head 输出二维 μ 和二维 σ:
固定两次二维噪声,观察重参数化如何把概率云团变成真正送入 Decoder 的采样点:
Decoder 也用一个真实的小网络。为了手算简单,这里让它先汇总二维 z,再生成一维重建值:
最后算 loss。重建项看 x' 是否接近 x;KL 项看每个样本的 latent 分布是否接近标准正态 N(0, 1)。这里的 logvar 由上面的绿色 σ 换算而来:logvar = log(σ²)。
这个例子里,KL1 和 KL2 分别约束两个样本自己的局部后验 q(z|x1)、q(z|x2)。它不是要求所有样本变成同一个点,而是给每个概率云团一个软约束:中心不要离标准正态区域太远,尺度也不要失控。x2 的 μ=(1.0,0.8) 比 x1 的 μ=(0,0) 更远离标准正态中心,所以 KL2 更大。VAE 训练时就在同时做两件事:让 x' 像 x,又让 latent 分布规整到容易从 N(0, 1) 采样。
4. 用二维几何形状理解 latent
如果训练数据是二维几何形状,可以把一个具体圆形、三角形或方形样本理解成 latent space 中的一个 z 点;很多相似圆形样本靠在一起,就形成“圆形区域”;三角形和方形也会形成自己的区域。但要注意,z1、z2 只是坐标轴,不等于“圆形轴”或“三角形轴”。
例如,同一片圆形区域内部的一个方向,可能对应“半径变大”;另一个方向,可能对应“圆逐渐拉伸成椭圆”。普通 VAE 不保证每个坐标轴都有单独语义,真正更接近语义变化的通常是区域和方向。
5. 为什么这件事重要
当模型被要求让这些概率云团靠近标准正态分布时,latent space 会变得更连续、更可采样。训练后,我们可以直接从 N(0, I) 里取一个新的 z,再送进 Decoder 生成新样本。对二维几何形状来说,这意味着可以从 latent 地图上的某个位置生成一个新圆形、一个变宽的三角形,或一个介于两类结构之间的过渡形状。
6. 本模块的最小实验
实验台使用二维 toy data,而不是一上来训练大图像模型。这样每一步都能看清楚:数据点如何被编码到 latent space,μ 如何形成聚类,σ 椭圆如何表达不确定性,Decoder 又如何把手动选择的 z 翻译回数据空间。
把 μ、σ、z 画成概率云团
为什么用 logvar 得到 σ
重参数化:VAE 的核心公式
Encoder 输出 μ 和 logσ²,先把 logσ² 转成标准差 σ,再把固定标准正态噪声 ε 变换成采样点 z。这个过程叫重参数化:随机性来自 ε,μ 和 σ 只参与可微的平移与缩放,因此仍然能通过反向传播学习。
1. 先做一次二维数值代入
设 Encoder 对某个样本给出 μ=(1.2,-0.5)、σ=(0.3,0.2),再从标准正态中抽到 ε=(0.8,-1.1)。公式中的 ⊙ 表示逐元素相乘,不是矩阵乘法。
2. 按坐标展开看得更清楚
如果 latent_dim = 2,那么 z 的两个坐标分别由 μ、σ、ε 的对应坐标算出。这里的 z1、z2 是坐标维度,不是第 1 个样本和第 2 个样本。
3. 这一步到底在做什么
普通 AutoEncoder 通常把输入压成一个固定 latent 点;VAE 则把输入编码成一个概率云团 q(z|x)。μ 是云团中心,σ 是每个方向上的扩散大小,ε 是标准正态噪声。
4. 为什么叫“重参数化”
如果直接写 z ~ N(μ,σ²),采样动作依赖 Encoder 输出的 μ 和 σ,随机采样本身不方便反向传播。重参数化把采样改写成“固定噪声 + 可微变换”。
5. 生成直觉
同一个输入不再对应单个点,而是对应 latent space 中的一小片区域。每次 ε 不同,采样到的 z 也不同,Decoder 会得到相似但略有变化的结果。这也是 latent 连续变化和插值能够成立的基础。
VAE Loss:重建 + 空间规整
重建损失让 Decoder 尽量还原输入;KL 损失约束的是每个样本自己的局部后验 q(z|x),不是 q(z|class),也不是要求整个 latent histogram 严格服从 N(0,I)。它会把每个小概率云团轻微拉向标准正态范围,防止 latent space 碎裂成不可采样的孤岛;而圆形、三角形、方形等局部区域主要来自 reconstruction loss 对结构相似性的保留。β 控制重建精度和空间规整之间的权衡;β 太大时可能过度压缩差异,β 太小时空间可能不够连续。
KL Loss 如何约束概率云团
1. KL 到底在约束什么
VAE 里的 KL Loss 衡量的是:当前样本的局部后验 q(z|x)=N(μ,σ²),和标准正态先验 N(0,1) 有多远。它不是用来把不同类别强行分开,而是让 latent space 不要碎裂成孤岛,使训练后可以从 N(0,I) 中采样并得到有意义的生成结果。
2. 从数学公式到代码公式
一维高斯 N(μ,σ²) 到标准正态 N(0,1) 的 KL 可以写成:
代码中通常不直接学习 σ,而是学习 logvar = log(σ²)。因此 exp(logvar) 就是 σ²,公式可以改写为:
3. 三个核心项的直觉
这个公式真正做的事,可以拆成三个约束:中心不要太远,云团不要太宽,也不要缩成一个点。
4. 为什么要 mean()
训练时一个 batch 里有很多样本,每个样本都有自己的 q(z|x) 和自己的 KL。代码里的 mean() 表示把样本维度和 latent 维度上的 KL 取平均,得到当前 batch 的平均正则化压力。
5. 回到本模块的两个样本
手算示例里 KL1 ≈ 0.318,KL2 ≈ 0.944。这说明第 2 个样本的概率云团比第 1 个样本更偏离标准正态:它的中心 μ=(1.0,0.8) 离原点更远,而且第二个方向上的 σ=0.3 比标准正态更窄,所以 KL 更大。
和前面模块如何连起来
1. 和线性模型中的梯度下降相同
VAE 训练仍然遵循同一条优化链路:先 forward,计算 loss,再 backward,最后更新参数。只是这里的参数不再是 w,b,而是 Encoder 和 Decoder 的所有神经网络权重。
x_recon, mu, logvar, z = model(x)
loss = recon_loss + beta * kl_loss
optimizer.zero_grad()
loss.backward()
optimizer.step()2. 和 MLP 模块相同
本模块里的 Encoder 和 Decoder 都是小 MLP。Encoder 把二维输入加工成 hidden representation,再输出 mu 和 logvar;Decoder 把二维 z 转回二维数据点。
3. 和 Transformer 模块的表示空间相通
Transformer 关注 token 在表示空间中的关系;VAE 更进一步,显式地把表示空间建模成概率分布。你看到的 μ 点、σ 椭圆和采样点 z,就是“表示空间 + 不确定性”的可视化版本。
圆环 VAE 可交互实验
点击“训练圆环 VAE”后,会生成二维圆环数据并训练一个 2D latent VAE。调整样本数、圆环噪声、训练轮数、学习率或 KL 权重后,需要重新训练;手动 z1/z2 用于观察 Decoder 如何把 latent 点映射回数据空间。
VAE 训练与 latent space 实验台
训练一个最小 MLP-VAE。数据是四组二维点,模型学习把它们编码为二维 latent 分布,再解码回原始空间。调节 β 可以观察 reconstruction 和 KL 之间的权衡;调节 manual z 可以直接观察 Decoder 如何把 latent 点翻译成数据空间位置。