首页 / 模块 2 · 概率统计、信息论与最优化 / 第 6 课(共 10 课)

采样——逆变换、重参数化与蒙特卡洛

从零到前沿 ML 自学课程 · 阶段0:数学与工具基础 · 能力点:采样与重参数化——VAE/扩散/RL/LLM 解码的共同操作

上一课我们让三条线汇合:交叉熵 = 最大似然 = 最小化 \(D_{\mathrm{KL}}(p\|q)\)。那一课我们一直在"算期望、算 KL",但有一个问题被悄悄跳过了:这些期望到底怎么算出来? 当分布复杂到积不出闭式时,我们就只能采样(sampling)——造一堆样本,用样本平均替代积分。这一课就讲清楚:怎么从一个分布里造样本,怎么用样本估计期望,以及一个让 VAE / 扩散模型能反向传播的关键花招——重参数化(reparameterization)

读完这一课,你将能够

  • 蒙特卡洛(Monte Carlo)把任意期望写成样本平均,并说出误差为什么按 \(1/\sqrt{N}\) 缩小;
  • 推导并实现逆变换采样,把 \(U\sim\mathrm{Uniform}(0,1)\) 变成指数分布等任意分布的样本;
  • 解释重参数化技巧为什么能让梯度"穿过"采样节点,并手算 \(\partial\mathbb{E}[x]/\partial\mu\);
  • 分清拒绝采样重要性采样MCMC 各自解决什么问题;
  • 用随机种子(seed)让随机实验可复现,并知道 LLM 解码也是一种采样。

一、为什么要采样

采样这件事,在机器学习里干三种活:

一句话直觉:采样把"对整个分布求积分"这件难事,换成"造几个点求平均"这件易事。难点从此转移到——怎么造点,以及造少了误差有多大。这两个问题就是本课的主线。

二、蒙特卡洛估计:用平均代替积分

回忆本模块第1课:期望就是"按概率加权的平均"。连续情形 \(\mathbb{E}[f(X)]=\int f(x)\,p(x)\,dx\)。如果我们能从 \(p\) 里独立采出 \(x_1,\dots,x_N\),那么自然的近似是直接求样本平均:

\[ \mathbb{E}[f(X)] \;\approx\; \hat\mu_N \;=\; \frac{1}{N}\sum_{i=1}^N f(x_i). \]

这就是蒙特卡洛(Monte Carlo, MC)估计。它为什么对?因为大数定律保证 \(\hat\mu_N\) 收敛到真值。但工程上更关心的是:收敛有多快

关键结论——\(1/\sqrt{N}\) 定律。 设 \(\mathrm{Var}[f(X)]=\sigma_f^2\)。因为 \(x_i\) 独立,方差可加: \[ \mathrm{Var}[\hat\mu_N]=\mathrm{Var}\!\left[\frac1N\sum_i f(x_i)\right]=\frac{1}{N^2}\sum_i \mathrm{Var}[f(x_i)]=\frac{\sigma_f^2}{N}. \] 所以估计的标准差(误差的典型大小)是 \[ \mathrm{std}[\hat\mu_N]=\frac{\sigma_f}{\sqrt{N}}. \] 误差按 \(1/\sqrt{N}\) 下降:想把误差减半,样本要翻 4 倍;想多一位有效数字(误差 ÷10),样本要 ×100。
为什么是 \(\sqrt{N}\) 而不是 \(N\)? 误差是 \(N\) 个随机数的。如果它们同向,和会 \(\propto N\);但它们独立、有正有负,互相抵消,和只 \(\propto\sqrt N\)(这正是"随机游走 \(N\) 步离原点约 \(\sqrt N\)"的同一件事)。再除以 \(N\) 取平均,就得到 \(\sqrt N/N=1/\sqrt N\)。这个 \(1/\sqrt N\) 慢得让人心痛,却与维度无关——这正是高维积分宁可用 MC 也不用网格的原因。
Monte Carlo estimate converging to the true value as N grows, with a 1/sqrt(N) error band Monte Carlo convergence: estimate to true value 1.0 0.83 0.67 0.5 10 100 1000 10⁴ 10⁵ N (log scale) estimate E[X] = 2/3 (true value) MC estimate error band ∝ ±σ/√N band width ∝ 1/√N
蒙特卡洛估计随样本数 N 增大向真值收敛,估计值的波动(误差带)按 1/√N 收窄。横轴对数刻度的 N,纵轴估计值,真值为水平参考线。

例题:估计 \(\mathbb{E}[X]\),\(X\sim\mathrm{Exp}(\lambda=1.5)\)

指数分布的真值是 \(\mathbb{E}[X]=1/\lambda=0.6667\)。这里 \(f(x)=x\),它的标准差恰好 \(\sigma_f=\mathrm{std}[X]=1/\lambda=0.6667\)——指数分布的均值和标准差都等于 \(1/\lambda\),纯属巧合,别把这两个"\(0.667\)"搞混(一个是被估的均值,一个是用来算误差量级的标准差)。用固定 seed=0、\(N=2\times10^5\) 个样本(与下面"调一调"同一段代码),得到 \(\hat\mu\approx 0.6645\);某次实现的偏差 \(|0.6667-0.6645|\approx 0.0021\),正好落在理论标准差 \(\sigma_f/\sqrt N=0.667/\sqrt{2\times10^5}\approx 0.0015\) 的量级内(约 1σ)。注意这里 \(0.0021\) 是这一次抽样的偏离,而 \(0.0015\) 是偏离的典型大小(标准差)——换个 seed,单次偏差会在 0 上下随机浮动,但其典型尺度始终是 \(0.0015\)。要看"标准差本身",得像"调一调 1"那样重复很多次再统计:\(N=100\) 时实测约 \(0.065\)、理论 \(0.0667\);\(N=10^4\) 时实测约 \(0.0067\)、理论 \(0.0067\)——\(1/\sqrt N\) 几乎精确成立。

三、逆变换采样:用 CDF 的逆把均匀数"掰"成任意分布

蒙特卡洛要求"能从 \(p\) 采样"。可计算机的随机数发生器只会吐均匀随机数 \(U\sim\mathrm{Uniform}(0,1)\)。怎么把它变成指数分布、高斯分布的样本?

先想机制,别背公式。 回忆本模块第1课的累积分布函数(CDF)\(F(x)=P(X\le x)\):它是一条从 0 单调爬到 1 的曲线。关键观察:CDF 的取值本身就均匀分布在 \([0,1]\)。所以反过来——如果我先在 \(y\) 轴上均匀地取一个高度 \(U\),再沿水平线打到曲线上,竖直落下读出 \(x\),那么曲线陡的地方(概率密度大)落点密,曲线平的地方落点稀——这正好就是 \(p\) 想要的样本分布!

逆变换采样:在 y 轴均匀取 U,水平打到 CDF 曲线再竖直落到 x 轴得样本U1.00x01234F(x)=CDFU≈0.7样本 x = F⁻¹(U)陡段 → x 小且密平段 → x 大且稀
逆变换采样:在 y 轴均匀取高度 U,沿水平线打到 CDF 曲线,再竖直落到 x 轴得到样本。CDF 陡(密度大)处落点密集,平缓处稀疏,正好复现目标分布。
逆变换采样(inverse transform sampling)。 取 \(U\sim\mathrm{Uniform}(0,1)\),令 \[ X = F^{-1}(U), \] 则 \(X\) 服从以 \(F\) 为 CDF 的分布。
一行证明: \(P(X\le x)=P(F^{-1}(U)\le x)=P(U\le F(x))=F(x)\),因为 \(U\) 均匀,\(P(U\le t)=t\)(这一步正是在 \(P(U\le F(x))=F(x)\) 处用到的)。证毕——\(X\) 的 CDF 正是 \(F\)。

例题:推导指数分布的逆变换公式

指数分布 \(\mathrm{Exp}(\lambda)\) 的 CDF 是 \(F(x)=1-e^{-\lambda x}\)(\(x\ge0\))。令 \(U=F(x)=1-e^{-\lambda x}\),解出 \(x\):

\[ 1-U=e^{-\lambda x}\;\Longrightarrow\;\ln(1-U)=-\lambda x\;\Longrightarrow\;\boxed{\,x=-\dfrac{\ln(1-U)}{\lambda}\,}. \]

于是把均匀数 \(U\) 代进去就得到指数样本。小技巧: \(1-U\) 与 \(U\) 同分布(都均匀),所以常直接写 \(x=-\ln(U)/\lambda\)。代码里我们用 \(1-U\) 以忠实对应推导。

易错: 逆变换好看不好用的前提是 \(F^{-1}\) 有闭式。高斯分布的 CDF 是误差函数 \(\mathrm{erf}\),没有初等逆——所以实践中高斯不用逆变换,而用别的方法(Box–Muller,或库函数)。逆变换最适合一维、CDF 可逆的分布。

四、★重参数化技巧:让梯度穿过采样

这是本课的"主菜",也是后面 VAE / 扩散模型反向传播能工作的根。先讲清它解决的痛点。

假设我们有一个高斯 \(\mathcal N(\mu,\sigma^2)\),参数 \(\mu,\sigma\) 是网络要学的。训练目标常常长这样:

\[ L(\mu,\sigma)=\mathbb{E}_{x\sim\mathcal N(\mu,\sigma^2)}[\,f(x)\,]. \]

我们想用反向传播(模块1第10课)求 \(\partial L/\partial\mu\)。但问题来了:计算图里有一个"采样"节点——\(x\) 是从 \(\mathcal N(\mu,\sigma^2)\) 随机抽的。采样是个掷骰子的离散动作,不可导,梯度一到这里就断了,传不回 \(\mu,\sigma\)。

核心花招:把随机性"挪出去"。 与其说"\(x\) 是从一个依赖 \(\mu,\sigma\) 的分布里抽的",不如说——先抽一个与参数无关的标准噪声,再用参数把它平移缩放: \[ x=\mu+\sigma\,\varepsilon,\qquad \varepsilon\sim\mathcal N(0,1). \] 现在随机性全锁在 \(\varepsilon\) 里,而 \(\varepsilon\) 不含 \(\mu,\sigma\)。从 \(\mu,\sigma\) 到 \(x\) 的这一步变成了纯粹的确定性算术(加法、乘法),完全可导!梯度于是能顺着 \(x=\mu+\sigma\varepsilon\) 一路回传到 \(\mu,\sigma\)。
重参数化对比:直接采样梯度断 vs 重参数化梯度通直接采样:梯度断重参数化:梯度通μσsample N(μ,σ²)xf(x)L梯度断在采样节点μσεε~N(0,1) 与参数无关x = μ + σ·ε(确定性)xf(x)L∂x/∂μ=1,∂x/∂σ=ε梯度可穿过
重参数化对比两张计算图。左:x 直接从 N(μ,σ²) 采样,采样节点不可导,梯度(红色虚线)在采样处断开,传不回 μ、σ。右:x=μ+σε,ε 独立于参数,从 μ、σ 到 x 全是确定算术,梯度(绿色实线)可一路回传。
为什么这样就能"求导钻进期望"?看参数藏在哪。 把期望写成积分,参数 \(\mu,\sigma\) 可能出现在两个位置:被积函数里,或概率权重(密度)里。
  • 旧写法 \(\mathbb E_{x\sim\mathcal N(\mu,\sigma^2)}[f(x)]=\int f(x)\,p_{\mu,\sigma}(x)\,dx\):参数藏在权重 \(p_{\mu,\sigma}\) 里。对 \(\mu\) 求导得动到密度本身,而"从这个密度采样"这一步并不是 \(\mu\) 的可微函数——梯度断在采样节点。
  • 重参数化后 \(\mathbb E_{\varepsilon\sim\mathcal N(0,1)}[f(\mu+\sigma\varepsilon)]=\int f(\mu+\sigma\varepsilon)\,\phi(\varepsilon)\,d\varepsilon\):现在权重 \(\phi(\varepsilon)\) 不含参数,参数全挪进了被积函数 \(f(\mu+\sigma\varepsilon)\)。积分区域和权重都与 \(\mu,\sigma\) 无关,于是求导和求期望可以交换次序——梯度直接落到被积函数上,链式法则照常接力。
这就是"梯度能穿过采样"的本质:把参数从不可微的权重,搬到可微的被积函数

具体导数(呼应链式法则,模块1第8课):把 \(\varepsilon\) 当常数,对 \(x=\mu+\sigma\varepsilon\) 求偏导,

\[ \frac{\partial x}{\partial \mu}=1,\qquad \frac{\partial x}{\partial \sigma}=\varepsilon. \]

这两个数清清楚楚、处处可导,链式法则可以接力:\(\dfrac{\partial f}{\partial\mu}=f'(x)\cdot 1\),\(\dfrac{\partial f}{\partial\sigma}=f'(x)\cdot\varepsilon\)。

例题:在重参数化下手算 \(\partial\mathbb{E}[x]/\partial\mu\)

取 \(f(x)=x\),目标 \(L=\mathbb E[x]\)。用重参数化把期望对 \(\varepsilon\) 写:

\[ L=\mathbb E_{\varepsilon\sim\mathcal N(0,1)}[\,\mu+\sigma\varepsilon\,]=\mu+\sigma\,\mathbb E[\varepsilon]=\mu+\sigma\cdot 0=\mu. \]

关键一步:因为现在期望是对不含参数的 \(\varepsilon\) 求的(参数在被积函数里、不在权重里),求导和求期望可以交换次序(梯度直接钻进期望里):

\[ \frac{\partial L}{\partial\mu}=\mathbb E\!\left[\frac{\partial}{\partial\mu}(\mu+\sigma\varepsilon)\right]=\mathbb E[1]=1, \qquad \frac{\partial L}{\partial\sigma}=\mathbb E[\varepsilon]=0. \]

代码用同一批固定 \(\varepsilon\) 做数值微分,得到 \(\partial L/\partial\mu\approx 1.000\)、\(\partial L/\partial\sigma\approx 0\),与手算吻合。

ML 和 ML 的联系

这正是 VAE(变分自编码器)能用反向传播训练的原因:编码器输出每个隐变量的 \(\mu,\sigma\),要从 \(\mathcal N(\mu,\sigma^2)\) 采隐编码再喂给解码器。直接采样会切断梯度;写成 \(z=\mu+\sigma\varepsilon\) 后,梯度就能穿过采样、回到编码器。扩散模型的前向加噪 \(x_t=\sqrt{\bar\alpha_t}\,x_0+\sqrt{1-\bar\alpha_t}\,\varepsilon\) 是同一个套路的另一张脸。(这两个埋点,等到模块讲生成模型时会正式回收。)对比一下:当随机量没法写成"参数 + 与参数无关的噪声"这种可微变换时(典型如策略梯度里的离散动作 \(a\),但连续动作同样可能落在这一类),重参数化就用不上,只能换一套更通用、但方差通常更大的办法——REINFORCE / 对数导数技巧,那是后面 RL 的故事。

五、采样工具箱的其余三件:拒绝、重要性、MCMC

拒绝采样(rejection sampling)——"撒点,扔掉超出去的"

想从一个只知道形状 \(p(x)\)(甚至没归一化)的分布采样,但 \(F^{-1}\) 求不出来。办法:找一个容易采样的"罩子" \(M\,q(x)\ge p(x)\),从 \(q\) 采一个候选 \(x\),再以概率 \(p(x)/(M q(x))\) 接受它,否则扔掉重来。直觉上,当罩子 \(q\) 本身是均匀分布时,这就像往罩子覆盖的矩形里均匀撒点,落在 \(p\) 曲线下面的留、上面的扔,留下的点就服从 \(p\)(下面练习 3 正是这个最简特例)。\(q\) 非均匀时,"均匀撒点"的图景要换成"先按 \(q\) 撒、再按比值接受",但留下的样本同样服从 \(p\)。缺点:罩子套得松(\(M\) 大)时拒绝率高、很浪费,高维尤其糟。

重要性采样(importance sampling)——"借别人的样本,按权重折算"

有时我们根本不需要 \(p\) 的样本,只想算 \(\mathbb E_p[f(X)]\);偏偏从 \(p\) 采样很难,但有个好采样的 \(q\)。做个恒等变形:

\[ \mathbb E_p[f(X)]=\int f(x)\,p(x)\,dx=\int f(x)\,\frac{p(x)}{q(x)}\,q(x)\,dx=\mathbb E_q\!\left[f(X)\,\frac{p(X)}{q(X)}\right]. \]

于是从 \(q\) 采样,每个样本乘上重要性权重 \(w(x)=p(x)/q(x)\) 再平均即可:

\[ \mathbb E_p[f(X)]\approx\frac1N\sum_{i=1}^N f(x_i)\,\frac{p(x_i)}{q(x_i)},\qquad x_i\sim q. \]

直觉:\(q\) 在某处采多了(相对 \(p\)),权重 \(p/q<1\) 就把它调低;采少了就调高校正"用错了分布采样"这件事。

重要性采样:从提议分布 q 采样,用权重 w=p/q 折算回目标 p 的期望xw 大(调高)p 高 · q 低w 小(调低)p 低 · q 高p(x)目标 N(3,1)q(x)提议 N(0,2²)q 尾巴需覆盖 pEp[f] = Eq[ f · p/q ], w = p/q
重要性采样:从易采样的 q(提议分布)采点,用权重 w=p/q 折算回目标 p 的期望。p、q 两条曲线 + 权重比值示意。
易错: 重要性采样的方差对 \(q\) 极其敏感。若某处 \(p\) 大而 \(q\) 几乎为 0,权重 \(p/q\) 会爆炸,少数样本主宰整个估计,结果方差巨大甚至失效。经验法则:\(q\) 的尾巴要比 \(p\) 厚(\(q\) 覆盖 \(p\) 的所有高概率区)。这个"分布不匹配导致权重爆炸"的现象,正是 RL 里离策略(off-policy)方法要对概率比 \(p/q\) 加约束的根源——埋个点。

MCMC(马尔可夫链蒙特卡洛)——高维相关采样

当分布是高维、强相关、且只知道未归一化形状时,前面几招都失灵。MCMC 换个思路:不直接采独立样本,而是造一条随机游走的链,让它的长期停留分布恰好是目标 \(p\)。Metropolis–Hastings:从当前点提议一个邻近点,按 \(\min(1,\,p_{\text{新}}/p_{\text{旧}})\) 的概率接受(注意只用到比值,未知的归一化常数在分子分母上下相消,自动消掉!)。Gibbs 采样:每次固定其他维、只按条件分布更新一维,轮流刷。代价是相邻样本相关、需要"烧入期(burn-in)"、且要判断收敛——这一块我们只点到为止。

方法解决什么代价/限制
逆变换一维、CDF 可逆,精确独立采样需要闭式 \(F^{-1}\)
重参数化让梯度穿过采样(可训练)需把分布写成 \(g(\theta,\varepsilon)\),离散变量难办
拒绝采样有形状、无 \(F^{-1}\) 的分布高维拒绝率高、浪费
重要性采样\(p\) 难采但只想估期望\(q\) 选不好则方差爆炸
MCMC高维、相关、未归一化样本相关、需收敛判断

六、随机种子与可复现,以及 LLM 解码也是采样

所有这些方法都吃随机数。为了让实验可复现(reproducible),要固定随机种子(seed):同一个 seed 喂给伪随机数发生器,吐出的随机序列完全一样。本课代码统一用 np.random.default_rng(seed) 建一个独立的发生器——比全局 np.random.seed 更干净、不会被别处偷偷改状态。

埋点:LLM 解码就是采样。 大模型每一步输出一个词表上的概率分布,"生成下一个 token"就是从这个分布里采一个样本。你调的那几个旋钮全是采样参数:temperature \(T\) 把 logits 除以 \(T\) 再 softmax——\(T\) 小分布变尖(更确定、更死板),\(T\) 大变平(更随机、更发散);top-k 只在概率最高的 \(k\) 个候选里采;top-p(核采样)先把候选按概率降序排,取累积概率刚达到 \(p\) 的最小前缀集,重新归一化后再在这个集合里采。固定 seed + \(T=0\)(取 argmax)就能让 LLM 输出确定可复现。这块等讲 Transformer 解码时正式展开。

调一调,观察现象

下面每个小实验都只用 numpy + print,几秒跑完。先猜,再跑,对不上就回头想机制。

调一调 1:把 N 翻 4 倍,看误差减半

改什么 → 对 \(N\in\{100,400,1600,6400\}\) 各重复 2000 次估计 \(\mathbb E[X]\),记录估计的标准差。
预期看到 → 每翻 4 倍,标准差大约减半(相邻比值≈2.0),且与理论列 \(\sigma_f/\sqrt N\) 逐行吻合。(注意:每个标准差本身也是出来的,重复次数有限会带来 ±几个百分点的抖动;重复次数从 200 提到 2000 后,比值才稳稳收敛到 2.0——所以看整体趋势,而非苛求每一步精确减半。)
为什么 → 误差 \(\propto 1/\sqrt N\),\(N\times4\Rightarrow\sqrt N\times2\Rightarrow\)误差 \(\div2\)。

import numpy as np
rng = np.random.default_rng(0)
lam = 1.5
for N in [100, 400, 1600, 6400]:
    ests = []
    for _ in range(2000):                 # 重复多次让 std 估计更稳
        U = rng.uniform(0, 1, N)
        X = -np.log(1 - U) / lam          # 逆变换采样
        ests.append(X.mean())
    print(f"N={N:>5}  std(estimate)={np.std(ests):.4f}  "
          f"1/sqrtN*sigma={(1/lam)/np.sqrt(N):.4f}")

调一调 2:逆变换造的指数样本 vs 理论密度

改什么 → 用逆变换造 \(2\times10^5\) 个指数样本,分箱算经验密度,和 \(\lambda e^{-\lambda x}\) 对比。
预期看到 → 两列数几乎逐格吻合(如 \(x\approx0.25\) 处经验≈1.06、理论≈1.03,\(x\approx1.25\) 处经验≈0.23、理论≈0.23);近处的小偏差正是有限样本的统计涨落。
为什么 → 逆变换在数学上精确给出目标分布,只剩有限样本的统计涨落。

import numpy as np
rng = np.random.default_rng(0)
lam = 1.5
U = rng.uniform(0, 1, 200000)
X = -np.log(1 - U) / lam
edges = np.linspace(0, 5, 11)
counts, _ = np.histogram(X, bins=edges, density=True)
centers = 0.5 * (edges[:-1] + edges[1:])
theory = lam * np.exp(-lam * centers)
print("x      :", np.round(centers, 2))
print("empir. :", np.round(counts, 3))
print("theory :", np.round(theory, 3))

调一调 3:固定一批 ε,挪动 μ、σ,看 x 平滑可导地变

改什么 → 固定 5 个 \(\varepsilon\),让 \(\mu\) 从 0 走到 2、\(\sigma\) 变化,打印 \(x=\mu+\sigma\varepsilon\) 和数值梯度。
预期看到 → 每行 \(x\) 随 \(\mu\) 整体平移、平滑变化;数值 \(\partial\mathbb E[x]/\partial\mu\approx1.000\);\(\partial\mathbb E[x]/\partial\sigma\) 不会精确等于 0,而是等于这一批 \(\varepsilon\) 的样本均值 \(\overline\varepsilon\)(本例 seed=1 那批约 \(-0.0046\))——它只在样本量 \(\to\infty\) 时趋于真值 \(\mathbb E[\varepsilon]=0\)。
为什么 → 随机性锁在固定的 \(\varepsilon\) 里,\(x\) 对 \(\mu,\sigma\) 是确定光滑函数,故可导、梯度可穿过;而 \(\partial L/\partial\sigma=\frac1N\sum_i\varepsilon_i=\overline\varepsilon\),正是该批噪声的样本均值。

import numpy as np
rng = np.random.default_rng(1)
eps = rng.standard_normal(5)
print("fixed eps:", np.round(eps, 3))
sigma = 1.0
for mu in [0.0, 1.0, 2.0]:
    print(f"mu={mu}: x =", np.round(mu + sigma * eps, 3))

# 同一批大 eps 上做数值微分,验证 dE[x]/dmu=1, dE[x]/dsigma=mean(eps)
eps = rng.standard_normal(100000)
mu, sigma, h = 2.0, 0.5, 1e-3
base = (mu + sigma * eps).mean()
print("dE/dmu    ~", round(((mu + h + sigma * eps).mean() - base) / h, 4))
print("dE/dsigma ~", round(((mu + (sigma + h) * eps).mean() - base) / h, 4),
      " (mean(eps)=", round(eps.mean(), 4), ")")   # 两数应相等

动手练习

  1. 逆变换造别的分布。 标准 Logistic 分布的 CDF 是 \(F(x)=\frac{1}{1+e^{-x}}\)(就是 sigmoid!)。推导 \(F^{-1}(U)\),并用下面骨架采样、对比经验均值≈0。
    import numpy as np
    rng = np.random.default_rng(0)
    U = rng.uniform(0, 1, 100000)
    X = np.log(U / (1 - U))   # F^{-1}(U) = logit(U),自己推一遍
    print("mean:", round(X.mean(), 3), " median:", round(np.median(X), 3))
    
  2. 重要性采样动手。 估计 \(\mathbb E_p[X^2]\),其中 \(p=\mathcal N(3,1)\)(真值 \(=\mu^2+\sigma^2=10\)),但只从 \(q=\mathcal N(0,2^2)\) 采样。补全权重 \(p/q\)。
    import numpy as np
    rng = np.random.default_rng(1)
    p = lambda x: np.exp(-0.5*(x-3)**2)/np.sqrt(2*np.pi)
    q = lambda x: np.exp(-0.5*(x/2)**2)/(2*np.sqrt(2*np.pi))
    xs = rng.normal(0, 2, 200000)
    w  = p(xs) / q(xs)                 # 重要性权重
    print("IS est:", round(np.mean(w * xs**2), 3), " (theory 10)")
    
    现在把 \(q\) 改成 \(\mathcal N(0,0.5^2)\)(尾巴太薄),观察估计变得多么不稳——体会"权重爆炸"。
  3. 拒绝采样最小实现。 从三角形密度 \(p(x)=2x,\ x\in[0,1]\) 采样:用 \(q=\mathrm{Uniform}(0,1)\)、罩子 \(M=2\)。提议 \(x\sim U(0,1)\),再抽 \(u\sim U(0,1)\),当 \(u
  4. 验证 \(1/\sqrt N\) 的斜率。 把调一调 1 的 \((\,\ln N,\ \ln\mathrm{std}\,)\) 打印出来,估计斜率,应接近 \(-0.5\)。
  5. 种子可复现。 用相同 seed 跑两次同一段采样,验证两次结果逐位相同;换 seed 则不同。

掌握自检

下一课我们正式进入最优化:凸性与梯度下降的几何——为什么沿负梯度走能下山,Hessian \(H\) 的特征值/条件数(呼应模块1)又如何决定下山的快慢与形状。本课的采样会和它在后面的随机优化、变分推断里重逢。