变分自编码器 VAE
本文结合豆包生成
先从自编码器(AE) 说起,再讲VAE是怎么把它升级成能生成新内容的「变分自编码器」。
先搞懂基础——自编码器(AE)是什么?
大白话:AE就是一个「压缩-还原」的AI机器,分两个核心部分:
-
编码器:把输入的样本(比如一张人脸照片
x),压缩成一个很短的特征码z(也叫隐变量,相当于这张脸的“精华摘要”) -
解码器:拿着这个
z,把原来的照片还原出来,得到x̂
它的训练目标只有一个:还原出来的x̂和原图x越像越好,对应的损失就是「重构损失」(比如像素差的平方和MSE)。
极简公式版:
AE的致命痛点:只能压缩还原,没法生成新东西
训练好AE之后,只有编码器输出的那些z,解码器才能还原出合理的图片。如果随便编一个z,解码器输出的基本都是乱码、噪声。
原因很简单:AE的隐空间(所有z的集合)是离散、有空洞、无规律的——训练数据对应的z都是一个个孤立的点,点和点之间的空白区域全是无效的,解码器根本不知道这些z该对应什么内容。
而VAE,就是专门解决这个问题的:它要把隐空间变成连续、规整、处处都能用的,这样随便采样一个z,都能生成合理的新内容。
VAE的核心思路——给隐变量z加上概率分布
VAE的核心改动,完全针对编码器:
AE的编码器:输入
x,输出一个固定的z
VAE的编码器:输入
x,输出z的概率分布(默认用最易计算的正态分布)
大白话解释:
比如一张人脸x,AE说“这张脸的精华就是z=[1,2,3]”,一锤子买卖;
而VAE说“这张脸的精华,大概率在以μ=[1,2,3]为中心、σ=[0.1,0.2,0.1]为宽度的正态分布里,你从这个范围里随便采样一个z,都能还原出这张脸”。
为什么要这么做?
-
给每个
x的z留了容错空间,不会让z缩成孤立的点; -
我们可以约束所有样本对应的分布,都往标准正态分布
N(0, I)靠(均值为0,方差为1),这样所有分布都集中在同一个规整的空间里,点和点之间没有空洞,隐空间就连续了!
先把符号说清楚
| 符号 | 含义 |
|---|---|
| \(x\) | 输入的训练样本(比如人脸、手写数字) |
| \(z\) | 隐变量(压缩后的精华特征) |
| \(q_\phi(z|x)\) | 编码器(推断网络),\(\phi\)是编码器参数;输入x,输出z的正态分布 |
| \(p_\theta(x|z)\) | 解码器(生成网络),\(\theta\)是解码器参数;输入z,输出还原的x |
| \(p(z)\) | z的先验分布,我们规定它是标准正态分布N(0, I) 。输入一个具体的隐变量取值z,输出这个z在标准正态分布下对应的概率密度值 |
VAE的核心技巧——重参数化(Reparameterization Trick)
这里有个致命的工程问题:我们要从编码器输出的正态分布\(N(\mu, \sigma^2)\)里采样z,但“采样”这个动作是随机的、不可导的——反向传播更新参数时,梯度根本传不回去,模型没法训练。
重参数化就是专门解决这个问题的,核心思路:把随机性和可导的参数完全拆分开。
公式&大白话解释
原来的采样:\(z \sim N(\mu, \sigma^2)\) → 随机性来自\(\mu\)和\(\sigma\),梯度直接断了
重参数化之后:
其中 \(\varepsilon \sim N(0, 1)\),也就是从标准正态分布里采样一个和模型参数完全无关的随机噪声\(\varepsilon\)。
大白话翻译:
- 把所有随机性都甩给\(\varepsilon\)(\(\varepsilon\) 是在每一次前向传播的重参数化步骤里,临时、实时采样生成的随机数)。\(\mu\)和\(\sigma\)都是编码器输出的、可导的参数;
- 梯度可以正常通过\(\mu\)和\(\sigma\)回传给编码器,解决了不可导的问题。
实用细节:编码器一般不直接输出\(\sigma^2\),而是输出\(log\sigma^2\)。因为\(\sigma^2\)必须是正数,而神经网络的输出可以是任意实数,输出\(log\sigma^2\)后,用\(\sigma = \exp(log\sigma^2 / 2)\)就能得到一定为正的\(\sigma\),避免数值错误。
VAE的核心公式——损失函数(ELBO证据下界)
我们训练VAE的终极目标,是让模型能生成和训练数据一致的样本,也就是最大化训练样本x的出现概率\(\log p(x)\)(对数似然)。
但这个\(\log p(x)\)直接算不出来,因为它要对所有可能的z积分:\(p(x) = \int p(x|z)p(z) dz\),这个积分没有闭式解,根本算不动。
所以数学家们用「变分推断」,推导出了\(\log p(x)\)的下界ELBO(Evidence Lower Bound),也就是:
我们只要最大化ELBO,就相当于把\(\log p(x)\)的下界往上抬,间接实现了终极目标。
ELBO的分解(核心公式)
ELBO可以拆成两个极易理解的部分,我们的损失函数就是负的ELBO(因为梯度下降是最小化损失):
对应的损失函数:
我们要做的就是最小化这个Loss。
拆解每一项:
1. 重构项:和AE的重构损失完全等价
公式里的\(\mathbb{E}_{q_\phi(z|x)} \left[ \log p_\theta(x|z) \right]\),就是从编码器输出的分布里采样z,解码器用z还原出x的概率的期望。
- 大白话:还原得越像原图
x,这个值就越大,对应的负重构项就越小,损失就越小。 - 工程实现:这个项直接等价于AE里的MSE损失(连续数据,比如图片)或交叉熵损失(离散数据,比如文本),直接用就行。
2. KL散度项:约束隐空间的正则项
KL散度\(D_{KL}(P \parallel Q)\),就是衡量两个概率分布的“距离”:两个分布越像,KL散度越小,完全一致时等于0。
这里的KL散度,就是编码器输出的分布\(q_\phi(z|x)\),和我们规定的标准正态先验\(p(z)=N(0,I)\)的距离。
- 大白话:我们逼着编码器输出的分布尽量往标准正态分布靠,这样所有样本的
z分布都集中在同一个规整的空间里,不会乱跑,隐空间就没有空洞了。
关键福利:KL散度的闭式解(不用采样,直接算)
因为我们假设\(q_\phi(z|x)\)是对角协方差的正态分布\(N(\mu, \sigma^2 I)\),先验是\(N(0,I)\),所以它们的KL散度有现成的闭式解,不用复杂的采样,直接用\(\mu\)和\(\sigma\)就能算,是VAE能高效训练的核心:
单个维度的KL散度公式:
d维的多元正态分布(每个维度独立),直接把每个维度的KL散度加起来即可:
VAE完整的训练流程
- 取一个训练样本
x,输入编码器; - 编码器输出两个向量:均值\(\mu\),和对数方差\(log\sigma^2\)(维度和隐变量
z一致); - 用重参数化技巧采样
z:\(z = \mu + \exp(log\sigma^2 / 2) \cdot \varepsilon\),其中\(\varepsilon \sim N(0,I)\); - 把
z输入解码器,得到重构的\(\hat{x}\); - 计算损失:
Loss = 重构损失(MSE/交叉熵) + KL散度损失(闭式解计算); - 反向传播,更新编码器\(q_\phi(z|x)\)和解码器\(p_\theta(x|z)\)的参数\(\phi\) 和 \(\theta\)
- 重复以上步骤,直到模型收敛。
训练好的VAE怎么用?核心能力
- 生成新样本:直接从标准正态分布\(N(0,I)\)里随机采样一个
z,丢给解码器,就能生成一个全新的、和训练数据同分布的样本(比如全新的人脸、手写数字); - 隐空间插值:取两个样本的
z1和z2,做线性插值\(z = a \cdot z1 + (1-a) \cdot z2\)(a从0到1),就能生成从样本1平滑过渡到样本2的内容; - 特征解耦:隐变量
z的不同维度,可能对应不同的语义特征(比如人脸的微笑程度、年龄),调整对应维度的值,就能精准修改生成内容的属性。

浙公网安备 33010602011771号