LLM 实践系列—大模型的拒绝采样
拒绝采样是一种蒙特卡洛方法,和重要性采样一样,都是在原始分布难以采样时,用一个易于采样的建议分布进行采样。拒绝采样只是为了解决目标分布采样困难问题,它需要原始分布是已知的。形式描述是这样的:
假设已知原始分布为 $P(x)$,但是从 $P(x)$ 采样较为困难,我们可以找到一个容易采样的建议分布 $Q(x)$。再确定一个常数 $C$,确保任取 x 满足 $P(x) \le C \cdot Q(x)$。然后从 $Q(x)$ 中采样,以 $\frac{P(x)}{C \cdot Q(x)}$ 的概率保留这个样本,得到的采样结果就是服从 $P(x)$ 的样本。
对比来看看拒绝采样和重要性采样,重要性采样的目的是通过易采样的建议分布估算原始分布的期望,目的是为了数值计算,而拒绝采样是为了采样出一批样本。
具体例子
举个具体点的例子,假设现在有已知的分布 $P(x)$ 的概率密度函数如下:
$$ P(x) = \frac{1}{\sqrt{2\pi}} e^{-x^2/2} (1 + 0.5 \sin(5x)) $$
任取 x 我们都可以直接计算出 $P(x)$ 的值,我们也可以画出函数图像。

import matplotlib.pyplot as plt
import numpy as np
def func(x):
y = 1 / np.sqrt(2 * np.pi) * (np.e ** (- x ** 2 / 2)) * (1 + 0.5 * np.sin(5 * x))
return y
x = np.arange(1000) / 100 - 5
y = func(x)
plt.plot(x, y)
plt.show()
现在要采样出 5000 个服从 $P(x)$ 分布的随机样本。Python 肯定没有直接从这个分布采样的函数,我们可以用拒绝采样来实现这个函数。因为 $P(x)$ 中 x 的取值范围是 $(-\infty, +\infty)$,我们找到建议分布也要满足这个要求。我们在 $P(x)$ 中观察到一个正态分布的影子,而 Python 正好也能生成正态分布,那我们就用一个正态分布作为建议分布:
$$ Q(x) = \frac{1}{\sqrt{2\pi}} e^{-x^2/2} $$
现在需要确定常数 C,注意到:
$$ \max(P(x)) = 1.5 $$
最大值是 1.5,那么 C 取 1.5 就能保证分母恒大于等于分子。接下来实现这个采样函数:
def proposal_func(x):
y = 1 / np.sqrt(2*np.pi) * (np.e ** (- x**2 / ))
y
():
samples = []
(samples) < size:
x = np.random.randn()
func(x) / ( * proposal_func(x)) > np.random.random():
samples.append(x)
samples
samples = rejection_sampling()





