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)))))
每个节点的概率表作为父节点状态的函数给出,并返回true
和false
值的概率。例如,
((: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(贝叶斯网络)的主要内容,如果未能解决你的问题,请参考以下文章