概率图模型:基于R语言

978-7-115-47134-5
作者: 【法】David Bellot(大卫·贝洛特)
译者: 魏博
编辑: 王峰松
分类: R语言

图书目录:

详情

概率图模型结合了概率论与图论的知识,在人工智能、机器学习和计算机视觉等领域有着广阔的应用前景。本书讲解了如何掌握概率图模型PGM,如何理解计算机通过贝叶斯模型来解决现实世界的问题,如何选择合适的R包、合适的算法来准备数据并建立模型。

图书摘要

版权信息

书名:概率图模型:基于R语言

ISBN:978-7-115-47134-5

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

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

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

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

• 著    [法] 大卫·贝洛特(David Bellot )

  译    魏 博

  责任编辑 王峰松

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

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

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

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

  反盗版热线:(010)81055315


Copyright © 2016 Packt Publishing. First published in the English language under the title Learning Probabilistic Graphical Models in R, ISBN 978-1-78439-205-5. All rights reserved.

本书中文简体字版由Packt Publishing公司授权人民邮电出版社出版。未经出版者书面许可,对本书的任何部分不得以任何方式或任何手段复制和传播。

版权所有,侵权必究。


概率图模型结合了概率论与图论的知识,提供了一种简单的可视化概率模型的方法,在人工智能、机器学习和计算机视觉等领域有着广阔的应用前景。本书旨在帮助读者学习使用概率图模型,理解计算机如何通过贝叶斯模型和马尔科夫模型来解决现实世界的问题,同时教会读者选择合适的R语言程序包、合适的算法来准备数据并建立模型。本书适合各行业的数据科学家、机器学习爱好者和工程师等人群阅读、使用。


David Bellot是法国国家信息与自动化研究所(INRIA)计算机科学专业的博士,致力于贝叶斯机器学习。他也是美国加州大学伯克利分校的博士后,为英特尔、Orange电信和巴克莱银行等公司工作过。他现在财经行业工作,使用机器学习技术开发财经市场的预测算法,同时也是开源项目,如Boost C++库的贡献者。


魏博,志诺维思(北京)基因科技有限公司高级算法工程师。本科毕业于武汉大学数学系,博士毕业于中国科学院数学与系统科学研究院计算机软件与理论专业。前阿里巴巴优酷事业部视频搜索算法专家,欧普拉软件科技(北京)有限公司新闻推荐高级算法工程师。长期关注于用户需求建模、行为建模和自动推理。数据挖掘、机器学习和数据可视化爱好者,尤其热衷于海量数据中用户视角和用户行为模式的刻画和推断,以及自然语言处理问题。


Mzabalazo Z. Ngwenya拥有开普敦大学数学统计专业的研究生学历。他在统计咨询行业有广泛的业务,并有大量的R开发经验。他的兴趣主要在统计计算方面。他之前审阅了Packt出版社的书籍R Studio for R Statistical Computing(Mark P.J. van der Loo和Edwin de Jonge);R Statistical Application Development by Example Beginner’s Guide(Prabhanjan Narayanachar Tattar);Machine Learning with R(Brett Lantz);R Graph Essentials(David Alexandra Lillis);R Object-oriented Programming(Kelly Black);Mastering Scientific Computing with R(Paul Gerrard和Radia Johnson);Mastering Data Analysis with R(Gergely Darócz)。

Prabhanjan Tattar现在是Fractal Analytics公司的高级数据科学家。他拥有8年的统计分析经验。生存分析和统计推断是他的主要科研和兴趣方向。他在同行评审的期刊上发表了多篇科研论文,并撰写了两本R语言书籍:《R语言统计应用开发实例》(R Statistical Application Development by Example,Packt Publishing)和《R语言统计教程》(A Course in Statistics with R, Wiley)。R程序包gpk、 RSADBE和ACSWR也是由他维护的。


概率图模型是机器学习领域表示现实世界带有概率信息的数据和模型的最先进技术之一。在许多场景中,概率图模型使用贝叶斯方法来描述算法,以便可以从带有噪声和不确定性的现实世界中得出结论。

本书介绍了一些相关话题,例如推断(自动推理和学习),可以自动从原始数据中构建模型。它解释了算法是如何逐步运行的,并使用诸多示例展示了即时可用的R语言解决方案。介绍完概率和贝叶斯公式的基本原理之后,本书给出了概率图模型(Probabilistic Graphical Models,PGM),以及几种类型的推断和学习算法。读者会从算法设计过渡到模型的自动拟合。

本书关注在解决数据科学问题上有成功案例的有用模型,例如贝叶斯分类器、混合模型、贝叶斯线性回归,以及用于构建复杂模型的基本模型组件。

第1章,概率推理,介绍了概率论和概率图模型的基本概念,并通过贝叶斯公式的表示,为概率模型提供一种易用、高效、简单的建模方法。

第2章,精确推断,介绍了如何通过简单图形的组合和模型查询构建概率图模型。该查询使用一种叫作联结树算法的精确推断算法。

第3章,学习参数, 包括从数据集中使用最大似然法,拟合和学习概率图模型。

第4章,贝叶斯建模——基础模型, 介绍了简单而强大的贝叶斯模型,其可以作为更加复杂模型的基础模块,以及如何使用自适应算法来拟合和查询贝叶斯模型。

第5章,近似推断, 介绍了概率图模型上的第二种推断方法,同时介绍了主要的采样算法,例如马尔科夫链蒙特卡洛(MCMC)。

第6章,贝叶斯建模——线性模型,介绍了更高级贝叶斯视角的标准线性回归算法,并给出了解决过拟合问题的方案。

第7章,概率混合模型, 介绍了更加复杂的概率模型,其中的数据来自于几种简单模型的混合。

附录,介绍了本书所引用的所有书籍和文献。

本书的所有例子都需要版本在3.0以上的R环境中运行。

本书面向需要处理海量数据,并从中得出结论的读者,尤其是当数据有噪声或者存在不确定性的读者。数据科学家、机器学习爱好者、工程师和其他对机器学习最新技术感兴趣的人会觉得概率图模型很有意思。

欢迎读者反馈。让我们知道你关于这本书的想法——喜欢什么,不喜欢什么。读者反馈对于我们很重要,它可以帮助我们开发读者真正需要的话题。想给我们发送反馈,只需要发电子邮件至feedback@packtpub.com,并在邮件主题中告知书名。如果你是某个话题的专家,并且有兴趣编写书籍或者给予贡献,请查看我们的作者指导:www.packtpub.com/authors。

你现在已经是Packt书籍的荣誉所有者。你还拥有以下权利。

你可以从http://www.packtpub.com的个人账户中下载本书的示例代码文件。如果你是从别的地方购买的本书,你可以访问http://www.packtpub.com/support,在此网站注册后,会直接发邮件给你代码文件。你可以通过下列步骤下载代码文件:

1.使用你的邮箱和密码在我们的网站登录并注册;

2.在顶部的SUPPORT标签上悬停光标;

3.单击Code Downloads & Errata;

4.在Search框中输入书名;

5.选取代码文件所在的书籍;

6.选择购书途径的下拉菜单;

7.单击Code Download。

你也可以在本书网站的页面单击Code Files按钮下载代码文件。这本书的网页可以通过Search搜索框输入书名找到。你需要登录自己的Packt账户。

文件下载完成之后,确保使用下列软件的最新版解压或抽取文件:

尽管我们已经非常细心地保证内容的正确性,但是错误还是会发生。如果你在我们的书中找到一处错误并告诉我们,不管是文本错误或是代码错误,我们都会非常感激。你的善举会省去其他用户的烦恼,并帮助我们改进本书的后续版本。如果你找到了任何勘误,请访问https://www.packtpub.com/submit-errata报告给我们。你只须选取书名,单击Errata Submission Form链接,输入勘误的具体信息。一旦勘误确定之后,我们会接受你的提交。勘误会上传到我们的网站,或者添加到书籍勘误部分已有的勘误列表下。要查看以前提交的勘误,访问https://www.packtpub.com/books/content/support,在搜索框输入书名、所需信息会出现在Errata部分下。

互联网上版权资料的盗版问题一直是所有媒介无法避免的问题。在Packt,我们一直严肃对待版权和许可的保护问题。如果你在互联网上遇到任何形式的我社出版物的非法副本,请立即把具体地址或者网站名称提供给我们,我们可以采取补救措施。请联系copyright@packtpub.com,附上可疑的盗版材料的链接。我们非常感谢你在保护作者方面的努力,也会注重提升自我能力,给你带来更有价值的内容。

如果你对本书有任何疑问,可以联系我们questions@packtpub.com。我们会尽全力解决您的问题。


在有关21世纪的所有预测中,最不希望的一个也许是我们需要每天收集世界上任何地方、关于任何事情的海量数据。近几年来,人们见证了关于世界、生活和技术方面难以置信的数据爆炸,这也是我们确信引发变革的源动力。虽然我们生活在信息时代,但是仅仅收集数据而不发掘价值和抽取知识是没有任何意义的。

在20世纪开始的时候,随着统计学的诞生,世界都在收集数据和生成统计。那个时候,唯一可靠的工具是铅笔和纸张,当然还有观察者的眼睛和耳朵。虽然在19世纪取得了长足的发展,但是科学观察依然处在新生阶段。

100多年后,我们有了计算机、电子感应器以及大规模数据存储。我们不但可以持续地保存物理世界的数据,还可以通过社交网络、因特网和移动电话保存我们的生活数据。而且,存储技术水准的极大提高也使得以很小的容量存储月度数据成为可能,甚至可以将其放进手掌中。

但是存储数据不是获取知识。存储数据只是把数据放在某个地方以便后用。同样,随着存储容量的快速演化,现代计算机的容量甚至在以难以置信的速度提升。在读博士期间,我记得当我收到一个崭新、耀眼的全功能PC来开展科研工作时,我在试验室是多么的骄傲。而今天,我口袋里老旧的智能手机,还要比当时的PC快20倍。

在本书中,你会学到把数据转化为知识的最先进的技术之一:机器学习。这项技术用在当今生活的方方面面,从搜索引擎到股市预测,从语音识别到自动驾驶。而且,机器学习还用在了人们深信不疑的领域,从产品链的质量保证到移动手机网络的天线阵列优化。

机器学习是计算机科学、概率论和统计学相互融合的领域。机器学习的核心问题是推断问题或者说是如何使用数据和例子生成知识或预测。这也给我们带来了机器学习的两个基础问题:从大量数据中抽取模式以及高层级知识的算法设计,和使用这些知识的算法设计——或者说得更科学一些:学习和推断。

皮埃尔-西蒙·拉普拉斯(Pierre-Simon Laplace,1749—1827),法国数学家,也是有史以来最伟大的科学家之一,被认为是第一批理解数据收集重要性的人:他发现了数据不可靠,有不确定性,也就是今天说的有噪声。他也是第一个研究使用概率来处理不确定性等问题,并表示事件或信息信念度的人。

在他的论文《概率的哲学》(Essai philosophique sur les probabilités,1814)中,拉普拉斯给出了最初的支持新老数据推理的数学系统,其中的用户信念会在新数据可用的时候得到更新和改进。今天我们称之为贝叶斯推理。事实上,托马斯·贝叶斯确实是第一个、早在18世纪末就发现这个定理的人。如果没有贝叶斯工作的铺垫,皮埃尔-西蒙·拉普拉斯就需要重新发现同一个定理,并形成贝叶斯理论的现代形式。有意思的是,拉普拉斯最终发现了贝叶斯过世之后发表的文章,并承认了贝叶斯是第一个描述归纳推理系统原理的人。今天,我们会提及拉普拉斯推理,而不是贝叶斯推理,并称之为贝叶斯-普莱斯-拉普拉斯定理(Bayes-Price-Laplace Theorem)。

一个多世纪以后,这项数学技术多亏了计算概率论的新发现而得到重生,并诞生了机器学习中一个最重要、最常用的技术:概率图模型。

从此刻开始,我们需要记住,概率图模型中的术语指的是图论,也就是带有边和点的数学对象,而不是图片或者图画。众所周知,当你想给别人解释不同对象或者实体之间的关系时,你需要拿纸画出带有连线或箭头的方框。这是一种简明易懂的方法,可以来介绍任何不同元素之间的关系。

确切地说,概率图模型(Probabilistic Graphical Models,PGM)是指:你想描述不同变量之间的关系,但是,你又对这些变量不太确定,只有一定程度的相信或者一些不确定的知识。现在我们知道,概率是表示和处理不确定性的严密的数学方法。

概率图模型是使用概率来表示关于事实和事件的信念和不确定知识的一种工具。它也是现在最先进的机器学习技术之一,并有很多行业成功的案例。

概率图模型可以处理关于世界的不完整的知识,因为我们的知识总是有限的。我们不可能观察到所有的事情,不可能用一台计算机表示整个宇宙。和计算机相比,我们作为人类从根本上是受限的。有了概率图模型,我们可以构建简单的学习算法,或者复杂的专家系统。有了新的数据,我们可以改进这些模型,尽全力优化模型,也可以对未知的局势和事件做出推断或预测。

在第1章中,你会学到关于概率图模型的基础知识,也就是概率知识和简单的计算规则。我们会提供一个概率图模型的能力概览,以及相关的R程序包。这些程序包都很成功,我们只需要探讨最重要的R程序包。

我们会看到如何一步一步地开发简单模型,就像方块游戏一样,以及如何把这些模型连接在一起开发出更加复杂的专家系统。我们会介绍下列概念和应用。每一部分都包含几个可以直接用R语言上手的示例:

本书是关于机器学习领域的书籍,或者更广义地叫作人工智能。为了完成任务,或者从数据中得出结论,计算机以及其他生物需要观察和处理自然世界的各种信息。从长期来看,我们一直在设计和发明各种算法和系统,来非常精准地并以非凡的速度解决问题。但是所有的算法都受限于所面向的具体任务本身。另外,一般生物和人类(以及许多其他动物)展现了在通过经验、错误和对世界的观察等方式取得适应和进化方面令人不可思议的能力。

试图理解如何从经验中学习,并适应变化的环境一直是科学界的伟大课题。自从计算机发明之后,一个主要的目标是在机器上重复生成这些技能。

机器学习是关于从数据和观察中学习和适应的算法研究,并实现推理和借助学到的模型和算法来执行任务。由于我们生活的世界本身就是不确定的,从这个意义上讲,即便是最简单的观察,例如天空的颜色也不可能绝对的确定。我们需要一套理论来解决这些不确定性。最自然的方法是概率论,它也是本书的数学基础。

但是当数据量逐渐增长为非常大的数据集时,即便是最简单的概率问题也会变得棘手。我们需要一套框架支持面向现实世界问题复杂度的模型和算法的便捷开发。

说到现实世界的问题,我们可以设想一些人类可以完成的任务,例如理解人类语言、开车、股票交易、识别画中的人脸或者完成医疗诊断等。

在人工智能的早期,构建这样的模型和算法是一项非常复杂的任务。每次产生的新算法,其实现和规划总是带着内在的错误和偏差。本书给出的框架,叫作概率图模型,旨在区分模型设计任务和算法实现任务。因为,这项技术基于概率论和图论,因此它拥有坚实的数学基础。但是同时,这种框架也不需要实践者一直编写或者重写算法,因为算法是针对非常原生的问题而设计的,并且已经存在了。

同时,概率图模型基于机器学习技术,它有利于实践人员从数据中以最简单的方式创造新的模型。

概率图模型中的算法可以从数据中学到新的模型,并使用这些数据和模型回答相关问题,当然也可以在有新数据的时候改进模型。

在本书中,我们也会看到概率图模型是我们熟知的许多标准和经典模型的数学泛化,并允许我们复用、混合和修改这些模型。

本章的其他部分会介绍概率论和图论所需的概念,帮助读者理解和使用R语言概率图模型。

概率图模型,从数学的角度看,是一种表示几个变量概率分布的方法,也叫作联合概率分布。换句话说,它是一种表示几个变量共同出现的数值信念的工具。基于这种理解,虽然概率图模型看起来很简单,但是概率图模型强调的是对于许多变量概率分布的表示。在某些情况下,“许多”意味着大量,比如几千个到几百万个。在这一部分里,我们会回顾概率图模型的基本概念和R语言的基本实现。如果你对这些内容很熟悉,你可以跳过这一部分。我们首先研究为什么概率是表示人们对于事实和事件信念的优良工具,然后我们会介绍概率积分的基本概念。接着,我们会介绍贝叶斯模型的基础构建模块,并做一些简单而有意思的计算。最后,我们会讨论本书的主要话题:贝叶斯推断。

我之前说过贝叶斯推断是本书的主要话题吗?的确,概率图模型也是执行贝叶斯推断,或者换句话说,是从先前信念和新数据中计算新的事实和结论的前沿技术。

更新概率模型的原理首先是由托马斯·贝叶斯发现的,并由他的朋友普莱斯于1763年发表在著名的论文《论机会主义下的问题解决》( An Es An Essay toward solving a Problem in the Doctrine of Chances)中。

Probability theory is nothing but common sense reduced to calculation

Théorie analytique des probabilités, 1821.

Pierre-Simon, marquis de Laplace

正如皮埃尔-西蒙·拉普拉斯所说,概率是一种量化常识推理和信念程度的工具。有意思的是,在机器学习的背景下,信念这一概念已经被不知不觉地扩展到机器上,也就是计算机上。借助算法,计算机会对确定的事实和事件,通过概率表示自己的信念。

让我们举一个众人熟知的例子:掷硬币游戏。硬币正面或者反面向上的概率或机会是多少?大家都应该回答是50%的机会或者0.5的概率(记住,概率是0和1之间的数)。

这个简单的记法有两种理解。一种是频率派解释,另一种是贝叶斯派解释。第一种频率派的意思是如果我们投掷多次,长期来看一半次数正面向上,另一半次数反面向上。使用数字的话,硬币有50%的机会一面朝上,或者概率为0.5。然而,频率派的思想,正如它的名字,只在试验可以重复非常多的次数时才有效。如果只观察到一两次事实,讨论频率就没有意义了。相反,贝叶斯派的理解把因素或事件的不确定性通过指认数值(0~1或者0%~100%)来量化。如果你投掷一枚硬币,即使在投掷之前,你也肯定会给每个面指认50%的机会。如果你观看10匹马的赛马,而且对马匹和骑手一无所知,你也肯定会给每匹马指认0.1(或者10%)的概率。

投掷硬币是一类可以重复多次,甚至上千次或任意次的试验。然而,赛马并不是可以重复多次的试验。你最喜欢的团队赢得下次球赛的概率是多少?这也不是可以重复多次的试验:事实上,你只可以试验一次,因为只有一次比赛。但是由于你非常相信你的团队是今年最厉害的,你会指认一个概率,例如0.9,来确信你的团队会拿下下一次比赛。

贝叶斯派思想的主要优势是它不需要长期频率或者同一个试验的重复。

在机器学习中,概率是大部分系统和算法的基础部件。你可能想知道收到的邮件是垃圾邮件的概率。你可能想知道在线网站下一个客户购买上一个客户同一个商品的概率(以及你的网站是否应该立刻给它打广告的概率)。你也想知道下个月你的商铺拥有和这个月同样多客户的概率。

从这些例子可以看出,完全频率派和完全贝叶斯派之间的界限远远不够清晰。好消息是不论你选择哪一种理解,概率计算的规则是完全相同的。

机器学习尤其是概率图模型的核心是条件概率的思想。事实上,准确地说,概率图模型都是条件概率的思想。让我们回到赛马的例子。我们说,如果你对骑手和马匹一无所知,你可以给每一匹马(假定有10匹马)指认0.1的概率。现在,你知道这个国家最好的骑手也参加了这项赛事。你还会给这些骑手指认相同的机会吗?当然不能!因此这个骑手获胜的概率可能是19%,进而所有其他骑手获胜的概率只有9%。这就是条件概率:也就是基于已知其他事件的结果,当前事件的概率。这种概率的思想可以完美地解释改变直觉认识或者(更技术性的描述)给定新的信息来更新信念。概率图模型就是关注这些技术,只是放在了更加复杂的场景中。

在之前的部分,我们看到了为什么概率是表示不确定性或者信念,以及事件或事实频率的优良工具。我们也提到了不管是贝叶斯派还是频率派,他们使用的概率计算规则是相同的。在本部分中,我们首先回顾概率计算规则,并介绍随机变量的概念。它是贝叶斯推理和概率图模型的核心概念。

样本空间,事件和概率

在这一部分中,我们会介绍本书概率论中使用的基本概念和语言。如果读者已经知道了这些概念,可以跳过这一部分。

一个样本空间Ω是一个试验所有可能输出的集合。在这个集合中,我们称Ω中的一个点ω,为一个实现。我们称Ω的一个子集为一个事件

例如,如果我们投掷一枚硬币一次,我们可以得到正面(H)或者反面(T)。我们说样本空间是Ω={H,T}。一个事件可以是我得到了正面(H)。如果我们投掷一枚硬币两次,样本空间变得更大,我们可以记录所有的可能Ω={HH,HT,TH,TT}。一个事件可以是我们首先得到了正面。因此我的事件是E={HH,HT}。

更复杂的例子可以是某人身高的米数度量[1] 。样本空间是所有从0.0到10.9的正数。你的朋友很有可能都没有10.9米高,但是这并不会破坏我们的理论。

一个事件可以是所有的篮球运动员,也就是高于2米的人。其数学记法写作,相对区间Ω=[0,10.9],E=[2,10.9]。

一个概率是指派给每一个事件E的一个实数P(E)。概率必须满足下列3个公理。在给出它们之前,我们需要回顾为什么需要使用这些公理。如果你还记得我们之前说的,不论我们对概率做何理解(频率派或贝叶斯派),控制概率计算的规则是一样的:

随机变量和概率计算

在计算机程序中,变量是与计算机内存中一部分存储空间相关联的名称或者标记。因此一个程序变量可以通过它的位置(和许多语言中的类型)来定义,并保存有且仅有一个取值。这个取值可以很复杂,例如数组或者数据结构。最重要的是,这个取值是已知的,并且除非有人特意改变,它保持不变。换句话说,取值只能在算法确定要改变它的时候才会发生变化。

而随机变量有点不同:它是从样本空间到实数的函数映射。例如,在一些试验中,随机变量被隐式地使用:

对于每一个可能的事件,我们可以关联一个概率Pi。所有这些概率的集合是随机变量的概率分布

让我们看一个例子:考虑投掷一枚硬币3次的试验。(样本空间中的)样本点是3次投掷的结果。例如,HHT,两次正面向上和一次背面向上是一个样本点。

因此我们可以很容易地列举所有可能的输出,并找出样本空间:

S={HHH, HHT, HTH,THH,TTH,THT,HTT,TTT}

假设Hi为第i次投掷正面向上的事件。例如:

H1={HHH,HHT,HTH,HTT}

如果我们给每个事件指认1/8的概率,那么使用列举的方法,我们可以看到P(H1)=P(H2)=P(H3)=1/2。

在这个概率模型中,事件H1H2H3是相互独立的。要验证这个结论,我们首先有:

我们还必须验证每一对乘积。例如:

对于另外两对也需要同样的验证。所以H1H2H3是相互独立的。通常,我们把两个独立事件的概率写作它们独自概率的乘积:P(AB)=P(AP(B)。我们把两个不相干独立事件的概率写作它们独立概率的和:P(AB)=P(A)+P(B)。

如果我们考虑不同的结果,可以定义另外一种概率分布。例如,假设我们依然投掷3次骰子。这次随机变量X是完成3次投掷后,正面向上的总次数。

使用列举方法我们可以得到和之前一样的样本空间:

S={HHH, HHT, HTH,THH,TTH,THT,HTT,TTT}

但是这次我们考虑正面向上的次数,随机变量X会把样本空间映射到表1-1所示的数值:

表1-1

s

HHH

HHT

HTH

THH

TTH

THT

HTT

TTT

X(s)

3

2

2

2

1

1

1

0

因此随机变量X的取值范围是{0,1,2,3}。和之前一样,如果我们假设所有点都有相同的概率1/8,我们可以推出X取值范围的概率函数,如表1-2所示:

表1-2

x

0

1

2

3

P(X=x)

1/8

3/8

3/8

1/8

让我们回到第一个游戏,同时得到2次正面向上和一次6点,低概率的获胜游戏。我们可以给硬币投掷试验关联一个随机变量N,它是2次投掷后获得正面的次数。这个随机变量可以很好地刻画我们的试验,N取0、1和2。因此,我们不说对两次正面向上的事件感兴趣,而等价的说我们对事件N=2感兴趣。这种表述方便我们查看其他事件,例如只有1次正面(HT或TH),甚至0次正面(TT)。我们说,给N的每个取值指派概率的函数叫作概率分布。另一个随机变量是D,表述投掷骰子之后的点数。

当我们同时考虑两个试验(投掷硬币2次和投掷一个骰子)的时候,我们对同时获得0、1或2的概率以及1、2、3、4、5或6的点数概率更感兴趣。这两个同时考虑的随机变量的概率分布写作P(N,D),称作联合概率分布

如果一直加入越来越多的试验和变量,我们可以写出一个很长很复杂的联合概率分布。例如,我们可能对明天下雨的概率,股市上涨的概率,以及明天上班路上高速堵车的概率感兴趣。这是一个复杂的例子但是没有实际意义。我们几乎可以确定股市和天气不会有依赖关系。然而,交通状况和天气状况是密切关联的。我可以写出分布P(W,M,T)——天气、股市、交通——但是它似乎有点过于复杂了。而事实并非如此,这也是本书要一直探讨的话题。

一个概率图模型就是一个联合概率分布。除此以外,并无他物。

联合概率分布的最后一个重要概念是边缘化(Marginalization)。当你考察几个随机变量的概率分布,即联合概率分布时,你也许想从分布中消除一些变量,得到较少变量的分布。这个操作很重要。联合分布P(X,Y)的边缘分布P(X)可以通过下列操作获得:

其中我们按照y所有可能的取值汇总概率。通过这个操作,你可以从P(X,Y)消除Y。作为练习,可以考虑一下这个概率与之前看到的两个不相干事件概率之间的关系。

对于数学见长的读者,当Y是连续值时,边缘化可以写作

这个操作非常重要,但对于概率图模型也很难计算。几乎所有的概率图模型都试图提出有效的算法,来解决这个问题。多亏了这些算法,我们可以处理现实世界里包含许多变量的复杂而有效的模型。

让我们继续探讨概率图模型的一些基本概念。我们看到了边缘化的概念,它很重要,因为当有一个复杂模型的时候,你可能希望从一个或者少数变量中抽取信息。此时就用上边缘化的概念了。

但是最重要的两个概念是条件概率和贝叶斯规则。

条件概率是指在知道其他事件发生的条件下当前事件的概率。很明显,两个事件必须某种程度的依赖,否则一个事件的发生不会改变另一个事件:

这就是条件概率。更形式化的,我们可以给出下列公式:

从这两个等式我们可以轻松地推导出贝叶斯公式:

这个公式是最重要的公式,它可以帮助我们转换概率关系。这也是拉普拉斯生涯的杰作,也是现代科学中最重要的公式。然而它也很简单。

在这个公式中,我们把P(X|Y)叫作是给定YX的后验分布。因此,我们也把P(X)叫作先验分布。我们也把P(Y|X)叫做似然率,P(Y)叫做归一化因子。

我们再解释一下归一化因子。回忆一下:P (X,Y )= P (Y |X) P (X )。而且我们有,即旨在消除(移出)联合概率分布中单个变量的边缘化。

因此基于上述理解,我们可以有

借助简单的代数技巧,我们可以把贝叶斯公式改写成一般的形式,也是最方便使用的形式:

这个公式之美,以至于我们只需要给定和使用P(Y|X)和P(X),也就是先验和似然率。虽然形式简单,分母中的求和正如以后所见,可能是一个棘手的问题,复杂的问题也需要先进的技术。

理解贝叶斯公式

现在我们有XY两个随机变量的贝叶斯公式,让我们改写成另外两个变量的形式。毕竟,用什么字母并不重要,但是它可以给出公式背后的自然理解:

这些概念背后的直觉逻辑如下:

这个公式也给出了更新变量θ信念的前向过程。使用贝叶斯规则可以计算θ新的分布。如果又收到了新的信息,我们可以一次又一次更新信念。

贝叶斯规则的第一个例子

在这一部分中,我们会看到第一个R语言的贝叶斯程序。我们会定义离散随机变量,也就是随机变量只能取预定义数量的数值。假设我们有一个制作灯泡的机器。你想知道机器是正常工作还是有问题。为了得到答案你可以测试每一个灯泡,但是灯泡的数量可能很多。使用少量样本和贝叶斯规则,你可以估计机器是否在正常的工作。

在构建贝叶斯模型的时候,我们总是需要建立两个部件:

在这个例子中,我们不需要特殊的程序包;我们只需要编写一个简单的函数来实现贝叶斯规则的简单形式。

先验分布是我们关于机器工作状态的初始信念。我们确定了第一个刻画机器状态的随机变量M。这个随机变量有两个状态{working,broken}。我们相信机器是好的,是可以正常工作的,所以先验分布如下:

P(M= working)=0.99

P(M= broken)=0.01

简单地说,我们对于机器正常工作的信念度很高,即99%的正常和1%的有问题。很明显,我们在使用概率的贝叶斯思想,因为我们并没有很多机器,而只有一台机器。我们也可以询问机器供应商,得到生产正常机器的频率信息。我们也可以使用他们提供的数字,这种情况下,概率就有了频率派的解释。但是,贝叶斯规则在所有理解下都适用。

第二个变量是L,是机器生产的灯泡。灯泡可能是好的,也可能是坏的。所以这个随机变量包含两个状态{good,bad}。

同样,我们需要给出灯泡变量L的先验分布:在贝叶斯公式中,我们需要给出先验分布和似然率分布。在这个例子中,似然率是P(L|M),而不是P(L)。

这里我们事实上需要定义两个概率分布:一个是机器正常M=working时的概率,一个是机器损坏M=broken时的概率。我们需要回答两遍:

让我们给出最可能的猜测,不管是支持贝叶斯派还是频率派,因为我们有下列统计:

P(L=good |M= working)=0.99

P(L=bad |M= working)=0.01

P(L=good |M= broken)=0.6

P(L=bad |M= broken)=0.4

我们相信,如果机器正常,生产100个灯泡只会有一个是坏的,这比之前说的还要高些。但是在这个例子中,我们知道机器工作正常,我们期望非常高的良品率。但是,如果机器坏掉,我们认为至少40%的灯泡都是坏的。现在,我们已经完整地刻画了模型,并可以使用它了。

使用贝叶斯模型是要在新的事实可用时计算后验分布。在我们的例子中,我们想知道,在已知最后一个灯泡是坏的情况下机器是否可以正常工作。所以,我们想计算P(M|L)。我们只需要给出P(M)和P(L|M),最后只需用一下贝叶斯公式来转换概率分布。

例如,假设最后生成的灯泡是坏的,即L=bad。使用贝叶斯公式我们有:

P(M=working|L=bad)=

正如所见,机器正常工作的概率是71%。这个值比较低,但是符合机器依然正常的直观感觉。尽管我们收到了一个坏灯泡,但也仅此一个,也许下一个就好了。

让我们重新计算同样的问题,其中机器正常与否的先验概率和之前的相同:50%的机器工作正常,50%的机器工作不正常。结果变成:

机器有2.4%的概率正常工作。这就很低了。确实,给定机器质量后,正如建模成似然率,机器似乎要生产出坏灯泡。在这个例子中,我们并没有做有关机器正常的任何假设。生产出一个坏灯泡可以看作出问题的迹象。

贝叶斯规则的第一个R语言例子

看了之前的例子,有人会问第一个有意义的问题:如果观察多个坏灯泡我们需要怎么办?只看到一个坏灯泡就说机器需要维修,这似乎有些不合情理。贝叶斯派的做法是使用后验概率作为新的概率,并在序列中更新后验分布。然后,徒手做起来会很繁重,我们会编写第一个R语言贝叶斯程序。

下列代码是一个函数,计算给定先验分布、似然率和观察数据序列后的后验概率。这个函数有3个变量:先验分布、似然率和数据序列。priordata是向量,likelihood是矩阵:

prior <-c(working =0.99, broken =0.01)  
likelihood <-rbind( 
    working =c(good =0.99, bad =0.01), broken =c(good =0.6,  
    bad =0.4))  
data <-c("bad", "bad", "bad", "bad")

所以我们定义了3个变量,包含工作状态workingbrokenprior,刻画每个机器状态(workingbroken)的likelihood,灯泡变量L上的distribution。因此一共有4个值,R矩阵类似于之前定义的条件概率:

likelihood 
         good   bad 
working  0.99  0.01 
broken   0.60  0.40

data变量包含观察到的,用于测试机器和计算后验概率的灯泡序列。因此,我们可以定义如下贝叶斯更新函数:

bayes < -function(prior, likelihood, data)  
{ 
  posterior < -matrix(0, nrow =length(data), ncol =length(prior))  
  dimnames(posterior) < -list(data, names(prior))  

  initial_prior < -prior 
  for (i in 1:length(data))  
  { 
    posterior[i, ] < - 
      prior *likelihood[, data[i]]/ 
      sum(prior *likelihood[,data[i]])  

    prior < -posterior[i, ]  
  } 

  return(rbind(initial_prior, posterior))  
}

这个函数做了下列事情:

最终,函数返回了一个矩阵,包含初始先验概率和所有后续后验概率。

让我们多运行几次,理解一下工作原理。我们使用函数matplot绘出两个分布的演化情况。一个是机器正常(绿色线)的后验概率,一个是机器故障(红色线)的后验概率,如图1-1所示。

matplot(bayes(prior, likelihood, data), t ='b', lty =1, pch =20,  
col =c(3, 2))  

图1-1

结果可以从图中看到:随着坏灯泡的增多,机器正常的概率快速下降(实线或绿色线)[2]

我们原本希望100只灯泡中只有1个坏灯泡,不要太多就好。所以这个机器现在需要维护了。红色线或虚线表示机器有问题。

如果先验概率不同,我们可以看到不同的演化。例如,假设我们不知道机器是否可以正常工作,我们为每一种情况指认相同的概率:

prior < -c(working =0.5, broken =0.5)  

再次运行代码:

matplot(bayes(prior, likelihood, data), t ='b', lty =1, pch =20,  
col =c(3, 2))  

我们又得到了一个快速收敛的曲线,其中机器有问题的概率很高。这对于给定一批坏灯泡的情形来说,并不意外,如图1-2所示。

图1-2

如果一直变换数据,我们可以看到不同的行为。例如,假设机器正常工作的概率是99%。我们观察10个灯泡,其中第一个灯泡是坏的。我们有R代码:

prior =c(working =0.99, broken =0.01)  
data =c("bad", "good", "good", "good", "good", "good", "good", 
"good", "good", "good") 
matplot(bayes(prior, likelihood, data), t ='b', pch =20, col =c(3, 2))  

结果如图1-3所示。

图1-3

算法在第一个灯泡处犹豫了一下。因为这么好的机器,不大可能生产出一个坏灯泡。但是然后它又收敛到很高的概率,因为好灯泡的序列不会预示任何问题。

我们的第一个R语言贝叶斯模型就完成了。本章的其他部分,会介绍如何创建带有多于两个随机变量现实世界的模型,以及如何解决两个重要问题:

细心的读者也许会问:刚才看到的这个简单的算法可以解决推断问题吗?它确实可以,但是只能在有两个离散变量的时候。这有些过于简单,而无法捕捉现实世界的复杂性。我们会介绍本书的核心内容和执行贝叶斯推理的主流工具:概率图模型。

在本章的最后一部分,我们会介绍概率图模型,作为原生框架支持通过简单的模块生成复杂的概率模型。这些复杂模型通常对于要解决的复杂任务是必需的。而复杂并不意味着混乱,简单的事情是最好、最有效的。复杂是指为了表示和解决拥有很多输入、部件或者数据的任务,我们需要一个不完全平凡的模型,但是要满足足够的复杂度。

这个复杂的模型可以分解成几个相互交互的简单问题。最终,最简单的构建模块是一个变量。这个变量有一个随机值,或者像之前部分看到的带有不确定性的一个值。

如果你还记得,我们看到使用概率分布表示复杂概念是有可能的。当我们有许多随机变量时,我们把这个分布叫作联合分布。有时拿到几百个甚至上千个更多的随机变量并非不可能。表示这么庞大的分布是非常困难的,在大多数情况下也是不可能的。

例如,在医学诊断中,每一个变量表示一个症状。我们可以拿到许多这样的变量。其他变量可以表示病人的年龄、性别、体温、血压等。我们可以使用许多不同的变量表示病人状态。我们也可以加入其他信息,例如最近的天气条件,病人的年龄和饮食状况。

从这个复杂的系统中,我们想解决两个问题:

然而,还有一个重要的问题:在这个模型中,我们想利用其他重要的知识,甚至是最重要的知识之一:不同模型部件之间的交互。换句话说,不同随机变量之间的依赖。例如,症状和疾病之间有明显的依赖关系。另外,饮食和症状之间的依赖关系比较遥远,或者通过其他变量例如年龄、性别有所依赖。

最终,在这个模型中完成的所有推理都天然地带有概率的性质。从对变量X的观察,我们想推出其他变量的后验分布,得到它们的概率而不是简单的是或不是的回答。有了这个概率,我们可以拿到比二元响应更丰富的回答。

让我们做一个简单的计算。假设我们有两个二元随机变量,比如一个是在本章上一节看到的变量。我们把它们命名为XY。这两个变量的联合概率分布是P(X,Y)。它们是二元变量,因此我们可以为每一个取值,为简便起见称之为x1x2y1y2

我们需要给定多少概率值?一共有4个,即P(X= x1, Y= y1)、P(X= x1, Y= y2)、P(X= x2, Y= y1)和P(X= x2, Y= y2)。

假设我们不止有两个二元随机变量,而是10个。这还是一个非常简单的模型,对吧?我们把这些变量叫作X1X2X3X4X5X6X7X8X9X10。这种情况下,我们需要提供210=1 024个值来确定我们的联合概率分布。如果我们还有10个变量,也就是一共20个变量该怎么办?这还是一个非常小的模型。但是我们需要给定220=1 048 576个值。这已经超过了一百万个值了。因此对于这么简单的模型,建模任务已经变得几乎不可能了!

概率图模型正是简洁地描述这类模型的框架,并支持有效的模型构建和使用。事实上,使用概率图模型处理上千个变量并不罕见。当然,计算机模型并不会存储几十亿个值,但是计算机会使用条件独立,以便模型可以在内存中处理和表示。而且,条件独立给模型添加了结构知识。这类知识给模型带来了巨大的不同。

在一个概率图模型中,变量之间的知识可以用图表示。这里有一个医学例子:如何诊断感冒。这只是一个示例,不代表任何医学建议。为了简单,这个例子做了极大的精简。我们有如下几个随机变量:

因为每一个症状都有不同的程度,所以我们很自然地使用随机变量来表示这些症状。例如,如果病人的鼻子有点堵塞,我们会给这个变量指派,例如60%。即P(N=blocked)=0.6和P(N=notblocked)=0.4。

在这例子中,概率分布P(Se,N,H,S,C,Cold)一共需要4×25=128个值(4个季节,每一个随机变量取2个值)。这已经很多了。坦白讲,这已经很难确定诸如“鼻子不堵塞的概率”“病人头痛和打喷嚏等的概率”。

但是,我们可以说头痛与咳嗽或鼻子堵塞并不是直接相关,除非病人得了感冒。事实上,病人头痛有很多其他原因。

而且,我们可以说季节打喷嚏鼻子阻塞有非常直接的影响,或者咳嗽对于头痛的影响很少或没有。在概率图模型中,我们会用图表示这些依赖关系。如图1-4所示,每一个随机变量都是图中的节点,每一个关系都是两个节点间的箭头。

图1-4

如图1-4所示,概率图模型中的每一个节点间都存在有向关系,即箭头。我们可以使用这种方式来简化联合概率分布,以便概率可以追踪。

使用图作为模型来简化复杂(或者甚至混乱)的分布有诸多好处:

在之前的普通感冒诊断的例子中,我们定义了一个简单的模型,包含变量Se、N、H、S、CCold。我们看到,对于这样一个简单的专家系统,我们就需要128个参数!

我们还看到,我们可以基于常识或者简单的知识做出几个独立假设。在以后的内容中,我们会看到如何从数据集中发现这些假设(也叫作结构学习)。

所以我们可以做出假设,重写联合概率分布:

P(Se,N,H,S,C,Cold)

=P(Se)P(S|Se,Cold)P(N|Se,Cold)P(Cold)P(C|Cold)P(H|Cold)

在这个分布中,我们进行了分解。也就是说,我们把原来的联合概率分布表示为一些因子的乘积。在这个例子中,因子是更加简单的概率分布,例如P(C|Cold),病人感冒的情况下咳嗽的概率。由于我们可以把所有的变量看作二元的(除了季节,它有4个取值),每一个小的因子(分布)只需要确定少量的参数:4+23+23+2+22+22=30。我们只需要30个简单的参数,而不是128个!这是个巨大的改进。

我说过,参数非常容易确定,不管是通过手工还是根据数据。例如,我们不知道病人是否得了感冒,因此我们可以给变量Cold指派相同的概率,即P(Cold=true)=P(Cold=false)=0.5。

类似的,我们也很容易确定P(C|Cold),因为如果病人得了感冒(Cold = true),他很有可能咳嗽。如果他没有感冒,病人咳嗽的概率很低,但是不是零不能确定,因为还有其他可能的原因。

通常,有向概率图模型可以按照如下形式分解多个随机变量X1X2,...,Xn上的联合概率分布:

pa(Xi)是图中定义的变量Xi的父变量的子集。

图中的父变量很容易理解:当箭头从A指向B时,A就是B的父变量。一个节点可以有很多可能的子节点,也可以有很多可能的父节点。

有向模型非常适合建模需要表示因果关系的问题。它也非常适合参数学习,因为每一个局部概率分布都很容易学习。

我们在本章中多次提到了概率图模型可以使用简单的模块进行构建,并组合出更大的模型。在有向模型中,模块指的是小的概率分布P(Xi|pa(Xi))。

而且,如果我们想给模型扩展9个新的变量以及一些关系,我们只需简单扩展图形。有向概率图模型的算法适用于任何图形,不管什么样的规模。

尽管如此,并不是所有的概率分布都可以表示成有向概率图模型。有时,我们也有必要放松一些假设。

同时,注意到图必须是无环的很重要。这意味着,你不可能同时找到从AB的箭头和从BA的箭头,如图1-5所示。

图1-5

事实上,这个图并不表示之前定义的分解过程。它可能意味着AB的原因,同时B也是A的原因。这是矛盾的,也没有等价的数学表示。

当假设或者关系不是有向的,还存在第二种概率图模型的形式。它的边都是无向的。它也叫作无向概率图模型或者马尔科夫网络。

无向概率图模型可以按照如下形式分解多个随机变量X1X2,...,Xn上的联合概率分布:

这个公式的解释如下:

图1-6

在上图中,我们有4个节点,并且函数ϕc定义在子集,也就是极大团{ABC}和{A,D}上。因此这里的概率分布并不复杂。这种类型的模型在计算机视觉、图像处理、财经和其他变量间关系遵循一定模式的领域都有广泛的应用。

现在来讨论一下概率图模型的应用。其实这些应用用几百页去讲述也很难涵盖其中的一部分。正如我们看到的,概率图模型是一种建模复杂概率模型的很有用的框架,可以使得概率易于理解和处理。

在这部分中,我们会使用之前的两个模型:灯泡机和感冒诊断。

回忆一下,感冒诊断模型有下列分解形式:

P(Se,N,H,S,C,Cold)=P(Se)P(S|Se,Cold)P(N|Se, Cold)P(Cold)P(C|Cold)P(H|Cold)

而灯泡机仅仅通过两个变量定义:LM。分解形式也很简单。

P(L,M)=P(MP(L|M)

对应分布的图模型也很简单,如图1-7所示。

图1-7

为了表示概率图模型,我们会使用R程序包gRain。安装如下:

source("http://bioconductor.org/biocLite.R") 
biocLite() 
install.packages("gRain")

需要注意,这个安装过程可能会持续几分钟,因为这个程序包还依赖于许多其他的程序包(尤其是我们经常用到的gRbase程序包),而且提供了对图模型的一些基本操作函数。当程序包安装好后,你可以加载:

library("gRbase") 

首先,我们想定义一个带有变量A、B、C、D、E的简单无向图:

graph < -ug("A:B:E + C:E:D")
class(graph)  

我们定义了带有团A、BE以及另一个团C、ED的图模型。这形成了一个蝴蝶状的图。它的语法很简单:字符串的每一个团用+分开,每一个团使用冒号分隔的变量名定义。

接着我们需要安装图的可视化程序包。我们会使用流行的Rgraphviz。要安装可以输入:

install.packages("Rgraphviz") 
plot(graph) 

你可以得到第一个无向图,如图1-8所示。

图1-8

接着,我们希望定义一个有向图。假设我们依然有变量{A, B, C, D, E}:

dag < -dag("A + B:A + C:B + D:B + E:C:D")
dag 
plot(dag)  

语法依然很简单:没有父节点的节点单独表示,例如A,否则父节点通过冒号分隔的节点列表刻画。

这个程序包提供了多种定义图模型的语法。你也可以按照节点的方式构建图模型。我们会在本书中用到几种表示法,以及一个非常著名的表示法:矩阵表示法。一个图模型可以等价地表示为一个方阵,其中每一行和每一列表示一个节点。如果节点间存在边,那么矩阵的系数是1,否则为0。如果图是无向的,矩阵会是对称的;否则可以是任何样式。

最终,通过第二个例子我们可以得到图1-9所示的图模型。

图1-9

现在我们想为灯泡机问题定义一个简单的图模型,并给出数值概率。我们再做一遍计算,看看结果是否一致。

首先,我们为每一个节点定义取值:

machine_val < -c("working", "broken") 
light_bulb_val < -c("good", "bad") 

然后为两个随机变量定义百分比数值:

machine_prob < -c(99, 1)  
light_bulb_prob < -c(99, 1, 60, 40) 

接着,使用gRain定义随机变量:

M < -cptable(~machine, values = machine_prob, levels = machine_val)  
L < -cptable(~light_bulb |machine, values = light_bulb_prob, levels = light_
bulb_val)  

这里,cptable表示条件概率表:它是离散型随机变量概率分布的内存表示。我们会在第2章精确推断中再次讨论这个表示法。

最后,我们可以构建新的概率图模型。当我们在第2章精确推断中研究推断算法例如联结树算法(Junction Tree Algorithm)时,这种表示法更加易于理解:

plist < -compileCPT(list(M, L))  
plist 

打印网络的时候,结果如下:

CPTspec with probabilities:  
 P( machine )  
 P( light_bulb |machine )  

这里,可以清楚地看到之前定义的概率分布。如果我们打印出变量的分布,我们可以再次看到之前的结果:

plist$machine 
plist$light_bulb

输出的结果如下:

>plist$machine 
machine 
working  broken 
   0.99    0.01 
>plist$light_bulb 
         machine 
light_bulb working broken 
      good    0.99    0.6 
      bad     0.01    0.4

现在我们从模型中找出后验概率。首先,给模型输入证据(即我们观察到一个坏灯泡),操作如下:

net < -grain(plist)  
net2 < -setEvidence(net, evidence =list(light_bulb ="bad")) 
querygrain(net2, nodes =c("machine")) 

程序包会借助推断算法计算结果,并输出下列结果:

$machine 
machine 
  working    broken 
0.7122302 0.2877698

这个结果与之前使用贝叶斯方法得到的结果完全相同。现在我们可以创建更加强大的模型,以及针对不同的问题应用不同的算法。这就是下一章关于图模型上精确推断的内容。

在第1章中,我们学到了概率论的基础概念。

我们看到了如何以及为什么使用概率来表示数据和知识的不确定性,同时我们还介绍了贝叶斯公式。这是计算后验概率的最重要的公式。也就是说,当新的数据可用时,要更新关于一个事实的信念和知识。

我们看到了什么是联合概率分布,同时看到它会很快变得很复杂以至于难以处理。我们学到了概率图模型的基础知识,它是对概率模型进行易于处理、高效和简单建模的原生框架。最后,我们介绍了概率图模型的不同类型,并学到如何使用R程序包来编写第一个模型。

在下一章中,我们会学到概率图模型上执行贝叶斯推断的一系列算法,即给模型提出问题和寻求答案。我们会介绍R程序包的新特性,同时我们会学到这些算法如何工作,以及高效的执行。

[1] 原书此处为厘米,似乎有问题。

[2] 原书中“as the bad light bulbs arrive, the probability that the machine will fail quickly falls”,应有误。


相关图书

R语言编程:基于tidyverse
R语言编程:基于tidyverse
R语言医学多元统计分析
R语言医学多元统计分析
Python与R语言数据科学实践
Python与R语言数据科学实践
R数据挖掘实战
R数据挖掘实战
R语言机器学习实战
R语言机器学习实战
R语言高效能实战:更多数据和更快速度
R语言高效能实战:更多数据和更快速度

相关文章

相关课程