人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

 

贝叶斯统计是个神鸟呢?

数学家贝叶斯,在200多年前写的《机会学说中一个问题的解》这本书中提过个观点,他说,支持某项属性的事件发生得愈多,则该属性成立的可能性就愈大。简言之,如果你看到一个人总是做一些好事,那个人多半会是个好人。

很好理解对吧?

下面就从一个简单的例子入手,来进一步理论结合Python学习如何进行贝叶斯统计。

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

抛硬币问题

抛硬币是统计学中的一个经典问题,其描述如下:我们随机抛一枚硬币,重复一定次数,记录正面朝上和反面朝上的次数,根据这些数据,我们需要回答诸如这枚硬币是否公平,以及更进一步这枚硬币有多不公平等问题。抛硬币是一个学习贝叶斯统计非常好的例子,一方面是因为几乎人人都熟悉抛硬币这一过程,另一方面是因为这个模型很简单,我们可以很容易计算并解决这个问题。此外,许多真实问题都包含两个互斥的结果,例如0或者1、正或者负、奇数或者偶数、垃圾邮件或者正常邮件、安全或者不安全、健康或者不健康等。因此,即便我们讨论的是硬币,该模型也同样适用于前面这些问题。

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

为了估计硬币的偏差,或者更广泛地说,想要用贝叶斯学派理论解决问题,我们需要数据和一个概率模型。对于抛硬币这个问题,假设我们已试验了一定次数并且记录了正面朝上的次数,也就是说数据部分已经准备好了,剩下的就是模型部分了。考虑到这是第一个模型,我们会列出所有必要的数学公式,并且一步一步推导。下一章中,我们会重新回顾这个问题,并借用PyMC3从数值上解决它(也就是说那部分不需要手动推导,而是利用PyMC3和计算机来完成)。

通用模型

首先,我们要抽象出偏差的概念。我们称,如果一枚硬币总是正面朝上,那么它的偏差就是1,反之,如果总是反面朝上,那么它的偏差就是0,如果正面朝上和反面朝上的次数各占一半,那么它的偏差就是0.5。这里用参数 θ 来表示偏差,用 y 表示 N 次抛硬币实验中正面朝上的次数。根据贝叶斯定理,我们有如下公式:

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

这里需要指定我们将要使用的先验

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

和似然

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

分别是什么。让我们首先从似然开始。

选择似然

假设多次抛硬币的结果相互之间没有影响,也就是说每次抛硬币都是相互独立的,同时还假设结果只有两种可能:正面朝上或者反面朝上。但愿你能认同我们对这个问题做出的合理假设。基于这些假设,一个不错的似然候选是二项分布:

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

这是一个离散分布,表示 N 次抛硬币实验中 y 次正面朝上的概率(或者更通俗地描述是, N 次实验中, y 次成功的概率)。下面的代码生成了9个二项分布,每个子图中的标签显示了对应的参数:

n_params = [1, 2, 4]
p_params = [0.25, 0.5, 0.75]
x = np.arange(0, max(n_params)+1)
f, ax = plt.subplots(len(n_params), len(p_params), sharex=True, sharey=True)
for i in range(3):
 for j in range(3):
 n = n_params[i]
 p = p_params[j]
 y = stats.binom(n=n, p=p).pmf(x)
 ax[i,j].vlines(x, 0, y, colors='b', lw=5)
 ax[i,j].set_ylim(0, 1)
 ax[i,j].plot(0, 0, label="n = {:3.2f}\np = 
 {:3.2f}".format(n, p), alpha=0)
 ax[i,j].legend(fontsize=12)
ax[2,1].set_xlabel('$\\theta$', fontsize=14)
ax[1,0].set_ylabel('$p(y|\\theta)$', fontsize=14)
ax[0,0].set_xticks(x)

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

二项分布是似然的一个合理选择,直观上讲, θ 可以看作抛一次硬币时正面朝上的可能性,并且该过程发生了 y 次。类似地,我们可以把“1− θ ”看作抛一次硬币时反面朝上的概率,并且该过程发生了“ N − y ”次。

假如我们知道了 θ ,那么就可以从二项分布得出硬币正面朝上的分布。如果我们不知道 θ ,也别灰心,在贝叶斯统计中,当我们不知道某个参数的时候,就对其赋予一个先验。接下来继续选择先验。

选择先验

这里我们选用贝叶斯统计中最常见的 beta分布 ,作为先验,其数学形式如下:

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

仔细观察上面的式子可以看出,除了 Γ 部分之外,beta分布和二项分布看起来很像。 Γ 是希腊字母中大写的伽马,用来表示伽马函数。现在我们只需要知道,用分数表示的第一项是一个正则化常量,用来保证该分布的积分为1,此外,

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

两个参数用来控制具体的分布形态。beta分布是我们到目前为止见到的第3个分布,利用下面的代码,我们可以深入了解其形态:

params = [0.5, 1, 2, 3]
x = np.linspace(0, 1, 100)
f, ax = plt.subplots(len(params), len(params), sharex=True, sharey=True)
for i in range(4):
 for j in range(4):
 a = params[i]
 b = params[j]
 y = stats.beta(a, b).pdf(x)
 ax[i,j].plot(x, y)
 ax[i,j].plot(0, 0, label="$\\alpha$ = {:3.2f}\n$\\beta$ = {:3.2f}".format(a, b), alpha=0)
 ax[i,j].legend(fontsize=12)
ax[3,0].set_xlabel('$\\theta$', fontsize=14)
ax[0,0].set_ylabel('$p(\\theta)$', fontsize=14)
plt.savefig('B04958_01_04.png', dpi=300, figsize=(5.5, 5.5))

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

为什么要在模型中使用beta分布呢?在抛硬币以及一些其他问题中使用beta分布的原因之一是:beta分布的范围限制在0到1之间,这跟我们的参数

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

一样;另一个原因是其通用性,从前面的图可以看出,该分布可以有多种形状,包括均匀分布、类高斯分布、U型分布等。第3个原因是:beta分布是二项分布(前面我们使用了该分布描述似然)的共轭先验。似然的共轭先验是指,将该先验分布与似然组合在一起之后,得到的后验分布与先验分布的表达式形式仍然是一样的。简单说,就是每次用beta分布作为先验、二项分布作为似然时,我们会得到一个beta分布的后验。除beta分布之外还有许多其他共轭先验,例如高斯分布,其共轭先验就是自己。关于共轭先验更详细的内容可以查看https://en.wikipedia.org/wiki/Conjugate_prior。许多年来,贝叶斯分析都限制在共轭先验范围内,这主要是因为共轭能让后验在数学上变得更容易处理,要知道贝叶斯统计中一个常见问题的后验都很难从分析的角度去解决。在建立合适的计算方法来解决任意后验之前,这只是个折中的办法。从下一章开始,我们将学习如何使用现代的计算方法来解决贝叶斯问题而不必考虑是否使用共轭先验。

计算后验

首先回忆一下贝叶斯定理:后验正比于似然乘以先验。

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

对于我们的问题而言,需要将二项分布乘以beta分布:

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

现在,对上式进行简化。针对我们的实际问题,可以先把与 θ 不相关的项去掉而不影响结果,于是得到下式:

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

重新整理之后得到:

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

可以看出,上式和beta分布的形式很像(除了归一化部分),其对应的参数分别为

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

。也就是说,在抛硬币这个问题中,后验分布是如下beta分布:

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

计算后验并画图 现在已经有了后验的表达式,我们可以用Python对其计算并画出结果。下面的代码中,其实只有一行是用来计算后验结果的,其余的代码都是用来画图的:

theta_real = 0.35
trials = [0, 1, 2, 3, 4, 8, 16, 32, 50, 150]
data = [0, 1, 1, 1, 1, 4, 6, 9, 13, 48]

beta_params = [(1, 1), (0.5, 0.5), (20, 20)]
dist = stats.beta
x = np.linspace(0, 1, 100)

for idx, N in enumerate(trials):
 if idx == 0:
 plt.subplot(4,3, 2)
 else:
 plt.subplot(4,3, idx+3)
 y = data[idx]
 for (a_prior, b_prior), c in zip(beta_params, ('b', 'r', 'g')):
 p_theta_given_y = dist.pdf(x, a_prior + y, b_prior + N - y)
 plt.plot(x, p_theta_given_y, c)
 plt.fill_between(x, 0, p_theta_given_y, color=c, alpha=0.6)

 plt.axvline(theta_real, ymax=0.3, color='k')
 plt.plot(0, 0, label="{:d} experiments\n{:d} heads".format(N, y), alpha=0)
 plt.xlim(0,1)
 plt.ylim(0,12)
 plt.xlabel(r"$\theta$") 
 plt.legend()
 plt.gca().axes.get_yaxis().set_visible(False)
plt.tight_layout()
plt.savefig('B04958_01_05.png', dpi=300, figsize=(5.5, 5.5))

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

在上图的第一行中,实验的次数为0,因此第一个图中的曲线描绘的是先验分布,其中有3条曲线,每条曲线分别表示一种先验。

  • 蓝色的线是一个均匀分布先验,其含义是:偏差的所有可能取值都是等概率的。
  • 红色的线与均匀分布有点类似,对抛硬币这个例子而言可以理解为:偏差等于0或者1的概率要比其他值更大一些。
  • 最后一条绿色的线集中在中间值0.5附近,该分布反映了通常硬币正面朝上和反面朝上的概率大致是差不多的。我们也可以称,该先验与大多数硬币都是公平的这一信念是兼容的。“兼容”这个词在贝叶斯相关的讨论中会经常用到,特别是在提及受到数据启发的模型时。

剩余的子图描绘了后续实验的后验分布

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

,回想一下,后验可以看做是在给定数据之后更新了的先验。实验(抛硬币)的次数和正面朝上的次数分别标注在每个子图中。此外每个子图中在横轴0.35附近还有一个黑色的竖线,表示的是真实的 θ ,显然,在真实情况下,我们并不知道该值,在这里标识出来只是为了方便理解。从这幅图中可以学到很多贝叶斯分析方面的知识。

  • 贝叶斯分析的结果是后验分布而不是某个值,该分布描述了根据给定数据和模型得到的不同数值的可能性。
  • 后验最可能的值是根据后验分布的形态决定的(也就是后验分布的峰值)。
  • 后验分布的离散程度与我们对参数的不确定性相关;分布越离散,不确定性越大。
  • 尽管1/2=4/8=0.5,但从图中可以看出,前者的不确定性要比后者大。这是因为我们有了更多的数据来支撑我们的推断,该直觉也同时反映在了后验分布上。
  • 在给定足够多的数据时,两个或多个不同先验的贝叶斯模型会趋近于收敛到相同的结果。在极限情况下,如果有无限多的数据,不论我们使用的是怎样的先验,最终都会得到相同的后验。注意这里说的无限多是指某种程度而非某个具体的数量,也就是说,从实际的角度来讲,某些情况下无限多的数据可以通过比较少量的数据近似。
  • 不同后验收敛到相同分布的速度取决于数据和模型。从前面的图中可以看出,蓝色和红色的后验在经过8次实验之后就很难看出区别了,而红色的曲线则一直到150次实验之后才与另外两个后验看起来比较接近。
  • 有一点从前面的图中不太容易看出来,如果我们一步一步地更新后验,最后得到的结果跟一次性计算得到的结果是一样的。换句话说,我们可以对后验分150次计算,每次增加一个新的观测数据并将得到的后验作为下一次计算的先验,也可以在得到150次抛硬币的结果之后一次性计算出后验,而这两种计算方式得到的结果是完全一样的。这个特点非常有意义,在许多数据分析的问题中,每当我们得到新的数据时可以更新估计值。

先验的影响以及如何选择合适的先验

从前面的例子可以看出,先验对分析的结果会有影响。一些贝叶斯分析的新手(以及一些诋毁该方法的人)会对如何选择先验感到茫然,因为他们不希望先验起到决定性作用,而是更希望数据本身替自己说话!有这样的想法很正常,不过我们得牢记,数据并不会真的“说话”,只有在模型中才会有意义,包括数学上的和脑海中的模型。面对同一主题下的同一份数据,不同人会有不同的看法,这类例子在科学史上有许多,查看以下链接可以了解《纽约时报》最近一次实验的例子: http://www.nytimes.com/interactive/2016/09/20/upshot/the-error-the-polling-world-rarely-talks-about.html?_r=0 。

有些人青睐于使用没有信息量的先验(也称作均匀的、含糊的或者发散的先验),这类先验对分析过程的影响最小。本书将遵循Gelman、McElreath和Kruschke 3人的建议

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

,更倾向于使用带有较弱信息量的先验。在许多问题中,我们对参数可以取的值一般都会有些了解,比如,参数只能是正数,或者知道参数近似的取值范围,又或者是希望该值接近0或大于/小于某值。这种情况下,我们可以给模型加入一些微弱的先验信息而不必担心该先验会掩盖数据本身的信息。由于这类先验会让后验近似位于某一合理的边界内,因此也被称作正则化先验。

当然,使用带有较多信息量的强先验也是可行的。视具体的问题不同,有可能很容易或者很难找到这类先验,例如在我工作的领域(结构生物信息学),人们会尽可能地利用先验信息,通过贝叶斯或者非贝叶斯的方式来了解和预测蛋白质的结构。这样做是合理的,原因是我们在数十年间已经从上千次精心设计的实验中收集了数据,因而有大量可信的先验信息可供使用。如果你有可信的先验信息,完全没有理由不去使用。试想一下,如果一个汽车工程师每次设计新车的时候,他都要重新发明内燃机、轮子乃至整个汽车,这显然不是正确的方式。

现在我们知道了先验有许多种,不过这并不能缓解我们选择先验时的焦虑。或许,最好是没有先验,这样事情就简单了。不过,不论是否基于贝叶斯,模型都在某种程度上拥有先验,即使这里的先验并没有明确表示出来。事实上,许多频率统计学方面的结果可以看做是贝叶斯模型在一定条件下的特例,比如均匀先验。让我们再仔细看看前面那幅图,可以看到蓝色后验分布的峰值与频率学分析中 θ 的期望值是一致的:

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

注意,这里

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

是点估计而不是后验分布(或者其他类型的分布)。由此看出,你没办法完全避免先验,不过如果你在分析中引入先验,得到的会是概率分布分布而不只是最可能的一个值。明确引入先验的另一个好处是,我们会得到更透明的模型,这意味着更容易评判、(广义上的)调试以及优化。构建模型是一个循序渐进的过程,有时候可能只需要几分钟,有时候则可能需要数年;有时候整个过程可能只有你自己,有时候则可能包含你不认识的人。而且,模型复现很重要,而模型中透明的假设能有助于复现。

在特定分析任务中,如果我们对某个先验或者似然不确定,可以自由使用多个先验或者似然进行尝试。模型构建过程中的一个环节就是质疑假设,而先验就是质疑的对象之一。不同的假设会得到不同的模型,根据数据和与问题相关的领域知识,我们可以对这些模型进行比较,本书第6章模型比较部分会深入讨论该内容。

由于先验是贝叶斯统计中的一个核心内容,在接下来遇到新的问题时我们还会反复讨论它,因此,如果你对前面的讨论内容感到有些疑惑,别太担心,要知道人们在这个问题上已经困惑了数十年并且相关的讨论一直在继续。

报告贝叶斯分析结果

现在我们已经有了后验,相关的分析也就结束了。下面我们可能还需要对分析结果进行总结,将分析结果与别人分享,或者记录下来以备日后使用。

模型注释和可视化

根据受众不同,你可能在交流分析结果的同时还需要交流模型。以下是一种简单表示概率模型的常见方式:

  •  
  •  

这是我们抛硬币例子中用到的模型。符号~表示左边随机变量的分布服从右边的分布形式,也就是说,这里 θ 服从于参数为 α 和 β 的Beta分布,而 y 服从于参数为 n = 1和 p = θ 的二项分布。该模型还可以用类似Kruschke书中的图表示成如下形式:

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

在第一层,根据先验生成了 θ ,然后通过似然生成最下面的数据。图中的箭头表示变量之间的依赖关系,符号~表示变量的随机性。

本书中用到的类似Kruschke中的图都是由Rasmus Bååth( http://www.sumsar.net/blog/2013/10/diy-kruschke-style-diagrams/ )提供的模板生成的。

总结后验

贝叶斯分析的结果是后验分布,该分布包含了有关参数在给定数据和模型下的所有信息。如果可能的话,我们只需要将后验分布展示给观众即可。通常,一个不错的做法是:同时给出后验分布的均值(或者众数、中位数),这样能让我们了解该分布的中心,此外还可以给出一些描述该分布的衡量指标,如标准差,这样人们能对我们估计的离散程度和不确定性有一个大致的了解。拿标准差衡量类似正态分布的后验分布很合适,不过对于一些其他类型的分布(如偏态分布)却可能得出误导性结论,因此,我们还可以采用下面的方式衡量。

最大后验密度

一个经常用来描述后验分布分散程度的概念是 最大后验密度 ( Highest Posterior Density,HPD)区间。一个HPD区间是指包含一定比例概率密度的最小区间,最常见的比例是95%HPD或98%HPD,通常还伴随着一个50%HPD。如果我们说某个分析的HPD区间是[2, 5],其含义是指:根据我们的模型和数据,参数位于2~5的概率是0.95。这是一个非常直观的解释,以至于人们经常会将频率学中的置信区间与贝叶斯方法中的可信区间弄混淆。如果你对频率学的范式比较熟悉,请注意这两种区间的区别。贝叶斯学派的分析告诉我们的是参数取值的概率,这在频率学的框架中是不可能的,因为频率学中的参数是固定值,频率学中的置信区间只能包含或不包含参数的真实值。在继续深入之前,有一点需要注意:选择95%还是50%或者其他什么值作为HPD区间的概率密度比例并没有什么特殊的地方,这些不过是经常使用的值罢了。比如,我们完全可以选用比例为91.37%的HPD区间。如果你选的是95%,这完全没问题,只是要记住这只是个默认值,究竟选择多大比例仍然需要具体问题具体分析。

对单峰分布计算95%HPD很简单,只需要计算出2.5%和97.5%处的值即可:

def naive_hpd(post):
 sns.kdeplot(post)
 HPD = np.percentile(post, [2.5, 97.5])
 plt.plot(HPD, [0, 0], label='HPD {:.2f} {:.2f}'.format(*HPD), 
 linewidth=8, color='k')
 plt.legend(fontsize=16);
 plt.xlabel(r'$\theta$', fontsize=14)
 plt.gca().axes.get_yaxis().set_ticks([])


np.random.seed(1)
post = stats.beta.rvs(5, 11, size=1000)
naive_hpd(post)
plt.xlim(0, 1)

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

对于多峰分布而言,计算HPD要稍微复杂些。如果把对HPD的原始定义应用到混合高斯分布上,我们可以得到:

np.random.seed(1)
gauss_a = stats.norm.rvs(loc=4, scale=0.9, size=3000)
gauss_b = stats.norm.rvs(loc=-2, scale=1, size=2000)
mix_norm = np.concatenate((gauss_a, gauss_b))

naive_hpd(mix_norm)
plt.savefig('B04958_01_08.png', dpi=300, figsize=(5.5, 5.5))

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

从上图可以看出,通过原始HPD定义计算出的可信区间包含了一部分概率较低的区间,位于[0,2]。为了正确计算出HPD,这里我们使用了plot_post函数,你可以从本书附带的代码中下载对应的源码:

from plot_post import plot_post
plot_post(mix_norm, roundto=2, alpha=0.05)
plt.legend(loc=0, fontsize=16)
plt.xlabel(r"$\theta$", fontsize=14)

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

从上图可以看出,95%HPD包含两个区间,同时plot_post函数也返回了两个众数。

人人都可以当赌神的秘密:用Python学习神奇的贝叶斯统计

今天到这里 ,喜欢的给小编一个关注 谢谢!

原创文章 22 获赞 16 访问量 1246

猜你喜欢

转载自blog.csdn.net/weixin_46780321/article/details/106042081