Clojure DAG(贝叶斯网络)

Posted

技术标签:

【中文标题】Clojure DAG(贝叶斯网络)【英文标题】:Clojure DAG (Bayesian Network) 【发布时间】:2012-07-14 00:17:05 【问题描述】:

我想在 clojure 中构建一个贝叶斯网络,因为我还没有找到任何类似的项目。

我研究了很多 BN 的理论,但我仍然看不到如何实现网络(我不是人们所谓的“大师”,尤其是函数式编程)。

我知道 BN 只不过是一个 DAG 和很多概率表(每个节点一个),但现在我不知道如何实现 DAG。

我的第一个想法是一个巨大的集合(DAG)和一些小地图(DAG 的节点),每个地图都应该有一个名称(可能是一个:key)一个概率表(另一个地图?)一个父母的向量最后是-后代的向量。

现在我不知道如何实现父母和非后代的引用(我应该在两个向量中放入什么)。 我想指针应该是完美的,但 clojure 缺少它;我可以在向量中放入:另一个节点的名称,但它会很慢,不是吗?

我在想,我可以使用更多集合而不是向量,这样可以更快地找到节点的后代。

概率表的类似问题,我仍然需要在其他节点上进行一些参考。

最后我还想学习 BN(从数据开始构建网络),这意味着我将大量更改概率表、边和节点。

我应该使用可变类型还是只会增加复杂性?

【问题讨论】:

这个[SO question][1]可以帮助你。 [1]:***.com/questions/3127890/… Chas Emerick 有一个talk on Bayesian networks,他给了一个 ClojureConj。里面有一些有用的信息可以回答你的一些问题。 ...现在youtube.com/watch?v=xoSFcSqo1jQ 你见过 loom lib 吗? github.com/aysylu/loom 可能不完全相关,但你看过robots.ox.ac.uk/~fwood/anglican(Clojure 中的Church 派生词)也看过robots.ox.ac.uk/~fwood/anglican/examples/index.html 【参考方案1】:

您可以尝试更扁平化,并有几张按节点 ID 索引的地图:一张用于概率表,一张用于父母,一张用于非后代(我不是 BN 专家:这是什么,它是如何使用的等等. ? 感觉像是可以从父表^W关系^W映射重新计算的东西。

【讨论】:

【参考方案2】:

这不是一个完整的答案,但这是来自wikipedia article 的示例网络的可能编码。每个节点都有一个名称、一个后继(子)列表和一个概率表:

(defn node [name children fn]
  :name name :children children :table fn)

此外,这里还有一些用于构建真/假概率的小辅助函数:

;; builds a true/false probability map
(defn tf [true-prob] #(if % true-prob (- 1.0 true-prob)))

上述函数返回一个闭包,当给定true 值(分别为false 值)时,返回事件X=true 的概率(对于我们正在编码的X 概率变量)。

由于网络是一个 DAG,我们可以直接相互引用节点(就像您提到的指针一样),而不必关心循环引用。我们只是按照拓扑顺序构建图:

(let [gw (node "grass wet" [] (fn [& :keys [sprinkler rain]]
                            (tf (cond (and sprinkler rain) 0.99
                                      sprinkler 0.9
                                      rain 0.8
                                      :else 0.0))))

  sk (node "sprinkler" [gw]
           (fn [& :keys [rain]] (tf (if rain 0.01 0.4))))

  rn (node "rain" [sk gw]
           (constantly (tf 0.2)))]

  (def dag :nodes :grass-wet gw :sprinkler sk :rain rn
        :joint (fn [g s r]
                 (*
                  (((:table gw) :sprinkler s :rain r) g)
                  (((:table sk) :rain r) s)
                  (((:table rn)) r)))))

每个节点的概率表作为父节点状态的函数给出,并返回truefalse 值的概率。例如,

((:table (:grass-wet dag)) :sprinkler true :rain false)

...返回:true 0.9, :false 0.09999999999999998

得到的联合函数根据以下公式组合概率:

P(G,S,R) = P(G|S,R).P(S|R).P(R)

((:joint dag) true true true) 返回 0.0019800000000000004。 实际上,((:table <x>) <args>) 返回的每个值都是围绕if 的闭包,它返回知道概率变量状态的概率。我们用各自的true/false 值调用每个闭包,以提取适当的概率,并将它们相乘。

在这里,我有点作弊,因为我认为应该通过遍历图形来计算联合函数(在一般情况下,宏可能会有所帮助)。这也让人感觉有点混乱,尤其是关于节点的状态,这些状态不一定只有真假:在一般情况下,您很可能会使用地图。

【讨论】:

【参考方案3】:

一般来说,计算一个BN的联合分布的方法是

prod( P(node | parents of node) ) 

要实现这一点,您需要每个节点包含的节点列表

节点名称 父母名单 概率表 孩子名单

概率表在扁平时可能最容易处理,其中每一行值对应于父配置,每一列对应于节点的值。这假设您正在使用记录来保存所有值。节点的值也可以包含在节点内。

没有父节点的节点只有一行。

每一行都应该被规范化,之后 P(node|parents) = table[row,col]

您实际上并不需要子列表,但拥有它可以使拓扑排序更容易。 DAG 必须能够进行拓扑排序。

最大的问题是概率表中的单元格数量是父母和自我所有维度的乘积。我在 C++ 中使用行映射的稀疏表处理了这个问题。

查询 DAG 是另一回事,执行此操作的最佳方法取决于大小以及近似答案是否足够。这里没有足够的空间来覆盖它们。搜索墨菲和贝叶斯网络工具箱可能会有所帮助

我知道您正在专门寻找一种实现方式,但只需做一些工作,您就可以自己实现。

【讨论】:

以上是关于Clojure DAG(贝叶斯网络)的主要内容,如果未能解决你的问题,请参考以下文章

20贝叶斯分类器:贝叶斯网络(属性之间存在依赖)

贝叶斯网络

提高贝叶斯因果网络的预测精度

优化预测基于matlab贝叶斯优化LSTM预测含Matlab源码 1329期

04 贝叶斯算法 - 贝叶斯网络

从贝叶斯方法谈到贝叶斯网络