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

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

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

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

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

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

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

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

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

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

【问题讨论】:

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


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

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

      【讨论】:

        猜你喜欢
        • 2013-04-08
        • 2011-01-09
        • 2013-05-11
        • 2013-07-19
        • 2019-11-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多