R——概率统计与模拟(四)拒绝抽样

原创:hxj7

本文介绍了拒绝抽样(Reject Sampling)。

前文《R-概率统计与模拟(三)变换均匀分布对特定分布进行抽样》介绍了通过“变换均匀分布”来对特定分布进行抽样的方法,但是该方法需要知道累积分布的解析表达式及其反函数,所以有一定的限制。

其实,我们最常接触的还是 \text{p.d.f.},根据\text{p.d.f.}抽样往往更直接。比如,均匀分布的\text{p.d.f.}就很简单,对其抽样也很方便。但是,对于一些复杂的\text{p.d.f.},直接抽样是不可取的,需要用其它方法。拒绝抽样就是其中一种。

下文分为五个部分:

  1. 拒绝抽样简介
  2. 利用拒绝抽样方法对\text{Gamma}分布抽样
  3. 如何利用rand7对rand10抽样
  4. 蓄水池抽样算法
  5. 对拒绝抽样的补充说明

拒绝抽样简介

所谓拒绝抽样,是针对一个已知\text{p.d.f.}的概率分布进行抽样的方法。具体步骤如下:

  1. 假设我们要对一个分布进行随机抽样,已知其\text{p.d.f.}p(x)
  2. p(x)可能比较复杂以至于直接抽样有困难,我们可以选择一个容易抽样的分布,假设该分布\text{p.d.f.}q(x),再选择一个足够大的数M,使得Mq(x)始终比p(x)大。
  3. 根据q(x)抽取一个值x_i
  4. [0,1]均匀分布中抽取一个数u_i,如果u_i < \frac{p(x_i)}{Mq(x_i)}, 那么接受x_i这个采样值,否则拒绝它。
  5. 重复第3步和第4步,知道采样的数量达到预定要求。

通过这些步骤获取到的采样值就是符合p(x)分布的。需要注意的是M的取值,如果M过大,会导致抽样过程中被拒绝的点增多,降低采样点的接受率。事实上,我们可以证明采样点的接受率是\frac{1}{M},所以M的取值应该在保证Mq(x) > p(x), \forall x的前提下尽可能得小。(相关的证明在文末。)


像上图一样,的取值要保证。

利用拒绝抽样方法对\text{Gamma}分布抽样

上面的步骤比较抽象,我们举一个实际的例子来说明:我们怎么通过拒绝抽样的方法对\text{Gamma}(x, \alpha, 1)进行抽样?
(选这个例子是因为它也用到了我们在前文提到的“变换均匀分布”的方法。)

我们对照上面的步骤一步一步来说:

  1. 我们知道,目标分布\text{Gamma}(x, \alpha, 1)\text{p.d.f.}是:
    p(x) = \frac{e^{-x}x^{\alpha -1}}{\Gamma(\alpha)}
  2. 显然p(x)比较复杂,不易直接采样。我们选择一个较容易采样的分布,其\text{p.d.f.}是:
    q(x) = \frac{\lambda \alpha^{\lambda} x^{\lambda-1}}{(\alpha^\lambda + x^\lambda)^2},
    我们选择M
    M = \frac{4e^{-\alpha} \alpha^\alpha}{\lambda \Gamma(\alpha)},
    所以:
    Mq(x) = \frac{4e^{-\alpha} \alpha^{\lambda + \alpha} x^{\lambda-1}}{\Gamma(\alpha) (\alpha^\lambda + x^\lambda)^2}.
    可以证明,当\alpha > 1 并且 1 \le \lambda \le \sqrt{2\alpha - 1} 时,对所有x都有p(x) < Mq(x)。并且可以看出,当\lambda取值不同时,q(x)M 以及 Mq(x) 都是在变化的,也会影响到接受率。
  3. 根据q(x)抽取一个值x_i。看起来q(x)也是蛮复杂的,怎么对其抽样呢?我们可以通过变换均匀分布的方法来实现:
    我们从[0,1]的均匀分布中抽取一个值r_i,然后令
    x_i = \alpha \left(\frac{r_i}{1-r_i} \right)^{1 / \lambda}
    这种通过变换均匀分布的方式得到的x_i等价于从q(x)中抽取x_i
  4. [0,1]均匀分布中抽取一个数u_i,如果u_i < \frac{p(x_i)}{Mq(x_i)}, 那么接受x_i这个采样值,否则拒绝它。
  5. 重复第3步和第4步,知道采样的数量达到预定要求。

上文已经提及,\lambda取值不同会影响M值,从而影响到接受率。为了验证这一点,笔者写了一段代码来做一个实验:
假设我们让\alpha=5,再分别让\lambda=3以及\lambda=1来看看\lambda取值不同对接受率的影响。
代码如下:

# Step1. p(x)函数
px <- function(x, a) dgamma(x, shape=a, scale=1)  

# Step2. q(x)函数
qx <- function(x, a, l) a ^ l * l * x ^ (l - 1) / ((a ^ l + x ^ l ) ^ 2)  
getM <- function(a, l) 4 * exp(-a) * a ^ a / (gamma(a) * l)  # 计算M值
mqx <- function(x, a, l, M) M * qx(x, a, l)     # Mq(x)函数

# Step3. 变换均匀分布以便对q(x)抽样的函数
transUnif <- function(x, a, l) a * (x / (1 - x)) ^ (1 / l)

# 单纯为了画p(x)的理论曲线用的函数
px.1 <- function(x) px(x, alpha)  
# 单纯为了画lambda=lambda.1时Mq(x)的理论曲线用的函数
mqx.1 <- function(x) mqx(x, alpha, lambda.1, M.1)  
# 单纯为了画lambda=lambda.2时Mq(x)的理论曲线用的函数
mqx.2 <- function(x) mqx(x, alpha, lambda.2, M.2)

ogamma <- function(a, l, M) {
  while (T) {
    x <- runif(1, 0, 1)
    y <- transUnif(x, a, l)   # 变换均匀分布实现对q(x)的抽样
    u <- runif(1, 0, 1) 
    if (u < px(y, a) / mqx(y, a, l, M))   # Step4. 是否接受采样点
      break
    nfail <<- nfail + 1   # 记录被拒绝的次数
  }
  y
}

sgamma <- function(n, a, l, M) {
  set.seed(123)
  replicate(n, ogamma(a, l, M))
}

N <- 100000
alpha <- 5

# 第一种lambda值的效果
lambda.1 <- sqrt(2 * alpha - 1)
M.1 <- getM(alpha, lambda.1)
nfail <- 0
res.1 <- sgamma(N, alpha, lambda.1, M.1)
nfail / (nfail + N)   # 计算模拟的拒绝率,14.38%
1 - 1 / M.1         # 计算理论的拒绝率,14.51%
plot(density(res.1), xlim=c(0, 20), col="red", xlab="x",
    main=paste0("Reject Sampling for Gamma(x, ", alpha, ", 1)\nwith lambda=", lambda.1))
curve(px.1, 0, 20, col="blue", add=T, lty=2)
curve(mqx.1, 0, 20, col="black", add=T, lty=3)
legend("topright", legend=c("simulative", "theoretical p(x)", "theoretical Mq(x)"),
       col=c("red", "blue", "black"), lty=c(1,2,3), bty="n")

# 第二种lambda值的效果
lambda.2 <- 1
M.2 <- getM(alpha, lambda.2)
nfail <- 0
res.2 <- sgamma(N, alpha, lambda.2, M.2)
nfail / (nfail + N)   # 计算模拟的拒绝率,71.54%
1 - 1 / M.2          # 计算理论的拒绝率,71.50%
plot(density(res.2), xlim=c(0, 20), ylim=c(0, 0.7), col="red", xlab="x",
     main=paste0("Reject Sampling for Gamma(x, ", alpha, ", 1)\nwith lambda=", lambda.2))
curve(px.1, 0, 20, col="blue", add=T, lty=2)
curve(mqx.2, 0, 20, col="black", add=T, lty=3)
legend("topright", legend=c("simulative", "theoretical p(x)", "theoretical Mq(x)"),
       col=c("red", "blue", "black"), lty=c(1,2,3), bty="n")

\alpha=5, \lambda=3时,代码模拟出的接受率是85.62\%,理论上的接受率是\frac{1}{M}=85.49\%,很接近。采样点的具体分布如下:


当时,代码模拟出的接受率是,理论上的接受率是,也很接近。采样点的具体分布如下:

我们可以看出,当分别取和时,接受率从下降到了。所以,对接受率的影响是非常大的。

如何利用rand7对rand10抽样

这来源于一个常见的算法题:如果已知一个rand7()函数,它可以从1到7这7个数字中随机地、等概率地抽取一个数,那么如何利用rand7()函数实现rand10()函数呢?所谓rand10()函数,就是这个函数可以从1到10这10个数字中随机地、等概率地抽取一个数。

这个问题的一个常见解答步骤是:

  1. 首先利用[rand7() - 1] \times 7 + rand7()这个表达式实现从1-49这49个数中随机而等概率地抽取一个数x
  2. 如果x \le 40,那么接受x,并将x \% 10 + 1的值作为一个采样点;否则拒绝x;那么这样得到的一系列采样点就是符合预期要求的。

这个解答步骤为什么是正确的,证明也很简单,和文末的证明过程类似,所以在此略过。那为什么要介绍这个题目呢?因为我们看到抽样过程中也涉及到了“接受-拒绝”的过程,所以笔者认为这可以看作文初所述的拒绝抽样过程的一种变型。

这个问题的代码也很简单:

rand7 <- function() {
  sample(7, 1)    # 从1-7中随机抽取一个整数
}

rand10 <- function() {
  while (T) {
    x <- (rand7() - 1) * 7 + rand7()   # 1-49 uniformly.
    if (x <= 40)
      break
  }
  x %% 10 + 1    # 一个服从均匀分布的rand10的采样值
}

N <- 100000  # 样本大小
set.seed(123)
res <- replicate(N, rand10())
hist(res, prob=T, breaks = 0:10, ylim=c(0, 0.15), xlab="x",
     main="Simulation of rand10")
abline(h=0.1, col="red", lty=2)

结果如下:



从上图中可以看出,rand10()的抽样结果符合均匀分布,达到预期。

蓄水池抽样算法

如同上面rand10()的题目一样,蓄水池算法的实现过程中也涉及到了“接受-拒绝”过程,所以在此一并介绍:

所谓蓄水池抽样算法,主要应用于如下问题:即我们要从1,2,\ldots,NN个样本中随机抽取m个样本,N非常大或者未知,且m << N
蓄水池抽样算法的步骤是:

  1. 首先将1,2,\ldots,mm个样本选进来;
  2. i=m+1开始,从[1, i]中随机等概率抽取一个数r,如果1 \le r \le m,那么用第i个样本替代第r个样本。否则不做处理
  3. i=i+1
  4. 重复第2步和第3步,直至处理完全部的样本。

其证明过程也很简单,用归纳法即可。笔者曾经写过一篇文章介绍了该算法在测序数据reads抽取方面的应用:《算法(二)蓄水池抽样算法快速随机抽取reads》。

对拒绝抽样的补充说明

为什么按照拒绝抽样的过程进行抽样所得到的采样点就是符合p(x)的分布的?

证明:
首先我们给出一些记号:假设我们将目标分布p(x)中的点记为\hat{X},从q(x)中抽取的任意一个值记为X;并且让事件A代表X这个值被接受了。如果当X=x_i时被接受了,那么就是\hat{X}=X=x_i

有了这些记号,我们可以很容易地知道:
\Pr(A|X=x_i)=\Pr \left[u_i < \frac{p(x_i)}{Mq(x_i)} \right] = \frac{p(x_i)}{Mq(x_i)}

利用全概率公式,我们知道:
\begin{aligned} \Pr(A) & = \int \Pr(A|X=x_i) \, q(x_i) {\rm d}x_i \\ & = \int \frac{p(x_i)}{Mq(x_i)}q(x_i) {\rm d}x_i \\ & = \frac{1}{M} \int p(x_i) {\rm d}x_i \\ & = \frac{1}{M} \end{aligned}

最终,我们想要知道的\hat{X}的分布:
\Pr(\hat{X} \le x_i) = \Pr(X \le x_i|A)

利用贝叶斯公式,我们有:
\begin{aligned} \Pr(\hat{X} \le x_i) & = \Pr(X \le x_i|A) \\ & = \frac{\Pr(X \le x_i, A)}{\Pr(A)} \\ & = \frac{\displaystyle \int^{x_i} q(t)\frac{p(t)}{Mq(t)} {\rm d}t}{\Pr(A)} \\ & = \cfrac{\cfrac{1}{M} \displaystyle \int^{x_i} p(t) {\rm d}t}{\cfrac{1}{M}} \\ & = \int^{x_i} p(t) {\rm d}t \end{aligned}

所以可以证明,依照拒绝抽样过程得到的采样点符合p(x)分布。

拒绝采样的接受率如何计算?

从上面的证明过程中可以看出,接受率就是\Pr(A),等于\frac{1}{M}。上述证明过程是笔者按照自己的理解写的,如果有错误,请朋友们指正。

小结

本文介绍了拒绝采样,并以\text{Gamma}分布、rand10以及蓄水池抽样算法等三个例子加以说明。拒绝采样不同于变换均匀分布的方法,它是直接根据\text{p.d.f.}进行抽样。其缺点是要找到合适的q(x)M值并不容易,尤其是在高维的情况下,M值往往偏大,从而导致拒绝率很高。

参考

《生物序列分析》第11章

(公众号:生信了)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,717评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,501评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,311评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,417评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,500评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,538评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,557评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,310评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,759评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,065评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,233评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,909评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,548评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,172评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,420评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,103评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,098评论 2 352