扩散模型入门——从MLE到EM

zhouyx 40 阅读 1 评论 技术学习

最大似然估计

扩散模型的核心功能是学习图像,然后从生成新的图像,就像一名技术高明的雕刻师,看过很多雕塑以后,就可以从一块大理石中雕刻出精美的石雕。

从概率的角度来讲,把每一张图片都理解为一个数据点,我们的任务是要找出这一批数据的分布情况,这样,我们就能生成最像这一批数据的一个数据

概率论的方法通常是使用最大似然估计(Maximum Likelihood Estimation, MLE)来解决这个问题

怎么做的?举个例子:

假设观测到 5 个数据:D={2, 3, 5, 7, 8},已知他们符合的是高斯分布,我们要找的就是能让他们同时发生的概率最大的参数 θ(在这里就是μ和σ2) ,联合概率(似然函数)可以写成为:

L(μ,σ2)=∏i=1Np(xi∣μ,σ2)L(\mu, \sigma^2) = \prod_{i=1}^{N} p(x_i \mid \mu, \sigma^2)

展开带入:

L(μ,σ2)=∏i=1N12πσ2exp⁡(−(xi−μ)22σ2)L(\mu, \sigma^2) = \prod_{i=1}^{N} \frac{1}{\sqrt{2\pi\sigma^2}} \exp\left(-\frac{(x_i-\mu)^2}{2\sigma^2}\right)

取 log可以把连乘变为求和,因为我们这里是找让这个式子值最大的θ ,所以套一个log无伤大雅

ℓ(μ,σ2)=ln⁡L(μ,σ2)=∑i=1Nln⁡p(xi∣μ,σ2)\ell(\mu, \sigma^2) = \ln L(\mu, \sigma^2) = \sum_{i=1}^{N} \ln p(x_i \mid \mu, \sigma^2)

因为 xi 已知,所以在这里未知数就是我们要求的参数(μ和σ2),直接对两个求导

∂ℓ∂μ=1σ2∑i=1N(xi−μ)=0⟹  μ^MLE=1N∑i=1Nxi=15(2+3+5+7+8)∂ℓ∂σ2=−N2σ2+12σ4∑i=1N(xi−μ)2=0⟹  σ^MLE2=∑i=15(xi−μ^)2=(2−5)2+(3−5)2+(5−5)2+(7−5)2+(8−5)2=5.2\begin{aligned} \frac{\partial \ell}{\partial \mu} &= \frac{1}{\sigma^2}\sum_{i=1}^{N}(x_i-\mu) = 0 \\ &\Longrightarrow\; \hat{\mu}_{\text{MLE}} = \frac{1}{N}\sum_{i=1}^{N} x_i = \frac{1}{5}(2+3+5+7+8)\\ \frac{\partial \ell}{\partial \sigma^2} &= -\frac{N}{2\sigma^2} + \frac{1}{2\sigma^4}\sum_{i=1}^{N}(x_i-\mu)^2 = 0 \\ &\Longrightarrow\; \hat{\sigma}^2_{\text{MLE}} = \sum_{i=1}^{5}(x_i-\hat{\mu})^2 = (2-5)^2+(3-5)^2+(5-5)^2+(7-5)^2+(8-5)^2 = 5.2 \end{aligned}

用程序验证一下

from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np


def generate_sample_data(
    sample_size: int = 300, seed: int = 42
) -> np.ndarray:
    """模拟产品重量测量值,单位为克。"""
    rng = np.random.default_rng(seed)

    # 假设产品的目标重量约为 500 g,生产波动的标准差约为 12 g。
    measurements = rng.normal(loc=500, scale=12, size=sample_size)

    # 模拟测量仪器只能精确到 0.1 g。
    return np.round(measurements, 1)


def gaussian_mle(data: np.ndarray) -> tuple[float, float]:
    """返回高斯分布参数的最大似然估计:均值 mu 和标准差 sigma。"""
    mu = np.mean(data)

    # MLE 方差的分母是 n(ddof=0),不是无偏估计使用的 n - 1。
    variance = np.mean((data - mu) ** 2)
    sigma = np.sqrt(variance)
    return float(mu), float(sigma)


def plot_fit(data: np.ndarray, mu: float, sigma: float) -> Path:
    """绘制样本直方图和拟合后的高斯概率密度曲线。"""
    margin = 0.15 * (data.max() - data.min())
    x = np.linspace(data.min() - margin, data.max() + margin, 500)
    pdf = np.exp(-0.5 * ((x - mu) / sigma) ** 2) / (
        sigma * np.sqrt(2 * np.pi)
    )

    plt.figure(figsize=(8, 5))
    plt.hist(
        data,
        bins=20,
        density=True,
        alpha=0.55,
        color="skyblue",
        edgecolor="black",
        label="Measured weights",
    )
    plt.plot(
        x,
        pdf,
        color="crimson",
        linewidth=2.5,
        label=rf"Gaussian MLE ($\mu={mu:.2f}$, $\sigma={sigma:.2f}$)",
    )
    plt.scatter(
        data,
        np.zeros_like(data),
        marker="|",
        s=45,
        color="black",
        alpha=0.18,
        label="Individual measurements",
    )
    plt.xlabel("Product weight (g)")
    plt.ylabel("Probability density")
    plt.title(f"Gaussian MLE Fit for {len(data)} Product Measurements")
    plt.legend()
    plt.grid(alpha=0.2)
    plt.tight_layout()

    output_path = Path(__file__).with_name("gaussian_mle_fit.png")
    plt.savefig(output_path, dpi=150)
    plt.show()
    return output_path


if __name__ == "__main__":
    samples = generate_sample_data(sample_size=300, seed=42)
    estimated_mu, estimated_sigma = gaussian_mle(samples)

    print(f"Sample count: {len(samples)}")
    print(f"MLE mean (mu): {estimated_mu:.4f}")
    print(f"MLE variance (sigma^2): {estimated_sigma**2:.4f}")
    print(f"MLE standard deviation (sigma): {estimated_sigma:.4f}")

    saved_path = plot_fit(samples, estimated_mu, estimated_sigma)
    print(f"Plot saved to: {saved_path}")

隐变量的引入

对于单一分布,MLE 通常可以直接求解。然而,许多实际数据的生成机制并非来自单一分布,而是由多个同族但参数不同的分布共同生成。

例子:身高数据与性别隐变量

1. 单一分布的情形

假设你只调查一群男性的身高,记为

x1,x2,…,xn,x_1,x_2,…,x_n,​

可以合理假设它们来自同一个正态分布:

xi∼N(μ,σ2)xix_i∼N(μ,σ^2)x_i

这时用 MLE 直接估计 μ 和 σ2σ^2 就行了,没有任何难度。


2. 实际数据的情形

但现实中,你拿到的数据可能是一群男性和女性混在一起的身高,而且你不知道每个人的性别。

这时数据看起来可能是这样的:

  • 一部分人偏高,集中在 175 cm 左右;

  • 另一部分人偏矮,集中在 162 cm 左右;

  • 整体直方图可能呈现双峰。

如果你硬用一个正态分布去拟合,会得到一个大方差的正态分布,效果很差:

xi​∼N(μ,σ2)x_i​∼N(μ,σ^2)

这个模型无法刻画“两个群体”的结构,我们假设这个数据属于A群体的概率为π1\pi_1,属于B群体的概率为π2\pi_2

推导

这是因为实际上对于一个数据的概率由两个变量决定,但是从数据中实际上没有办法看到这个数据属于哪个群体,数据似然可以写成:

p(xi)=∑k=12πkN(xi∣μk,σk2)p(x_i) = \sum_{k=1}^{2} \pi_k \mathcal{N}(x_i \mid \mu_k, \sigma_k^2)

如果把这个式子套进去,那对数似然的式子就写成了

ℓ(θ)=∑i=1nlog⁡p(xi∣θ)=∑i=1nlog⁡(∑k=1Kπk N(xi∣μk,σk2))\ell(\theta) = \sum_{i=1}^{n} \log p(x_i \mid \theta) = \sum_{i=1}^{n} \log \left( \sum_{k=1}^{K} \pi_k \, \mathcal{N}(x_i \mid \mu_k, \sigma_k^2) \right)

很显然,这个无法解析解,因为有log-sum 形式,但是这个式子是能做一些推导的:

logpθ(x)=log⁡pθ(x,z)pθ(z∣x)=log⁡ppθ(x,z)pθ(z∣x)q(z)q(z)=log⁡pθ(x,z)q(z)+log⁡q(z)pθ(z∣x) log p_\theta(x) = \log p\frac{_\theta(x,z) }{ p_\theta(z|x) }\\ =\log p\frac{p_\theta(x,z) }{p_\theta(z|x) }\frac{q(z) }{q(z) }\\ =\log \frac{p_\theta(x,z) }{q(z) } + \log\frac{ q(z) }{p_\theta(z|x) }

进一步的:

log⁡pθ(x)=log⁡pθ(x)∑zq(z)=∑zq(z)log⁡pθ(x)=∑zq(z)(log⁡pθ(x,z)q(z)+log⁡q(z)pθ(z∣x))=∑zq(z)log⁡pθ(x,z)q(z)+∑zq(z)log⁡q(z)pθ(z∣x)=∑zq(z)log⁡pθ(x,z)q(z)+DKL(q(z)∣∣pθ(z∣x))\log p_\theta(x) =\log p_\theta(x)\sum_zq(z)\\ =\sum_zq(z)\log p_\theta(x)\\ =\sum_zq(z)(\log\frac{ p_\theta(x,z) }{q(z) } + \log\frac{ q(z) }{p_\theta(z|x) }) \\ =\sum_zq(z) \log \frac{p_\theta(x,z) }{q(z) }+\sum_zq(z)\log\frac{q(z) }{ p_\theta(z|x) }\\ =\sum_zq(z)\log \frac{p_\theta(x,z) }{q(z) }+D_{KL}(q(z)||p_\theta(z|x))

最后就是log⁡pθ(x)\log p_\theta(x)被分解为两部分,一个式子+一个散度,KL散度用来描述两个分布的差异性,知乎上有一篇很好的文章,感谢这位作者:深入理解 KL 散度(Kullback-Leibler Divergence):从直觉、数学到前沿应用的全方位解析 - 知乎

因为DKL≥0D_{KL} \ge 0,所以,∑zq(z)log⁡pθ(x,z)log⁡q(z)≤log⁡pθ(x)\sum_zq(z)\frac{\log p_\theta(x,z) }{\log q(z) } \le \log p_\theta(x),定义这个式子为ELBO(证据下界),当且仅当q(z)=pθ(z∣x)q(z)=p_\theta(z|x)的时候可以,不等式取等号,而log⁡pθ(x)\log p_\theta(x)整个式子与z 无关,因此,整个式子的值没有变化,但是在这时候ELBO触碰到了log⁡pθ(x)\log p_\theta(x)因此,此时调整θ 使得ELBO增加,那么log⁡pθ(x)\log p_\theta(x)也会随之抬高。

步骤

所以我们可以分两步处理这个最大化log⁡pθ(x)\log p_\theta(x)问题:

  • E步:令q(z)=pθ(z∣x,θ)q(z)=p_\theta(z|x,\theta) ,根据贝叶斯定理:

P(zi∣xi,θ)=P(xi∣zi,θ) P(zi∣θ)P(xi∣θ)其中P(xi∣θ)=∑ziP(xi∣zi,θ) P(zi∣θ)\begin{equation} P(z_i \mid x_i,\theta) = \frac{P(x_i \mid z_i,\theta)\,P(z_i \mid \theta)}{P(x_i \mid \theta)} \end{equation} \begin{equation} 其中 P(x_i \mid \theta) = \sum_{z_i} P(x_i \mid z_i,\theta)\,P(z_i \mid \theta) \end{equation}

也就是说你需要知道隐变量的分布,隐变量情况下的概率先验,然后设定初始值迭代。

  • M步:确定了z 的分布以后就需要最大化前半部分

对这个式子做一些变形

ELBO=∑zq(z)log⁡pθ(x,z)q(z)=∑zq(z)log⁡pθ(x,z)−∑zq(z)log⁡q(z)=∑zq(z)log⁡pθ(x,z)−H(q)ELBO = \sum_zq(z)\log\frac{p_\theta(x,z) }{q(z) }\\ =\sum_zq(z)\log p_\theta(x,z) - \sum_zq(z)\log{q(z) }\\ = \sum_zq(z)\log p_\theta(x,z) - H(q)

也就是说后半部分是关于q的函数,我们要求的是使得ELBO最大的θ ,后半部分因为没有θ,可以省掉,所以求最大化ELBO等价于,求前半部分式子的最大化,把前半部分定义为Q函数,即求Q函数的最大化。

仔细看看Q 函数:

Q(θ)=∑zq(z)log⁡pθ(x,z)Q(\theta) = \sum_zq(z)\log p_\theta(x,z)

这个式子其实是z的后验分布的期望(带了log),注意,此时的q(z) 已经在E 步固定为了当前的后验分布了。

回看一下,整个EM算法的实质,是当我们引入了一个隐变量,导致最大似然的解析形式不好求积分,因此,我们通过推导,找到了最大似然函数的证据下界,然后我们发现,当我们引入的隐分布变成隐变量的后验分布以后,证据下界此时“贴紧了”最大似然函数,这时候固定后验分布q(z) 然后求θ 使得ELBO最大化,进而推动似然函数增大,然后重复E、M步。

例子

代码