在这一章中,我们将学习定向图模型,也称为贝叶斯网络。我们从什么(我们试图解决的问题)、如何(图表示)、为什么(因式分解以及 CPD 和图因式分解的等价性)开始,然后继续使用 Libpgm Python 库来玩一个小的贝叶斯网。
在我们进入贝叶斯网络之前,让我们学习一些图术语。图 G 由一组节点(也称为顶点)和另一组边组成。连接一对节点的边可以有两种类型:有向的(由表示)和无向的(由表示)。图也可以表示为邻接矩阵,在无向图的情况下,如果位置 G(i,j) 包含 1 ,则表示 i 和 j 顶点之间的边。在有向图的情况下, 1 或 -1 的值表示边的方向。
在许多情况下,我们对所有边都是有向或无向的图感兴趣,导致它们分别被称为有向图或无向图。
有向图中节点的父节点是具有终止于的输出边的一组节点。
节点的子节点是具有离开的输入边的节点集
节点的度是它参与的边的数量。
团是一组节点,其中每对节点由一条边连接。最大团是指如果包含任何其他节点,则失去团属性的团。
如果存在从一个节点经过其他节点后返回自身的路径,则称之为循环或循环。
一个有向无环图 ( DAG ) 是一个没有循环的图。
一个部分有向无环图 ( PDAG ) 是一个既可以包含有向边又可以包含无向边的图。
森林是一组树。
我们将很快开始探索使用 Python 的 GMs,这是回顾您的 Python 安装的好时机。本书示例的推荐基础 Python 安装是 IPython,所有平台都可以使用。有关特定平台的文档,请参考 IPython 网站。
我们还将使用多个 Python 库来探索图模型的各个领域。除非另有说明,安装 Python 库的常用方法是使用pip install <packagename>
或easy_install <packagename>
。
要使用本章中的代码,请安装 Lipgm(https://pypi.python.org/pypi/libpgm)和 scipy(http://scipy.org/)。
图模型解决的关键问题之一是定义联合分布。我们来看一下求职面试的例子,有一定经验和教育程度的求职者在找工作。这位候选人也在申请进入高等教育项目。
我们正试图充分说明工作机会上的联合分配,这(根据我们的直觉)取决于工作面试的结果、候选人的经验和他的成绩(我们假设候选人被研究生院录取与工作机会无关)。三个随机变量 *{Offer,Experience,gradies }*取两个值(比如对于工作 Offer 取 yes 和 no,对于工作经历取高度相关和不相关),面试取三个值,联合分布会用一个 24 行的表来表示(即 2×2×2×3)。
每行包含该行中随机变量赋值的概率。虽然表的不同实例可能有不同的概率分配,但我们需要 24 个参数(每行一个)来编码表中的信息。然而,为了计算的目的,我们将只需要 23 个独立的参数。为什么我们要移除一个?由于概率之和等于 1,最后一个参数可以通过从已经找到的 23 个参数之和中减去 1 来计算。
| |经验
|
等级
|
采访
|
提供
|
可能性
| | --- | --- | --- | --- | --- | --- | | Zero | Zero | Zero | Zero | Zero | 0.7200 | | one | Zero | Zero | Zero | one | 0.0800 | | Two | Zero | Zero | one | Zero | 0.0720 | | three | Zero | Zero | one | one | 0.1080 | | 。 | | | | | | | 。 | | | | | | | 行省略 | | Twenty-one | one | one | one | one | 0.1200 | | Twenty-two | one | one | Two | Zero | 0.0070 | | Twenty-three | one | one | Two | one | 0.6930 |
前面的联合分布是printjointdistribution.ipynb
IPython 笔记本的输出,它打印随机变量的经验、成绩、面试和报价的所有排列,以及它们的概率。
观察上表应该清楚,由于以下原因,很难获得完全指定的联合分布:
如何才能避免必须指定联合分配?我们可以通过使用独立参数的概念来做到这一点,我们将在下面的例子中探讨这一点。
等级和录取上的联合分布 P 如下(在本书中,上标 0 和 1 表示低分和高分):
|等级
|
准许进入
|
概率
| | --- | --- | --- | | S 0 | A 0 | Zero point six six five | | S 0 | A 1 | Zero point zero three five | | S 1 | A 0 | Zero point zero six | | S 1 | A 1 | Zero point two four |
当我们从因果关系的角度对研究生录取进行推理时,很明显录取取决于成绩,也可以用条件概率表示如下:
上式要求的参数个数为三个,一个,和各两个。由于这是一个简单的分布,所以条件分布和联合分布所需的参数数量是相同的,但是让我们观察整个网络,看看条件参数化是否有所不同:
我们如何计算前图中贝叶斯网的参数数量?让我们一次一个参数地浏览每个条件概率表。体验和等级取两个值,因此每个需要一个独立的参数。面试表有 12 个(3×4)参数。然而,每行加起来是 1,因此,每行需要两个独立的参数。整个表需要 8 个(2×4)独立参数。类似地, Offer 表有 6 个条目,但每行只需要 1 个独立参数,这样就有 3 个(1×3)独立参数。因此,参数总数为 1(经验)+ 1(等级)+ 12(面试)+ 3(报价)总计为 17,这比 24 个参数要少得多,以充分指定联合分布。因此,贝叶斯网络中的独立性假设有助于我们避免指定联合分布。
贝叶斯网络是一种可以表示为有向无环图的结构,它所包含的数据可以从以下两个角度来看:
我们将探讨到目前为止已经看到的求职面试例子中的两个想法(顺便说一下,这是一个贝叶斯网络)。
贝叶斯网络的模块化结构是一组局部概率模型,表示每个变量对其父变量的依赖性质(柯勒等人 3.2.1.1)。经验和等级各有一个概率分布,条件概率分布 ( CPD )分别存在于面试和报价中。给定分配给其父代的所有组合,CPD 指定随机变量的分布。因此,给定贝叶斯网络的模表示是每个随机变量的 CPD 集合。
条件独立的观点来自不同随机变量之间的边缘(我们的直觉得出),在那里,我们假设工作面试的要求必须取决于候选人的经验以及他在学位课程中获得的分数,而获得工作机会的概率仅取决于工作面试的结果。
链式法则允许我们将联合分布定义为因素的乘积。在求职面试的例子中,利用概率的链式法则,我们可以写出以下公式:
在上式中, E 、 G 、 I 、 O 分别代表体验、等级、面试、 Offer 。然而,我们可以使用由图编码的条件独立性假设来重写联合分布,如下所示:
这是贝叶斯网络的链式规则的一个例子。更一般地说,我们可以这样写:
这里,是图 G 中的节点,是图 G 中节点的父节点
在本节中,我们将研究贝叶斯网络中使用的不同类型的推理。我们将使用 Libpgm 库创建一个贝叶斯网络。Libpgm 从具有特定格式的 JSON 格式的文件中读取网络信息,如节点、边和与每个节点相关联的 CPD 概率。这个 JSON 文件被读入NodeData
和GraphSkeleton
对象,以创建一个离散贝叶斯网络(顾名思义,这是一个贝叶斯网络,其中 CPD 取离散值)。TableCPDFactorization
对象是包装离散贝叶斯网络的对象,允许我们查询网络中的 CPD。本例中的 JSON 文件job_interview.txt
应该与 IPython 笔记本放在同一个文件夹中,以便自动加载。
下面的讨论使用整数 0、1 和 2 作为每个随机变量的离散结果,其中 0 是最差的结果。例如面试= 0 表示面试结果最差,面试= 2 表示结果最好。
我们将要探索的第一种推理叫做因果推理。最初,我们观察一个事件的先验概率不受任何证据的制约(对于这个例子,我们将集中于提供的随机变量)。然后我们引入对父变量之一的观察。与我们的逻辑推理一致,我们注意到,如果观察到一个事件的父母之一(相当于原因),那么我们对孩子的随机变量有更强的信念( Offer )。
我们首先定义一个函数来读取 JSON 数据文件,并创建一个可以用来运行概率查询的对象。以下代码来自Bayes net-Causal Reasoning.ipynb
IPython 笔记本:
from libpgm.graphskeleton import GraphSkeleton
from libpgm.nodedata import NodeData
from libpgm.discretebayesiannetwork import DiscreteBayesianNetwork
from libpgm.tablecpdfactorization import TableCPDFactorization
def getTableCPD():
nd = NodeData()
skel = GraphSkeleton()
jsonpath="job_interview.txt"
nd.load(jsonpath)
skel.load(jsonpath)
# load bayesian network
bn = DiscreteBayesianNetwork(skel, nd)
tablecpd=TableCPDFactorization(bn)
return tablecpd
我们现在可以使用specificquery
函数在我们定义的网络上运行推理查询。获得T4 优惠的先验概率是多少?请注意,概率查询采用两个字典参数:第一个是查询,第二个是证据集,由空字典指定,如以下代码所示:
tcpd=getTableCPD()
tcpd.specificquery(dict(Offer='1'),dict())
以下是前面代码的输出:
0.432816
大约是 43%,如果我们现在引入候选人成绩差的证据,如何改变获得录用的概率?我们将评估的值,如下代码所示:
tcpd=getTableCPD()
tcpd.specificquery(dict(Offer='1'),dict(Grades='0'))
以下是前面代码的输出:
0.35148
不出所料,这降低了被录取的概率,因为我们推断成绩差的学生不太可能被录取。添加候选人经验也低的进一步证据,我们评估,如下代码所示:
tcpd=getTableCPD()
tcpd.specificquery(dict(Offer='1'),dict(Grades='0',Experience='0'))
以下是前面代码的输出:
0.2078
正如预期的那样,在额外的证据上,它下降得更低,从 35%下降到 20%。
我们所看到的是,观察到的父随机变量的引入强化了我们的信念,这就导致了因果推理这个名字。
在下图中,我们可以看到因果推理和证据推理所采取的不同路径:
证据推理是当我们观察一个孩子变量的值时,我们希望推理它如何加强我们对它的父母的信念。我们将评估高Experience
的先验概率,如下代码所示:
tcpd=getTableCPD()
tcpd.specificquery(dict(Experience='1'),dict())
前面代码的输出如下:
0.4
我们现在引入候选人面试良好的证据,并评估*P(Experience = 1 | Interview = 2)*的值,如下代码所示:
tcpd=getTableCPD()
print tcpd.specificquery(dict(Experience='1'),dict(Interview='2'))
前面代码的输出如下:
0.864197530864
我们看到,如果候选人在面试中得分较高,那么候选人经验丰富的概率就会增加,这遵循的推理是候选人必须有良好的经验或学历,或者两者都有。在证据推理中,我们从结果推理到原因。
顾名思义,因果间推理是一种推理类型,其中单个效应的多个原因相互作用。我们首先确定具有高的相关经验的先验概率;因此,我们将评估 P(Experience=1) ,如下代码所示:
tcpd=getTableCPD()
tcpd.specificquery(dict(Experience='1'),dict())
以下是前面代码的输出:
0.4
通过介绍面试进行得非常顺利的证据,我们认为候选人一定很有经验。我们现在将评估的值,如以下代码所示:
tcpd=getTableCPD()
tcpd.specificquery(dict(Experience='1'),dict(Interview='2'))
以下是前面代码的输出:
0.864197530864
贝叶斯网络证实了我们认为的是真的(候选人是有经验的),高经验的概率从 0.4 上升到 0.86。现在,如果我们引入证据,证明候选人没有取得好成绩,但仍然设法在面试中获得好成绩,我们可能会得出结论,候选人一定非常有经验,他的成绩根本不重要。我们将评估的值,如下代码所示:
tcpd=getTableCPD()
tcpd.specificquery(dict(Experience='1'),dict(Interview='2',Grades='0'))
前面代码的输出如下:
0.909090909091
这证实了我们的预感,即使高经验的概率只上升了一点点,但它加强了我们对候选人高经验的信念。这个例子展示了求职面试节点的两个家长之间的相互作用,分别是经验和学位分数,并向我们展示了如果我们知道一个结果背后的原因之一,就会降低另一个原因的重要性。换句话说,我们在观察候选人的经历时解释了成绩差的原因。这种现象俗称解释走。
下图显示了因果推理中涉及的节点之间的交互路径:
贝叶斯网络通常是将独立事件画在上面,影响从上到下流动(类似于求职面试的例子)。回忆因果推理从上到下流动,证据推理从下到上流动,因果间推理横向流动,这可能是有用的。
了解了箭头的方向表示在贝叶斯网络中一个节点可以影响另一个节点之后,让我们看看在贝叶斯网络中如何确切地影响流量。我们可以看到,成绩最终会影响工作机会,但是在一个非常大的贝叶斯网络的情况下,说明叶节点受到贝叶斯网络顶部所有节点的影响是没有帮助的。是否存在影响不流动的情况?我们将在下表中看到解释影响力流动的简单规则:
|没有观察到变量
|
观察到了 y
| | --- | --- | | | | | | | | | | | | |
上表描述了三个节点 X 、 Y 和 Z 之间的开放和封闭活动轨迹。在第一列中,没有观察到变量,而在第二列中,观察到了 Y 。
我们将首先考虑没有观察到随机变量的情况。考虑上表第一列中的节点链。请注意,前三行中的规则允许影响从第一个节点流向最后一个节点。
影响可以沿着边缘的路径流动,即使这些链延伸了更长的序列。必须指出,影响的流动不受连接它们的环节的方向性的限制。
但是,有一种情况我们要注意,那就是所谓的 V 型结构,——之所以这么叫,可能是因为边缘的方向指向内部。
在这种情况下,影响不能从 X 流向 Z,因为它被 y 阻挡。在更长的链中,影响将流动,除非它被 V 形结构阻挡。
在这种情况下,由于在 X 处的 V 型结构,影响可以从和流出,但不能穿过节点 X
我们现在可以陈述活动轨迹(影响力)的概念。如果没有观察到证据,踪迹如果不包含 V 型结构,则是活动的。如果两个变量之间存在多个轨迹,如果没有任何轨迹是活动的,则它们是有条件独立的。
现在让我们看看第二种情况,我们确实观察到了证据变量。如果我们将案例与之前的链条进行比较,更容易理解,在这里我们观察到随机变量 Y 。
上表中显示的笑脸轨迹表示活动轨迹,其他轨迹表示受阻轨迹。可以观察到,证据的引入只是否定了先前活跃的踪迹,如果先前阻塞的 V 型结构存在,它就会打开。
我们现在可以声明,如果证据集中存在中间节点或其任何 V 型结构的后代(例如, Y 或其后代在)时,给定证据的踪迹将是活动的。换句话说,观察 Y 或者它的任何一个子体,都会打开阻塞的 V 型结构,使其成为一个活跃的踪迹。此外,从下表中可以看出,公开的线索会因证据的引入而受阻,反之亦然。
在本节中,我们将使用求职者示例来了解 D-分离。在执行因果推理的过程中,我们将查询工作机会,并将在工作机会的父项中引入观察到的变量,以验证我们在上一节中看到的活动轨迹的概念。以下代码来自D-separation.ipynb
IPython 笔记本。
我们首先在没有其他观察变量的情况下查询工作机会,如以下代码所示:
getTableCPD().specificquery(dict(Offer='1'),dict())
前面代码的输出如下:
0.432816
从主动跟踪规则中我们知道,观察Experience
应该会改变报价的概率,如下代码所示:
getTableCPD().specificquery(dict(Offer='1'),dict(Experience='1'))
前面代码的输出如下:
0.6438
根据输出,它发生变化。现在,让我们添加Interview
观察变量,如下代码所示:
getTableCPD().specificquery(dict(Offer='1'),dict(Interview='1'))
前面代码的输出如下:
0.6
我们得到的Offer
概率略有不同。从 D 分离规则中我们知道,观察Interview
应该会阻挡从Experience
到Offer
的活动轨迹,如下代码所示:
getTableCPD().specificquery(dict(Offer='1'),dict(Interview='1',Experience='1'))
前面代码的输出如下:
0.6
观察Offer
的概率从0.6
没有变化,尽管添加了Experience
变量被观察。我们可以添加Interview
对象的父变量的其他值,如下面的代码所示:
query=dict(Offer='1')
results=[getTableCPD().specificquery(query,e) for e in [dict(Interview='1',Experience='0'),
dict(Interview='1',Experience='1'),
dict(Interview='1',Grades='1'),
dict(Interview='1',Grades='0')]]
print results
前面代码的输出如下:
[0.6, 0.6, 0.6, 0.6]
前面的代码显示,一旦观察到Interview
变量,Experience
和Offer
之间的活动轨迹就会被阻断。因此Experience
和Offer
在给出Interview
的时候是有条件独立的,也就是说观察面试父母的价值观,Experience
和Grades
并不会有助于改变报价的概率。
让我们来看看网络中唯一的 V 型结构,体验 → 面试☆等级,看看观察到的证据对活跃的踪迹有什么影响。
getTableCPD().specificquery(dict(Grades='1'),dict(Experience='0'))
getTableCPD().specificquery(dict(Grades='1'),dict())
前面代码的结果如下:
0.3
0.3
根据的 D-分离规则,面试节点在Experience
和Grades
之间是一个 V 型结构,它阻挡了它们之间的主动踪迹。前面的代码显示了观测变量Experience
的引入对等级的概率没有影响。
getTableCPD().specificquery(dict(Grades='1'),dict(Interview='1'))
以下是前面代码的输出:
0.413016270338
以下代码应激活Experience
和Grades
之间的轨迹:
getTableCPD().specificquery(dict(Grades='1'),dict(Interview='1',Experience='0'))
getTableCPD().specificquery(dict(Grades='1'),dict(Interview='1',Experience='1'))
前面代码的输出如下:
0.588235294118
0.176470588235
前面的代码现在显示了在Experience
和Grades
之间存在活动轨迹,其中改变观察到的Experience
值会改变Grades
的概率。
到目前为止,我们已经理解了图 G 是分布 P 的表示,我们可以用下面的方式正式定义图 G 和分布 P 之间的关系。
如果 G 是随机变量上的图,我们可以说分布 P 在 G 上分解,如果。这里,是的父节点。换句话说,联合分布可以定义为每个随机变量的乘积,当它的父变量被给定时。
因式分解和独立性之间的相互作用是一个有用的现象,它允许我们声明,如果分布在一个图上因式分解,并且给定两个节点是 D 分离的,则分布满足这些独立性()。
或者,我们可以声明图 G 是分布 P 的独立图(I-图),如果 P 在 G 上进行因子分解,那么我们可以从图中读取独立,而不管参数如何。一个 I-map 可能不会编码分布中的所有独立性。但是,如果该图满足分布中的所有依赖关系,则称之为完美图 ( P 图)。工作面试的图就是一个 I 图的例子。
我们可以这样总结:从以下两个角度可以看到一个图:
朴素贝叶斯模型是一个简单的独立性假设。我们使用朴素贝叶斯模型来执行二进制分类这里,我们被给予一组实例,其中每个实例由一组特征和一个类组成。分类中的任务是预测的正确类别,而其余的特征...都给了。
例如,给我们一组来自两个新闻组的新闻组帖子。给定一个特定的帖子,我们想要预测这个特定的帖子来自哪个新闻组。每个帖子都是一个由一袋单词组成的实例(我们假设单词的顺序无关紧要,只考虑单词的有无),因此特征表示单词的有无。
这里,我们将把朴素贝叶斯模型看作一个分类器。
朴素贝叶斯和求职者示例之间的区别在于,朴素贝叶斯之所以这么叫,是因为它做出了天真的条件独立性假设,并且模型将先验概率和单个条件概率的乘积分解,如下式所示:
虽然左边的术语是需要大量独立参数的联合分布(2 n+1 - 1,如果每个特征都是二进制值),但是右边的朴素贝叶斯表示只需要 2n+1 个参数,从而将参数的数量从指数(在典型的联合分布中)减少到线性(在朴素贝叶斯中)。
在新闻组示例的上下文中,我们有一组从alt.atheism
和sci.med
新闻组中提取的单词,如*{无神论者、医学、宗教、解剖学}* 。在这个模型中,你可以说每个单词出现的概率是只依赖于类别(即新闻组),而不依赖于帖子中的其他单词。很明显,这是一个过于简化的假设,但是已经证明它在功能数量大而实例数量少的领域中有相当好的性能,比如文本分类,我们将在 Python 程序中看到。
一旦我们看到特征之间的强相关性,分层贝叶斯网络就可以被认为是朴素贝叶斯模型的进化版本。
在朴素贝叶斯示例中,我们将使用 Scikit-learn 的朴素贝叶斯实现——一个机器学习库来对新闻组帖子进行分类。我们从 Scikit-learn 提供的数据集中选择了两个新闻组(alt.atheism
和sci.med
),我们将使用朴素贝叶斯来预测特定帖子来自哪个新闻组。以下代码来自Naive Bayes.ipynb
文件:
from sklearn.datasets import fetch_20newsgroups
import numpy as np
from sklearn.naive_bayes import MultinomialNB
from sklearn import metrics,cross_validation
from sklearn.feature_extraction.text import TfidfVectorizer
cats = ['alt.atheism', 'sci.med']
newsgroups= fetch_20newsgroups(subset='all',remove=('headers', 'footers', 'quotes'), categories=cats)
我们首先使用 Scikit-learn 提供的实用函数加载新闻组数据(这将从互联网下载数据集,可能需要一些时间)。newsgroup
对象是一张地图,新闻组帖子保存在data
键下,目标变量在newsgroups.target
,如下代码所示:
newsgroups.target
前面代码的输出如下:
array([1, 0, 0, ..., 0, 0, 0], dtype=int64)
由于特征是单词,我们使用术语频率-逆文档频率 ( Tfidf )将它们转换为另一种表示。Tfidf 的目的是不强调所有帖子中出现的单词(如“The”、“by”和“for”),而是强调特定类别独有的单词(如宗教和神创论,来自alt.atheism
新闻组)。我们可以通过创建一个TfidfVectorizer
对象,然后将所有新闻组数据转换为矢量表示来完成同样的工作,如下面的代码所示:
vectorizer = TfidfVectorizer()
vectors = vectorizer.fit_transform(newsgroups.data)
向量现在包含我们可以用作朴素贝叶斯分类器的输入数据的特征。一个形状查询显示它包含 1789 个实例,每个实例包含大约 24000 个特征,如下面的代码所示。然而,这些特征中的许多可以是 0,这表明这些单词没有出现在那个特定的帖子中:
vectors.shape
前面代码的输出如下:
(1789, 24202)
Scikit-learn 提供了几个版本的朴素贝叶斯分类器,我们将使用的那个叫做MultinomialNB
。由于使用分类器通常包括将数据集分为训练集、测试集和验证集,然后在训练集上进行训练,并在验证集上测试有效性,因此我们可以使用 Scikit-learn 提供的实用程序为我们做同样的事情。cross_validation.cross_val_score
函数自动将数据分割成多个集合,并返回f1
分数(衡量分类器准确性的指标),如以下代码所示:
clf = MultinomialNB(alpha=.01)
print "CrossValidation Score: ", np.mean(cross_validation.cross_val_score(clf,vectors, newsgroups.target, scoring='f1'))
前面代码的输出如下:
CrossValidation Score: 0.954618416381
我们可以看到,尽管假设当给定类时,所有特征都是有条件独立的,但分类器保持了 95%的良好f1
分数。
在这一章中,我们学习了条件独立性如何允许联合分布被表示为贝叶斯网络。然后,我们参观了推理的类型,了解了影响如何通过贝叶斯网络流动,并使用 Libpgm 探索了相同的概念。最后,我们使用一个简单的贝叶斯网络(朴素贝叶斯)来解决一个真实世界的文本分类问题。
在下一章中,我们将学习无向图模型或马尔可夫网络。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。