34

这就是我问这个问题的原因: 去年我编写了一些 C++ 代码来计算特定类型模型(由贝叶斯网络描述)的后验概率。该模型运行良好,其他一些人开始使用我的软件。现在我想改进我的模型。由于我已经为新模型编写了稍微不同的推理算法,因此我决定使用 python,因为运行时并不是至关重要的,python 可以让我编写更优雅和更易于管理的代码。

通常在这种情况下,我会在 python 中搜索现有的贝叶斯网络包,但我使用的推理算法是我自己的,我还认为这将是一个了解更多关于 python 良好设计的好机会。

我已经为网络图 (networkx) 找到了一个很棒的 python 模块,它允许您将字典附加到每个节点和每个边。本质上,这可以让我给出节点和边的属性。

对于特定网络及其观察数据,我需要编写一个函数来计算模型中未分配变量的可能性。

例如,在经典的“亚洲”网络(http://www.bayesserver.com/Resources/Images/AsiaNetwork.png)中,已知“X 射线结果”和“呼吸困难”的状态,我需要编写一个函数计算其他变量具有某些值的可能性(根据某些模型)。

这是我的编程问题: 我将尝试一些模型,将来我可能会想尝试另一种模型。例如,一种模型可能看起来与亚洲网络完全一样。在另一个模型中,可能会从“访问亚洲”到“患有肺癌”添加有向边。另一个模型可能使用原始的有向图,但给定“肺结核或癌症”和“患有支气管炎”节点的“呼吸困难”节点的概率模型可能不同。所有这些模型都将以不同的方式计算可能性。

所有模型将有大量重叠;例如,如果所有输入均为“0”,则进入“或”节点的多条边将始终为“0”,否则为“1”。但是有些模型的节点会在某个范围内采用整数值,而其他模型将是布尔值。

在过去,我一直在为如何编写这样的东西而苦苦挣扎。我不会说谎;有相当多的复制和粘贴代码,有时我需要将单个方法中的更改传播到多个文件。这次我真的很想花时间以正确的方式做到这一点。

一些选项:

  1. 我已经以正确的方式这样做了。先写代码,再问问题。复制和粘贴代码并为每个模型设置一个类会更快。世界是一个黑暗而杂乱无章的地方……
  2. 每个模型都是它自己的类,也是一般贝叶斯网络模型的子类。这个通用模型将使用一些将被覆盖的函数。Stroustrup 会感到自豪。
  3. 在同一个类中创建几个计算不同可能性的函数。
  4. 编写一个通用的 BayesianNetwork 库并将我的推理问题实现为该库读取的特定图表。节点和边应该被赋予像“Boolean”和“OrFunction”这样的属性,给定父节点的已知状态,可以用来计算不同结果的概率。这些属性字符串,例如“OrFunction”,甚至可以用来查找和调用正确的函数。也许几年后我会做出类似于 1988 年版 Mathematica 的东西!

非常感谢你的帮助。

更新: 面向对象的思想在这里有很大帮助(每个节点都有一组指定的特定节点子类型的前驱节点,并且每个节点都有一个似然函数,可以根据前驱节点的状态计算其不同结果状态的可能性等。 )。哎呀!

4

4 回答 4

22

我在业余时间一直在做这种事情。我想我现在正在处理同样问题的第三或第四个版本。我实际上正准备发布另一个版本的 Fathom (https://github.com/davidrichards/fathom/wiki),其中包含动态贝叶斯模型和不同的持久层。

当我试图明确我的答案时,它已经变得很长了。我为此道歉。这是我一直在解决这个问题的方式,这似乎回答了你的一些问题(有点间接):

我从 Judea Pearl 在贝叶斯网络中对信念传播的分解开始。也就是说,它是一个图表,其中包含来自父母的先验几率(因果支持)和来自孩子的可能性(诊断支持)。这样,基本类就只是一个 BeliefNode,就像你描述的那样,在 BeliefNode 之间有一个额外的节点,即 LinkMatrix。通过这种方式,我根据我使用的 LinkMatrix 类型明确选择我正在使用的可能性类型。它使解释信念网络之后的工作变得更容易,并使计算更简单。

我对基本 BeliefNode 所做的任何子类化或更改都将用于合并连续变量,而不是更改传播规则或节点关联。

我决定将所有数据保留在 BeliefNode 中,并且仅将固定数据保留在 LinkedMatrix 中。这与确保我以最少的网络活动保持干净的信念更新有关。这意味着我的 BeliefNode 存储:

  • 一组子项引用,以及来自每个子项的过滤似然性以及为该子项进行过滤的链接矩阵
  • 父引用数组,以及来自每个父级的过滤先验赔率和为该父级进行过滤的链接矩阵
  • 节点的组合可能性
  • 节点的组合先验几率
  • 计算的信念,或后验概率
  • 所有先前的几率和可能性都遵循的属性的有序列表

LinkMatrix 可以用许多不同的算法构建,具体取决于节点之间关系的性质。您描述的所有模型都只是您将使用的不同类。可能最简单的做法是默认使用或门,如果节点之间存在特殊关系,则选择其他方式处理 LinkMatrix。

我使用 MongoDB 进行持久性和缓存。我在事件模型中访问这些数据以实现速度和异步访问。这使得网络具有相当的性能,同时也有机会在需要时变得非常大。此外,由于我以这种方式使用 Mongo,我可以轻松地为同一个知识库创建新的上下文。因此,例如,如果我有一个诊断树,则对诊断的一些诊断支持将来自患者的症状和测试。我所做的是为该患者创建一个上下文,然后根据该特定患者的证据传播我的信念。同样,如果医生说患者可能患有两种或多种疾病,那么我可以更改我的一些链接矩阵以不同方式传播信念更新。

如果您不想为您的系统使用 Mongo 之类的东西,但您计划让多个消费者在知识库上工作,您将需要采用某种缓存系统来确保您正在新工作- 随时更新节点。

我的工作是开源的,所以如果你愿意,你可以跟随。这都是 Ruby,所以它与您的 Python 类似,但不一定是替代品。我喜欢我的设计的一件事是,人类解释结果所需的所有信息都可以在节点本身中找到,而不是在代码中。这可以在定性描述或网络结构中完成。

因此,我与您的设计有一些重要区别:

  • 我不计算类内的似然模型,而是计算链接矩阵内的节点之间的似然模型。这样,我就没有在同一个类中组合几个似然函数的问题了。我也没有一个模型与另一个模型的问题,我可以将两个不同的上下文用于同一个知识库并比较结果。
  • 通过使人为决策显而易见,我增加了很多透明度。即,如果我决定在两个节点之间使用默认的或门,我知道我何时添加了它,并且这只是一个默认决定。如果我稍后回来并更改链接矩阵并重新计算知识库,我会记录我为什么这样做,而不仅仅是选择一种方法而不是另一种方法的应用程序。你可以让你的消费者记下这类事情。无论您如何解决这个问题,从分析师那里获得关于他们为什么要以一种方式而不是另一种方式进行设置的逐步对话可能是一个好主意。
  • 我可能对先前的几率和可能性更明确。我不确定,我只是看到你使用不同的模型来改变你的可能性数字。如果您计算后验信念的模型没有以这种方式分解,那么我所说的大部分内容可能完全无关紧要。我的好处是能够执行三个可以按任何顺序调用的异步步骤:将更改后的可能性传递到网络中,将更改后的先验几率传递到网络中,并重新计算节点本身的组合信念(后验概率) .

一个重要的警告:我所说的一些内容尚未发布。我一直在研究我正在谈论的东西,直到今天早上 2:00 左右,所以它肯定是最新的,肯定会得到我的定期关注,但目前还没有全部向公众开放。由于这是我的热情所在,如果您愿意,我很乐意回答任何问题或一起开展项目。

于 2011-03-25T16:30:22.107 回答
3

Mozart/Oz3 基于约束的推理系统解决了一个类似的问题:您根据对有限域变量、约束传播者和分发者、成本函数的约束来描述您的问题。当无法进行更多推断但仍有未绑定变量时,它会使用您的成本函数将问题空间拆分到最有可能降低搜索成本的未绑定变量上:也就是说,如果 X 在 [a,c] 之间是这样一个变量,并且 c (a < b < c) 是最有可能降低搜索成本的点,您最终会遇到两个问题实例,其中 X 介于 [a,b] 之间,而在另一个实例中,X 介于 [b,c ]。Mozart 做得相当优雅,因为它将变量绑定具体化为第一类对象(这非常有用,因为 Mozart 是普遍并发和分布式的,可以将问题空间移动到不同的节点)。在其实施中,

您当然可以在基于图的库中实现写时复制方案(提示:numpy 使用各种策略来最小化复制;如果您将图表示基于它,您可能会免费获得写时复制语义)和达到你的目标。

于 2011-03-25T07:08:48.720 回答
2

我对贝叶斯网络不太熟悉,所以希望以下内容有用:

过去,我在使用高斯过程回归器而不是贝叶斯分类器时遇到了类似的问题。

我最终使用了继承,效果很好。所有特定于模型的参数都由构造函数设置。calculate() 函数是虚拟的。级联不同的方法(例如,结合任意数量的其他方法的求和方法)也可以很好地工作。

于 2011-03-23T14:04:14.120 回答
2

我认为你需要问几个影响设计的问题。

  1. 您多久添加一次模型?
  2. 您的图书馆的消费者是否期望添加新模型?
  3. 百分之几的用户将添加模型,而百分之几的用户将使用现有模型?

如果大部分时间都花在现有模型上,而新模型不太常见,那么继承可能是我会使用的设计。它使文档易于结构化,并且使用它的代码将易于理解。

如果该库的主要目的是提供一个用于试验不同模型的平台,那么我将使用带有映射到函子的属性的图,以便基于父项计算事物。库会更复杂,图的创建也会更复杂,但它会更强大,因为它允许您创建基于节点更改计算函子的混合图。

无论您最终设计什么,我都会从一个简单的一类实现设计开始。让它通过一组自动化测试,然后在完成后重构为更完整的设计。另外,不要忘记版本控制;-)

于 2011-03-23T14:17:08.043 回答