贝叶斯思维:统计建模的Python学习法

978-7-115-38428-7
作者: 【美】Allen B. Downey
译者: 许杨毅
编辑: 王峰松

图书目录:

详情

贝叶斯统计学方法正变得日益重要和日益普及。但是市场上适合初学者的资源很少。本书基于作者在大学讲授的课程,可以帮助读者获得一个良好的开端,诸如利用Python编程,处理统计学中的估值,预测,决策分析,假设检验等问题。书中包含掷骰子等简单的例子,也有解决现实问题的实际算例。

图书摘要

版权信息

书名:贝叶斯思维:统计建模的Python学习法

ISBN:978-7-115-38428-7

本书由人民邮电出版社发行数字版。版权所有,侵权必究。

您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。

我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。

如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。

• 著    [美] Allen B. Downey

  译    许杨毅

  责任编辑 王峰松

• 人民邮电出版社出版发行  北京市丰台区成寿寺路11号

  邮编 100164  电子邮件 315@ptpress.com.cn

  网址 http://www.ptpress.com.cn

• 读者服务热线:(010)81055410

  反盗版热线:(010)81055315


Copyright ©2013 by O’Reilly Media, Inc.

Simplified Chinese Edition, jointly published by O’Reilly Media, Inc. and Posts & Telecom Press, 2014. Authorized translation of the English edition, 2013 O’Reilly Media, Inc., the owner of all rights to publish and sell the same.

All rights reserved including the rights of reproduction in whole or in part in any form.

本书中文简体版由O’Reilly Media, Inc.授权人民邮电出版社出版。未经出版者书面许可,对本书的任何部分不得以任何方式复制或传播。

版权所有,侵权必究。


这本书旨在帮助那些希望用数学工具解决实际问题的人们,仅有的要求可能就是懂一点概率知识和程序设计。贝叶斯方法是一种常见的利用概率学知识去解决不确定性问题的数学方法,对于一个计算机专业人士,应当熟悉其在诸如机器翻译、语音识别、垃圾邮件检测等常见的计算机领域的应用。

本书实际上会扩大你的视野,即使不是一个计算机专业人士,你也可以看到在战争环境下(第二次世界大战德军坦克问题),法律问题上(肾肿瘤的假设验证),体育博彩领域中(棕熊队和加人队NHL比赛问题)贝叶斯方法的威力。怎么从有限的信息判断德军装甲部队的规模?你所支持的球队有多大可能赢得冠军?在《龙与地下城》勇士中,你应当对游戏角色属性的最大值有怎样的预期?甚至在普通的彩弹射击游戏中,拥有一些贝叶斯思维也能帮助你提高游戏水平。

除此以外,本书在共计15章的篇幅中讨论了怎样解决十几个现实生活中的实际问题。在这些问题的解决过程中,作者还潜移默化地帮助读者形成了建模决策的方法论,建模误差和数值误差怎么取舍,怎样为具体问题建立数学模型,如何抓住问题中的主要矛盾(模型中的关键参数),再一步一步地优化或者验证模型的有效性或者局限性。在这个意义上,这本书又是一本关于数学建模的成功样本。


很多人把世界理解为基于简单的、确定的,非一即零、非黑即白的。但是真实的世界却是非常复杂的,不是一两个公式可以完美总结概括的。就像我们的高考成绩和我们的学习水平,确实有很大的联系,但是最后又会受到很多因素的影响(比如身体状况,是否休息好了,心情,天气等),进而使得我们的最终成绩在真实水平上下有很大的波动。这就像我们分析很多事情时,经常得到的结论,“既有必然性,又有偶然性”。

这个时候,基于概率和统计的方法给了我们很多的帮助。很多时候,我们不能给出每一个人、每一件事的确定结果。但是当我们观察大量的相同事件后,我们就会发现从一个集体的意义上的规律是存在的。而单个事件每次可能得到不同的结果,这些结果以最有可能的结果为中心,服从一定的概率分布。了解这些分布数据,使我们更加容易理解和预期真实世界的多边形。

回顾在进行计算机自然语言处理过程中走过的路,我们就会发现从研究规则到研究统计的转变。最初,研究人员都认为,语言是基于语法规则。这个也很容易理解,因为我们学习语言的时候,总是背单词,学语法,然后掌握语言。基于这种思维,自然语言处理经历了多年的发展后,遇到了巨大的挑战。那就是即便语法规则已经非常复杂,仍然不能处理大多数的语言情况。从结果上而言,自然语言处理的准确度远低于人类,不具有真正的使用价值。而后,有一批学者开始另辟蹊径,基于统计的思路进行探索。如果语言是根据人类沟通需求自然发生,然后才有总结出来的语法呢?基于这种思想,研究人员放弃语法规则,开始建立基于统计的模型。他们使用了大量的真实文本数据,分析每个词和它前后的词出现的统计关系,用贝叶斯方法以及马尔科夫过程,建立了新的自然语言处理模型。这一次,语言处理准确率有了巨大的提升,进而达到可以实用的要求。今天,当我们使用谷歌翻译、苹果的Siri语音服务的时候,后面都有基于统计的模型的功劳。

还有很多的真实世界的事情都是这样的,比如路上的交通是否阻塞、银行排队的时间、球赛的比赛结果,都是以一种概率的形式出现的。了解贝叶斯方法,也是了解真实世界运行的一种有效途径。本书中也列举了很多的真实实例来告诉我们,贝叶斯方法和真实世界的联系。

另外,在我们正在经历的大数据时代,作为数据分析方法的一个巨大分支,基于贝叶斯的机器学习算法也在被广泛地使用,并产生很多实际意义。比如简单贝叶斯算法、贝叶斯信念网络等,被广泛地应用于分类和预测。对于海量数据的文本分类问题,例如,垃圾邮件的甄选和过滤,基于贝叶斯方法的算法取得了非常好的效果,并在很多公司中正在使用,帮助我们远离垃圾邮件的骚扰。

更加难能可贵的是,本书作者用相对简单的Python语言,对所涉及的实例进行了编程。对于有一定计算机基础的人来说,通过程序,可以进一步理解贝叶斯方法的应用,真正掌握并且可以利用这些程序达到举一反三的效果。

本书用简洁的语言,大量的实例和故事,辅之以简单的Python语言,把原本枯燥的概率理论讲得生动且容易理解。在学习到理论的同时,还了解了它的真实意义以及可以使用的地方。对于有追求的工程师和感兴趣的读者而言,这是一本提升自我的很好的图书。

酷我音乐 雷鸣

雷鸣,现任酷我音乐董事长、CEO,国家千人计划特聘专家,百度创始七剑客之一,百度搜索引擎的早期设计者和技术负责人之一。获北京大学计算机科学硕士学位和斯坦福大学商学院MBA学位,曾任北京大学计算机系学生会主席和斯坦福大学中国学生学者联合会副主席。


这本书以及Think系列其他书籍的一个前提是:只要懂得编程,你就能用这个技能去学习其他的内容 。

绝大多数贝叶斯统计的书使用数学符号并以数学概念的形式表示数学思想,比如微积分。但本书使用了Python代码而不是数学,离散近似而不是连续数学。结果就是原本需要积分的地方变成了求和,概率分布的大多数操作变成了简单的循环。

我认为这样的表述是易于理解的,至少对于有编程经验的人们来说是这样的。当作建模选择时也非常实用,因为我们可以选取最合适的模型而不用担心偏离常规分析太多。

另外,这也提供了一个从简化模型到真实问题的平滑发展路线,第3章就是一个好示例。它由一个关于骰子的简单例子开始,那是基本概率的一个主题;紧接着谈到了一个我从Mosteller《50个挑战的统计学难题》(Fifty Challenging Problems in Probability)一书中借用的火车头问题;最后是德军坦克问题,这个第二次世界大战中成功的贝叶斯方法应用案例。

本书中多数章节的灵感都是由真实世界里的问题所激发的,所以涉及了一些建模知识,在应用贝叶斯方法(或者其他的分析方法)前,我们必须决定真实世界中的哪些部分可以被包括进模型,而哪些细节可以被抽象掉。

例如,第7章中那个预测冰球比赛获胜队伍的例子,我将进球得分建模为一个泊松过程,这预示着在比赛的任何时段进球机会都是相等的,这并不完全符合实际情况,但就大多数目的来说可能就够了。

第12章中,问题是对SAT得分进行解释(SAT是用于全美大学的入学标准测试)。我以一个假设所有SAT试题难度相同的简化模型开始,但其实SAT的试题设计中既包括了相对容易,也包括了相对较难的试题。随后提出了第二个反映这一设计目的的模型,结果显出两个模型在最终效果上没有大的差别。

我认为在解决问题的过程中,明确建模过程作为其中一部分是重要的,因为这会提醒我们考虑建模误差(也就是建模当中简化和假设带来的误差)。

本书中的很多方法都基于离散分布,这让一些人担心数值误差,但对于真实世界的问题,数值误差几乎从来都小于建模误差。

再者,离散方法总能允许较好的建模选择,我宁愿要一个近似的良好的模型也不要一个精确但却糟糕的模型。

从另一个角度看,连续方法常在性能上有优势,比如能以常数时间复杂度的解法替换掉线性或者平方时间复杂度的解法。

总的来说,我推荐这些步骤的一个通用流程如下。

1.当研究问题时,以一个简化模型开始,并以清晰、好理解、实证无误的代码实现它。注意力集中在好的建模决策而不是优化上。

2.一旦简化模型有效,再找到最大的错误来源。这可能需要增加离散近似过程当中值的数量,或者增加蒙特卡洛方法中的迭代次数,或者增加模型细节。

3.如果对你的应用而言性能就已经足够了,则没必要再优化。但如果要做,有两个方向可以考虑:评估你的代码以寻找优化空间,例如,如果你缓存了前面的计算结果,你也许能避免重复冗余的计算;或者可以去发现找到计算捷径的分析方法。

这一流程的好处是第一、第二步较快,所以你能在投入大量精力前研究多个可替代的模型。

另一个好处是在第三步,你可以从一个大体正确的可参考实现开始进行回归测试。也就是,检查优化后的代码是否得到了同样的结果,至少是近似的结果。

本书中的很多例子使用了在thinkbayes.py当中定义的类和函数,可以从http://thinkbayes.com/thinkbayes.py下载这个模块。

本书大多数章节包括了可以从http://thinkbayes.com下载的代码,其中有一些依赖代码也需要下载,我建议你将这些文件全部放入同一个目录,这样代码间就可以彼此引用而无需变更Python的库文件搜索路径。

你可以在需要时再下载这些代码,或者一次性从http://thinkbayes.com/thinkbayes_code.zip下载,这个文件也包括了某些程序使用的数据文件,当解压时,将创建名为thinkbayes_code的包括本书中所有代码的目录。

另外,如果是Git用户,你可以通过fork和clone来一次性获得这个仓库:https://github.com/AllenDowney/ThinkBayes

我用到的模块之一是thinkplot.py,它对pyplot中一些函数进行了封装,要使用它需要安装好matplotlib,如果还没有,检查你的软件包管理器看看它是否存在,否则你可以从http://matplotlib.org得到下载指南。

最后,本书中一些程序使用了NumPy和SciPy,可以从http://numpy.orghttp://scipy.org 获得。

有经验的Python程序员会注意到本书中的代码没有符合PEP 8这一最通用的Python编码指南(http://www.python.org/dev/peps/pep-0008/)。

确切地说,PEP 8使用带有词间下划线的小写函数名like_this,而在本书中和实现的代码里,函数和方法名以大写开头并使用间隔式的大小写,LikeThis。

没有遵循PEP 8规范的原因是在我为书中内容准备代码时正在谷歌做访问学者,所以就遵循了谷歌的编码规范,它只在少数地方沿袭了PEP 8,一用上了谷歌风格我就喜欢上了,现在要改太麻烦。

同样,在主题风格上,如在“Bayes’s theorem”中,s放在单引号后,在某些风格指南中倾向这样使用而在其他指南当中不是。我没有特别的偏好,但不得不选择其一,所以就是你们现在看到的这个。

最后一个排版上的注脚是:贯穿全书,我使用PMF和CDF表示概率密度函数或累积分布函数这些数学概念,而Pmf和Cdf是指我所表述的Python对象。

还有几个出色的能在Python中进行贝叶斯统计的模块,包括pymc和OpenBUGS,由于读者需要有相当多的背景知识才能开始使用这些模块,因此本书中我没有使用它们,而且我想使阅读本书的预备条件最小。如果你了解Python和一点点概率知识,就可以开始阅读本书。

第1章关于概率论和贝叶斯定理,没有程序代码。第2章介绍了Pmf,一望而知是用来表示概率密度函数(PMF)的Python字典对象。然后第3章我介绍了Suite,一个Pmf对象,也是一个能进行贝叶斯更新的框架,因而万事具备了。

好了,随后的章节中,我使用了高斯(正态)分布,二次和泊松分布,beta分布等各种分析型的概率分布,在第15章,我介绍了不太常见的狄利克雷分布,不过接着也进行了解释。如果你不熟悉这类分布,可以从维基百科了解它们。也可以阅读本书的一本指南《统计思维》(Think Stats),或其他入门级的统计学书籍(不过,恐怕大多数类似书籍都会采取对实战没有太大帮助的数学方法来阐述)。

本书中使用了下面的印刷惯例。

斜体(Italic

表示新术语,URL,邮件地址,文件名和文件扩展名。

等宽(Constant width

用于程序代码,也包括那些表示程序代码元素的段落,例如,变量和函数名,数据库,数据类型,环境变量,声明和关键字。

等宽粗体(Constant width bold

命令或者其他由用户输入的文字。

等宽斜体(Constant width italic

应该由用户输入值替换或者由上下文决定的文本。

这个图标表示这是一个提示、建议或者一般性的注记。

这个图标表示这是提醒或者警示。

如果你想就本书发表评论或有任何疑问,敬请联系出版社。

美国:

O’Reilly Media Inc.

1005 Gravenstein Highway North

Sebastopol, CA 95472

中国:

北京市西城区西直门南大街2号成铭大厦C座807室(100035)

奥莱利技术咨询(北京)有限公司

我们还为本书建立了一个网页,其中包含了勘误表、示例和其他额外的信息。你可以通过地址访问该网页:http://oreil.ly/think-bayes

关于本书的技术性问题或建议,请发邮件到:bookquestions@oreilly.com。

欢迎登录我们的网站(http://www.oreilly.com),查看更多我们的书籍、课程、会议和最新动态等信息。

我们的其他联系方式如下。

Facebook:http://facebook.com/oreilly

Twitter:http://twitter.com/oreillymedia

YouTube:http://www.youtube.com/oreillymedia

如果你发现本书有需要更正的地方或者其他建议,请发送电子邮件至downey@allendowney.com。一旦根据你的反馈进行了修正,我会将你加入贡献者列表(除了要求不署名的情况)。

提供包含错误之处的段落部分,会让我更容易找到它们。只提供页和节数也可以,但还是不太容易找到错误之处。这里先致谢!


所有贝叶斯统计的方法都基于贝叶斯定理,如果有条件概率的学习基础,意识到这一点很自然。因此我们会从概率、条件概率开始,然后到贝叶斯定理,最后讨论贝叶斯统计的内容。

概率表示为0和1之间的数字(包括0和1),含义是某一事件或者预测行为的可信程度,1值表示“事件为真”的情形肯定发生,或表述为预测成真;而0值则表示“事件为真”这一情形为假。

其他中间值表示确定性的程度。例如,0.5通常也会写成50%,意味着一个预测结果发生和不发生有同等可能性。例如,在一个掷硬币事件中,人像面(正面)朝上的概率就非常接近50%。

条件概率是带有某些(前提条件)背景约束下的概率问题。例如,我想了解一下明年自己心脏病发作的可能性。根据疾病控制中心的数据,每年大约有78.5万名美国人罹患心脏病(http://www.cdc.gov//heartdisease/fact.html)。

美国约有3.11亿人,假设随机挑选一个美国人,那么其在明年心脏病发作的概率大约是0.3%。

但就具体个例而言,“我”可不是那个被随意选中的美国人。流行病学家们已经明确了多种影响心脏病发作的风险因素,根据这些因素我的风险则有可能高于或低于平均值。

本人男,45 岁,有临界高胆固醇,这些因素增加了我发病的可能性;然而,血压低、不抽烟这些因素则降低了可能性。

把上面这些条件输入在线计算器http://hp2010.nhlbihin.net/atpiii/calculator.asp,我发现自己明年心脏病发作的风险约为0.2%,低于全国平均水平。这个值就是一个条件概率,因为它是基于一系列前提因素的,这些因素构成了我患心脏病的“条件”。

通常条件概率的记号是p(A|B),表示在给定B条件下A事件发生的概率。在这个例子中,A表示我明年罹患心脏病带的概率,而B表示了上面所罗列的条件。

联合概率:是指两个事件同时发生的概率。p(AB)是AB事件的发生都为真的概率。

如果你已经理解了投骰例子和它的背景,我们开始学习下面的公式:

p(AB)= p(A)p(B)  提醒:表达式并非总是成立。

例如,如果我投掷两个硬币,A表示第一枚硬币正面朝上,B表示第二枚硬币正面朝上,那么p(A)= p(B) = 0.5,同样的p(AB) = p(A)p(B) = 0.25。

但是上面公式仅在AB都是独立事件的情况下才成立。即:已知A事件的结果并不影响或改变B事件发生的概率。或更正式表示为,p(B|A)= p(B)。

再考虑另一个事件之间并不独立的例子。假设 A 表示今天下雨的事件,B 表示明天会下雨的事件。如果我已经知道今天下雨,则明天还有可能下雨(译注:与仅仅单独考虑某一天会下雨的概率相比较),所以p(B|A)>p(B)。

通常意义下,联合概率表述为

p(A and B) = p(A) p(B|A)

对于任何AB事件,如果任意一天下雨的机会是0.5,连续两天就不会是0.25,而是可能更高一点。

我们即将开始讨论到贝叶斯定理,但我还想通过一个被称为“曲奇饼问题”的例子来介绍它。假设有两碗曲奇饼,碗1包含30个香草曲奇饼和10个巧克力曲奇饼,碗2有上述两种饼干各20个。

现在设想你在不看的情况下随机地挑一个碗拿一块饼,得到了一块香草曲奇饼。我们的问题是:从碗1取到香草曲奇饼的概率是多少?

这就是一个条件概率问题;我们希望得到概率p(碗1|香草),但怎样进行计算并非显而易见。问题如果换成在碗1中香草曲奇饼的概率则简单得多。

p(香草|碗1)= 3/4

不巧的是,p(A|B)并不和p(B|A)相同,但有方法从一个计算出另一个:贝叶斯定理。

现在,我们准备好进行贝叶斯定理推导需要的所有条件了。首先,我们注意到,联合概率是乘积可交换(乘法交换律)的,即:

p(A and B) = p(B and A)

对于任何AB表示的事件都成立。

然后,我们写出一个联合概率的表达式:

p(A and B) = p(A)p(B|A)

由于我们并没有明确定义AB的含义,因而可以对AB进行互换操作。

交换它们的位置:

p(B and A) = p(B)p(A|B)

把这些表达式连接起来,我们得到下面的表达式:

p(B)p(A|B)= p(A)p(B|A)

这意味着我们有两种方式计算联合概率,已知p(A),乘以p(B|A);或者从另一方向,已知p(B),乘以p(A|B)。两种方法是相同的。

最后,将上式除以p(B),得到:

这正是贝叶斯定理!看起来不起眼,不过它会显示出令人吃惊的强大之处。

例如,我们可以用它来解决曲奇饼问题。

假设B1表示曲奇饼属于碗1的概率,V表示曲奇饼是香草曲奇饼的概率。

带入贝叶斯定理我们得到:

等式左边就是我们希望得到的,一块香草曲奇饼来自碗1的概率。

等式的右边表示:

把它们放在一起,我们得到:

结果是3/5。所以,“得到一块香草曲奇饼”是支持于假设“来自碗1”的证据,因为香草曲奇饼来自碗1的可能性更大。

这个例子演示了一个应用贝叶斯定理的案例:它提供了一个从p(B|A) 得到p(A|B)的策略。

这种策略在解决类似“曲奇饼问题”的情况下是有用的,即从贝叶斯等式的右边计算要比左边容易的情况下。

还有另外一种理解贝叶斯定理的思路:它给我们提供的是一种根据数据集D的内容变化更新假设概率H的方法。

这种对贝叶斯定理的理解被称为“历时诠释”。

“历时”意味着某些事情随着时间而发生;在本例,即是假设的概率随着看到的新数据而变化。

在考虑HD的情况下,贝叶斯定理的表达式可以改写成:

在这种解释里,每项意义如下:

有些情况,我们可以基于现有背景信息进行计算。比如在曲奇饼问题中,我们就将随机选中碗1或碗2的概率假设为均等。

在其他情况下,先验概率是偏主观性的;对某一先验概率,理性派的人可能会有不同意见,或许由于他们使用不同的背景信息做出判断,或者因为他们针对相同的前提条件做出了不同的解读。

似然度是贝叶斯计算中最简单的部分,在曲奇饼问题中曲奇饼来自来自哪个碗,则我们就计算那个碗中香草曲奇饼的概率。

标准化常量则有些棘手,它被定义为在所有的假设条件下这一数据出现的概率,但因为考虑的正是最一般的情况,所以不容易确定这个常量在具体应用场合的现实意义。

最常见的,我们可以指定一组如下的假设集来简化。

互斥的:集合中,至多一个假设为真。

完备的:集合中,至少一个假设必为真,且集合包含了所有的假设。

我使用suite这个词来表示具备上述属性的假设集。

在曲奇饼问题中,仅有两个假设:饼干来自碗1或者碗2,它们就是互斥的和完备的。

在本例中,我们可以用全概率公式计算p(D),即如果发生某一事件有互不容的两个可能性,可以像下面这样累加概率:

p(D) = p(B1)p(D|B1) + p(B2)p(D|B2)

代入饼干问题中的实际值,得到:

p(D) = (1/2)(3/4) + (1/2)(1/2) = 5/8

我们早前心算得到的结果也是一样的。

M&M豆是有各种颜色的糖果巧克力豆。制造M&M豆的Mars公司会不时变更不同颜色巧克力豆之间的混合比例。

1995年,他们推出了蓝色的M&M豆。在此前一袋普通的M&M豆中,颜色的搭配为:30%褐色,20%黄色,20%红色,10%绿色,10%橙色,10%黄褐色。这之后变成了:24%蓝色,20%绿色,16%橙色,14%黄色,13%红色,13%褐色。

假设我的一个朋友有两袋M&M豆,他告诉我一袋是1994年,一袋是1996年。

但他没告诉我具体哪个袋子是哪一年的,他从每个袋子里各取了一个M&M豆给我。一个是黄色,一个是绿色的。那么黄色豆来自1994年的袋子的概率是多少?

这个问题类似于曲奇饼问题,只是变化了我抽取样品的方式 (碗还是袋)。这个问题也给了我一个机会演示纸面方法:也就在仅仅在纸上画画就可以解决类似这样的问题(译注:作者为后续章节的计算型方法铺垫)。在下一章中,我们将以计算方法解这些问题。

第一步是枚举所有假设。取出黄色M&M豆的袋子称为袋1,另一个称为袋2,所以假设是:

接着我们设计一个表格,每行表示每个假设,每列表示贝叶斯定理中的每一项:

先验概率p(H) 似然度p(D|H) p(H) p(D|H) 后验概率p(H|D)
假设A 1/2 (20) (20) 200 20/27
假设B 1/2 (10) (14) 70 7/27

第一列表示先验。基于问题的声明,选择p(A)=p(B)= 1/2是合理的。

第二列表示似然度,表明了问题的背景信息。举例来说,如果A为真,黄色M&M是来自1994年的袋概率20%,而绿色来自1996包的概率为20%。因为选择是独立的,我们将其相乘以得到联合概率。

第三列由前两列得到。此列的总和270是归一化常数(译注:参考全概率公式)。为了得到最后一列的后验概率,我们将第三列的值归一化后得到第四列的值。

就是这样。简单吧?

还有,你可能会被一个细节所困扰。我将p(D|H)写成了百分数的形式而不是概率形式,这意味着它没有除以因子10000。但是当我们将其除以归一化常数时就抵消了,因此这不影响结果。

当设定的假设是互斥和穷举的,你可以将似然度乘以任何因子,如果方便,将同一个因子应用到整列上。

蒙蒂大厅(Monty Hall problem)难题可能是历史上最有争议的概率问题。问题看似简单,但正确答案如此有悖常理以致很多人不能接受,很多聪明人都难堪于自己搞错了反而据理力争,而且是公开的。

蒙蒂大厅是游戏节目“来做个交易”(Let’s Make a Deal)的主场。蒙蒂大厅难题也是这一节目的常规游戏之一。如果你参加节目,规则是这样的:

问题是,你应该“坚持”还是“换”?有没有区别?

大多数人都有强烈的直觉,认为这没有区别。剩下两个门没有打开,车在门A背后的机会是50%。

但是,这是错的。事实上,如果你坚持选择门A,中奖概率只有1/3;而如果换到另外一个门,你的机会将是2/3。

运用贝叶斯定理,我们可以将这个问题分解成几个简单部分,也许这样可以说服自身,“正确”的答案实际上的的确确是对的。

首先,我们应该对数据进行仔细描述。在本例中为D包括两个部分:蒙蒂打开了门B,而且没有车在后面。

接下来,我们定义了三个假设:A,B和C,表示假设车在门A,门B,或门C后面。同样,采用表格法:

先验概率p(H) 似然度p(D|H) p(H) p(D|H) 后验概率p(H|D)
假设A 1/3 1/2 1/6 1/3
假设B 1/3 0 0 0
假设C 1/3 1 1/3 2/3

填写先验很容易,因为我们被告知奖品是随机配置的,这表明该车可能在任何门后面。

定义似然度需要一些思考,在充分合理的考虑后,我们确信正确的似然度如下:

现在我们已经完成有难度的部分了,剩下无非就是算术。第三列的总和为1/2,除以后得到p(A|D) = 1/3,p(C|D) = 2/3,所以你最好是换个选择。

该问题有许多变形。贝叶斯方法的优势之一就是可以推广到这些变形问题的处理上。

例如,设想蒙蒂总是尽可能选择门B,且只有在迫不得已的时候才选门C(比如车在门B后)。在这种情况下,修正后的表如下:

先验概率p(H) 似然度p(D|H) p(H) p(D|H) 后验概率p(H|D)
假设A 1/3 1 1/3 1/2
假设B 1/3 0 0 0
假设C 1/3 1 1/3 1/2

唯一的变化是p(D|A)。如果车在门A后,蒙蒂可以选择打开B或C。但在这个变形问题里面,他总是选择B,因此p(D|A) = 1。

因此,对A和C,似然度是相同的,后验也是相同的:p(A|D) = p(C|D) = 1/2,在这种情况下,蒙特选择B 门显示不了车位置的任何信息,所以无论选手选择坚持不变还是改变都无关紧要。

反过来的情况下,如果蒙蒂打开门C,我们就知道p(B|D) = 1(译注:因为蒙蒂总是优先选择门B,而门D是他打开了门C,因此在假设车在门B后的前提下,他必然会打开门C,概率为1,即p(B|D)=1)。

本章中我介绍了蒙蒂问题,因为我觉得这里有它的趣味性,也因为贝叶斯定理使问题的复杂性更易控制。但这并不算是一个典型的贝叶斯定理应用,所以如果你觉得它令人困惑,没什么好担心的!

对于涉及条件概率的很多问题,贝叶斯定理提供了一个分而治之的策略。如果p(A|B)难以计算,或难以用实验衡量,可以检查计算贝叶斯定理中的其他项是否更容易,如p(B|A),p(A)和p(B)。

如果蒙蒂大厅问题让你觉得有趣,我在一篇文章“All your Bayes are belong to us”中收集了很多类似问题,你可以去单击链接进行阅读http://allendowney.blogspot.com/2011/10/all-your-bayes-are-belong-to-us.html


在统计上,分布是一组值及其对应的概率。

例如,如果滚动一个六面骰子,可能的值是数字1至6,与每个值关联的概率是1/6。

再举一个例子,你应该有兴趣了解在日常的英语使用中每个单词出现的次数。你可以建立一个包含每个字及它出现的次数的分布。

为了表示Python中的分布,可以使用一个字典映射某个值和它的概率。我编写了一个名为Pmf的类,利用Python字典实现了上述功能,而且提供了一些有用的方法。为了对应概率质量函数这种分布的数学表示法,我将其命名为Pmf。

Pmf的定义在一个我为本书完成的Python模块thinkbayes.py中。可以从http://thinkbayes.com/thinkbayes.py下载。欲了解更多信息参见前言的“代码指南”。

要使用Pmf,可如下导入:

from thinkbayes.py  import  Pmf

下面的代码建立一个Pmf来表示六面骰子的结果分布:

pmf = Pmf() 
for x in [1,2,3,4,5,6]: 
    pmf.Set(x,1/6.0)

Pmf创建一个空的没有赋值的pmf。Set方法设置每个值的概率为1/6。

这里是另一个例子,计算每个单词在一个词序列中出现的次数:

pmf = Pmf()
for word in word_list:
    pmf.Incr(word, 1)

Incr为每个单词的相应“概率”加1。如果一个词还没有出现在Pmf中,那么就将这个词添加进去。

我把“概率”加上引号是因为在这个例子中概率还没有归一化,也就是说它们的累加和不是1,因此不是真正的概率。但在本例中单词计数与概率成正比。所以当完成了所有的计数,就可以通过除以计数的总值来计算得到概率。

Pmf提供了一种Normalize方法来实现上述功能:

pmf.Normalize()

一旦有一个Pmf对象,你可以像下面这样得到任何一个值相关联的概率:

print pmf.Prob('the')

这会打印输出单词“the”在词序列中出现的频率。

Pmf使用Python字典来存储值及其概率,所以Pmf中的值可以是任意可被哈希的类型。概率可以是任意数值类型,但通常是浮点数(float类型)。

在贝叶斯定理的语境下,可以很自然地使用一个Pmf映射每个假设和对应的概率。

在曲奇饼问题里面,该假设是B1和B2。在Python中可以使用字符串来表示它们:

pmf = Pmf() 
pmf.Set('Bowl1',0.5) 
pmf.Set('Bowl2',0.5)

这一分布包含了对每个假设的先验概率,称为先验分布

要更新基于新数据(拿到一块香草曲奇饼)后的分布,我们将先验分别乘以对应的似然度。

从碗1拿到香草曲奇饼的可能性是3/4,碗2的可能性是1/2。

pmf.Mult('Bowl1',0.75) 
pmf.Mult('Bowl2',0.5)

如你所愿,Mult将给定假设的概率乘以已知的似然度。

更新后的分布还没有归一化,但由于这些假设互斥且构成了完全集合(意味着完全包含了所有可能假设),我们可以进行重新归一化如下:

pmf.Normalize()

其结果是一个包含每个假设的后验概率分布,这就是所说的后验分布

最后,我们可以得到假设碗1的后验概率如下:

print pmf.Prob('Bowl 1')

答案是0.6。你可以从http://thinkbayes.com/cookie.py下载这个例子。欲了解更多信息,请参见前言的“代码指南”。

在继续讨论其他的问题前,我想在上一节的基础上重写代码以使其更通用。首先我将定义一个类来封装与此相关的代码 :

class Cookie(Pmf): 

    def __init__(self,hypos): 
        Pmf.__init__(self) 
        for hypo in hypos: 
            self.Set(hypo,1) 
        self.Normalize()

Cookie对象是一个映射假设到概率的Pmf对象。__init__方法给每个假设赋予相同的先验概率。如上一节中就有两种假设:

    hypos= ['Bowl1','Bowl2'] 
    pmf =Cookie(hypos)

Cookie类提供了Update方法,它以data为参数并修正相应的概率:

    def Update (self,data):
        for hypo in self.Values(): 
            like= self.Likelihood(data,hypo) 
            self.Mult(hypo,like) 
        self.Normalize()

Update遍历suite中的每个假设,并将其概率乘以数据在某一假设下的似然度,似然度由Likelihood计算:

   mixes = {
       'Bowl 1':dict(vanilla=0.75, chocolate=0.25),
       'Bowl 2':dict(vanilla=0.5, chocolate=0.5),
       }

   def Likelihood(self, data, hypo):
       mix = self.mixes[hypo]
       like = mix[data]
       return like

Likelihood使用mixes,它使用Python的字典结构来映射碗名和在碗中曲奇饼的混合比例。

如下面这样进行更新:

    pmf.Update('vanilla')

然后我们就可以打印输出每个假设的后验概率:

    for hypo , prob in pmf.Items(): 
        print  hypo,prob

其结果是

Bowl 1  0.6
Bowl 2  0.4

结果和我们之前得到的结论一样。比起我们在前面章节看到的,这段代码更复杂。

一个优点是,它可以推广到从同一个碗取不只一个曲奇饼(带替换)的情形:

    dateset= ['vanilla', 'chocolate', 'vanilla'] 
    for data in dataset: 
        pmf.Update(data)

另一优点是,它提供了解决许多类似问题的框架。在下一节中,我们将解决蒙蒂大厅问题的计算,然后看看框架的哪些部分相同。

本节中的代码可以从http://thinkbayes.com/cookie2.py获得。欲了解更多信息,请参见前言的“代码指南”。

为了解决蒙蒂大厅(Monty Hall)问题,我将定义一个新的类:

class monty(Pmf): 

    def __init__(self,hypos): 
        Pmf.__init__(self) 
        for hypo in hypos: 
            self.Set(hypo,1) 
        self.Normalize()

到目前为止,蒙蒂大厅和曲奇饼是完全一样的。创建Pmf的代码也一样,除了假设的名称:

    hypos='ABC' 
    pmf =Monty(hypos)

对Update的调用几乎是相同的:

    data='B' 
    pmf.Update(data)

Update的实现也是完全一样的:

    def Update (self,data): 
        for hypo in self.Values (): 
            like = self.Likelihood(data,hypo) 
            self.Mult(hypo,like) 
        self.Normalize()

唯一需要些额外工作的是Likelihood

    def Likelihood (self,data,hypo): 
        if hypo==data: 
            return 0 
        elif hypo=='A': 
            return 0.5 
        else: 
            return 1

最后,打印输出的结果是一样的:

    for hypo,prob in pmf.Items(): 
        print hypo,prob

答案是

A 0.333333333333 
B 0.0 
Ç 0.666666666667

在本例中,Likelihood的编写有一点点复杂,但该贝叶斯框架的Update很简单。本节中的代码可以从http://thinkbayes.com/monty.py获得。欲了解更多信息,请参见前言的“代码指南”。

现在,我们看看框架的哪些元素是相同的,这样我们就可以把它们封装进一个Suite对象,即一个提供__init__UpdatePrint方法的pmf对象:

class Suite(Pmf) 
    “代表一套假设及其概率。” 

    def __init__(self,hypo=tuple()): 
        “初始化分配。” 

    def Update(self,data): 
        “更新基于该数据的每个假设。” 

    def Print (self): 
        “打印出假设和它们的概率。”

Suite的实现在thinkbayes.py中。要使用Suite对象,你应当编写一个继承自Suite的类,并自行提供Likelihood方法的实现。例如,这是一个以蒙蒂大厅问题改写的使用Suite的方案 :

from  thinkbayes  import Suite 
class Monty(Suite): 
    def Likelihood (self,data,hypo): 
        if hypo ==data: 
            return 0 
        elif hypo=='A': 
            return 0.5 
        else: 
            return 1

而下面是一个使用这个类的代码:

    suite=Monty('ABC') 
    suite.Update('B') 
    suite.Print()

你可以从http://thinkbayes.com/monty2.py下载这个例子。更多信息见前言的“代码指南”。

我们可以使用Suite框架来解决M&M豆的问题。除了编写Likelihood有点棘手,其他一切都很简单。

首先,需要对1995年之前和之后的颜色混合情况进行封装:

mix94=dict(brown= 30,
           yellow= 20,
           red= 20,
           green= 10,
           orange= 10,
           tan= 10) 

    mix96=dict(blue= 24,
               green= 20,
               orange= 16,
               yellow= 14,
               red= 13,
               brown= 13)

然后,封装假设:

    hypoA =dict(bag1 = mix94,bag2 = mix96) 
    hypoB =dict(bag1 = mix96,bag2 = mix94)

hypoA表示假设袋1是1994年,袋2是1996年。hypoB是相反的组合。

接下来,从假设的名称来映射其含义:

hypotheses=dict(A=hypoA,B=hypoB)

最后,开始编写Likelihood。在这种情况下,假设hypo是一个AB的字符串,数据是一个指定了袋子年份和颜色的元组。

  def Likelihood(self, data, hypo):
      bag, color = data
      mix = self.hypotheses[hypo][bag]
      like = mix[color]
      return like

下面是创建该suite对象并进行更新的代码:

    suite = M_and_M('AB')

    suite.Update(('bag1', 'yellow'))
    suite.Update(('bag2', 'green')) 

    suite.Print()

结果如下:

A 0.740740740741 
B 0.259259259259

A的后验概率大约是20/27,正是我们之前得到的。

本节中的代码可以从http://thinkbayes.com/m_and_m.py获得。欲了解更多信息,请参见前言的“代码指南”。

本章介绍了Suite类,它封装了贝叶斯update框架。

Suite是一个抽象类(abstract type),这意味着它定义了Suite应该有的接口,但并不提供完整的实现。Suite接口包括UpdateLikelihood方法,但只提供了Update的实现,而没有Likelihood的实现。

具体类(concrerte type)是继承自抽象父类的类,而且提供了缺失方法的实现。例如,Monty扩展自Suite,所以它继承了Update并且实现了Likelihood

如果你熟悉设计模式,你可能会意识到这其实是设计模式中模板方法的一个例子。你可以从http://en.wikipedia.org/wiki/Template_method_pattern了解这个模式。

大多数在以下章节中的例子遵循相同的模式,对于每个问题我们定义一个扩展的Suite对象,继承了Update方法,并提供了Likelihood。在少数情况下,会重写Update方法,通常是为了提高性能的缘故。

练习2-1。

在第11页的“贝叶斯框架”里面,我提到了曲奇饼问题的解法是简化的,是曲奇饼有补充的取多个饼的情况(有放回的情况)。

但更可能的情况是,我们吃掉了取出的曲奇饼,那么似然度就依赖于之前的取曲奇饼行为(曲奇饼少了)。

修改本章中的解法以处理没有曲奇饼补充的情况。提示:添加Cookie的实例变量来表示碗的假设状态,并据此修改似然度。你可能要定义一个Bowl对象。


艾伦•唐尼是欧林工程学院计算机科学系的教授。他曾在韦尔斯利学院、科尔比学院和加州大学伯克利分校教授计算机课程。

他拥有U.C.伯克利的计算机科学博士学位和麻省理工学院的硕士及学士学位。


许杨毅,新浪网系统架构师,技术保障部总监,毕业于湖南大学,拥有15年互联网工作经验。


《贝叶斯思维》一书封面的动物是红鲻鱼(也称为纵带羊鱼)。这种可以在地中海、北大西洋东海域和黑海发现的须鲷科鱼,因其第一背鳍后独特的条纹为人所知。红鲻鱼是地中海地区人们青睐的美味,和同属的须鲷科鱼——羊鱼一样,只是羊鱼没有第一背鳍后的条纹。然而红鲻鱼要更珍贵,据说其味道品尝起来类似生蚝。传说古罗马人在池塘中饲养红鲻鱼,宠爱并训练它们一听到钟声就喂饲。即便是人工养殖的红鲻鱼一般也不到两磅重,其价格有时和银器一样。

非野外环境下,红鲻鱼于浅底水系饲养,其上下唇具有独特的被称为触须的两根胡须,触须用来探测海底的食物。因为饲养于较浅的沙滩和岩石底部,它的触须与其深水的近亲羊鱼相比没有那么灵敏。

封面图片来自迈尔斯•克莱斯词典。


本书的翻译其实来自和编辑开过的一个玩笑,我当时戏说这本书的书名Think Bayes不妨译成《纪念贝叶斯老先生》,而实际上托马斯·贝叶斯先生也的确是一个伟大的值得我们纪念的人,科学的历史正是由这样一些伟大的人们推动的。贝叶斯发展并推出了我们现在熟悉的贝叶斯定理,而后人们也利用这一工具发展出了各种各样的“贝叶斯方法”。读完这本书,我相信读者对此会有深刻的认识。

另外就是本书的作者艾伦·唐尼。他不仅是一位计算机学科教授,同时也是一位优秀的作家。在翻译本书前,我已经读过他的Think Complexity 和Think Stats,在本书的翻译过程中,你还可以看到在github上他正在写作另一本关于操作系统的书,书名Think OS。

显然,Think Bayes是艾伦目前写得最好的一本书,原因在于在书中不但用编写Python程序的方式消除了贝叶斯方法的学习门槛,而且在每章的实际问题中,艾伦还潜移默化地教会了读者怎样为具体问题建立数学模型,如何抓住问题中的主要矛盾(模型中的关键参数),再一步一步地优化或者验证模型的有效性或者局限性。在这个过程中,你还能学习到统计分布中那些具体概念的实际含义和用途,比如边缘分布、联合分布、贝叶斯层次化模型。

作者艾伦·唐尼在github上托管了本书所有的Python代码,阅读本书的过程中,在自己的机器上运行例子中的代码绝对是一件让人满意的事情。

作者在写作中还带上了大量的概率图形,借助图表可以帮助读者更直观地理解有关问题的概率形式结论。

我的实际工作也和贝叶斯方法有关联,比如我们新浪邮箱就曾经采用贝叶斯方法处理垃圾邮件,业务监控系统也可以用贝叶斯方法来进行异常指标判断。至于谈到系统建模,本书也帮助我自己厘清了很多有关建模决策的困惑。

总之,这本不到200页的书绝对值得一读再读。

最后,我要感谢人民邮电出版社的编辑给我机会翻译艾伦的书。毫无疑问,艾伦是一个值得尊敬的科技书籍作者和良师。我还要感谢家人对翻译工作的支持,特别是我在书房静心工作的时候,刚刚一岁的小女儿波妞会很乖地和妈妈游戏,没有干扰我。谢谢我的女儿,我的爱人李静,当然还有无私地替我照顾妻女的母亲。

译者 许杨毅

2014年8月于颐和园北


相关图书

算者生存:商业分析的方法与实践
算者生存:商业分析的方法与实践
数据结构与算法(Rust语言描述)
数据结构与算法(Rust语言描述)
R语言医学多元统计分析
R语言医学多元统计分析
Python数据分析(第3版)
Python数据分析(第3版)
Python数据分析入门与实战
Python数据分析入门与实战
Python贝叶斯分析(第2版)
Python贝叶斯分析(第2版)

相关文章

相关课程