Clojure DAG(贝叶斯网络)

时间:2012-07-14 09:11:03

标签: algorithm clojure functional-programming directed-acyclic-graphs

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

我研究了很多BN理论,但我仍然看不出如何实现网络(我不是人们称之为“大师”的任何东西,尤其不是函数式编程)。

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

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

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

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

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

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

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

3 个答案:

答案 0 :(得分:1)

这不是一个完整的答案,但这里是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值(resp。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值调用每个闭包以提取适当的概率,并将它们相乘。

在这里,我作弊有点因为我认为联合函数应该通过遍历图来计算(在一般情况下,宏可以帮助)。这也有点混乱,特别是关于节点的状态,这些状态不一定只是真实和错误:在一般情况下你最有可能使用地图。

答案 1 :(得分:1)

通常,计算BN的联合分布的方法是

prod( P(node | parents of node) ) 

要实现此目标,您需要一个节点列表,其中每个节点都包含

  • 节点名称
  • 父母名单
  • 概率表
  • 儿童名单
当平坦时,概率表可能是最容易处理的,其中每个行值对应于父配置,并且每个列对应于该节点的值。这假设您使用记录来保存所有值。节点的值也可以包含在节点中。

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

每行应该标准化,然后P(节点|父节点)=表[row,col]

你并不真正需要孩子的名单,但拥有它可以使拓扑排序更容易。 DAG必须能够进行拓扑排序。

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

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

我意识到你正在寻找一个实现,但是,通过一些工作,你可以自己动手。

答案 2 :(得分:0)

你可能会试着更平坦,并且有几个由节点id索引的地图:一个用于概率表的地图,一个用于父母,一个用于非后代(我不是BN专家:这是什么,它是如何使用的等等。感觉就像可以从父母表中重新计算的东西^ W关系^ W map)。