白冥 发表于 2025-1-26 19:40:54

【Python】【原创】条件概率分布与贝叶斯网络

本帖最后由 白冥 于 2025-1-28 16:55 编辑

目录      


[*]概述
[*]依赖库
[*]类与方法概述
[*]Cpd类的详细说明
[*]Bayesian_Network类的详细说明
[*]示例代码


概述      

      本贴将介绍两种用于构建和操作贝叶斯网络的 Python 类: Cpd和Bayesian_Network 。这些类提供了构建贝叶斯网络所需的核心功能,允许定义变量、变量的条件概率表(CPD),以及设置变量间的依赖关系。

依赖库      

该项目依赖以下几个 Python 库:

[*] collections.deque : 用于队列操作,特别是在实现图的拓扑排序时。
[*] math : 提供数学计算支持。
[*] typing : 用于类型注解。


类与方法概述      

3.1 Cpd类

         Cpd类用于表示贝叶斯网络中每个节点的条件概率分布(CPD)。它通过指定变量及其父节点,定义了该变量在给定父节点条件下的概率值。

class Cpd:
    def __init__(self, variable: str, variable_card: int, values: List], parents: List = [], parent_cards: List = []):
      self.variable=variable
      self._variable_card = variable_card
      self.values=values
      self.parents=parents
      self._parent_cards = parent_cards

关键属性:

[*] variable : 当前变量的名称(字符串类型)。
[*] variable_card : 当前变量的状态数(整数类型)。
[*] values : 条件概率表(二维列表,每行对应一种父节点状态的条件概率)。
[*] parents : 当前变量的父节点列表(字符串类型)。
[*] parent_cards : 父节点的状态数(整数类型列表)。


3.2 Bayesian_Network类

Bayesian_Network 类用于表示贝叶斯网络,提供了网络结构的定义、拓扑排序和条件概率分布的添加等功能。贝叶斯网络由节点(变量)和它们之间的有向边组成。


class Bayesian_Network:
    def __init__(self,edges:List]):
      self._nodes=self._add_nodes(edges)
      self._parents=self._add_parents(edges, self._nodes)
      self._neighbors=self._add_neighbors(edges, self._nodes)
      if not self._is_acyclic_graph(edges, self._nodes, self._neighbors):
            raise ValueError("Bayesian network is DAG")
      self._cpds=dict()
    def _add_nodes(self, edges):
      nodes=set()
      for node0,node1 in edges:
            nodes.update()
      return list(nodes)
    def _add_parents(self, edges, nodes):
      parents = {node: set() for node in nodes}
      for node0, node1 in edges:
            parents.add(node0)
      return parents
    def _add_neighbors(self, edges, nodes):
      neighbors = {node: set() for node in nodes}
      for node0, node1 in edges:
            neighbors.add(node1)
      return neighbors
    def _is_acyclic_graph(self, edges, nodes, neighbors):
      in_degree = {node: 0 for node in nodes}
      for node0, node1 in edges:
            in_degree += 1
      queue = deque()
      visited = set()
      while queue:
            node = queue.popleft()
            for neighbor in neighbors:
                in_degree -= 1
                if in_degree == 0:
                  queue.append(neighbor)
            visited.add(node)
      return len(visited)==len(in_degree)
    def add_cpds(self, *cpds: Cpd):
      for cpd in cpds:
            if cpd.variable not in self._nodes:
                raise ValueError(f"Variable {cpd.variable} is not a node in the model")
            for parent in cpd.parents:
                if parent not in self._parents:
                  raise ValueError(f"Parent node {parent} is not connected to {cpd.variable} in the model")
            self._cpds = cpd
关键方法:

[*] __init__(self, edges: List]) : 初始化贝叶斯网络,接受网络中的边(变量间的依赖关系)。
[*] _add_nodes(self, edges) : 从边列表中提取所有节点。
[*] _add_parents(self, edges, nodes) : 生成每个节点的父节点列表。
[*] _add_neighbors(self, edges, nodes) : 生成每个节点的邻居节点列表。
[*] _is_acyclic_graph(self, edges, nodes, neighbors) : 检查图是否为无环图(DAG)。
[*] add_cpds(self, *cpds: Cpd) : 向网络中添加条件概率分布(CPD)。


Cpd类的详细说明      

4.1__init__ 方法


    和上面的一样,不说了


Bayesian_Network类的详细说明      

5.1__init__方法

edges : 由一组二元组(边)组成的列表,表示网络中节点之间的依赖关系。每个二元组表示一个有向边,其中第一个元素是父节点,第二个元素是子节点。


      此方法用于初始化贝叶斯网络。通过提供网络中的边,它将自动添加所有节点、父节点和邻居节点。同时,方法会检查网络的拓扑是否是无环图(DAG),因为贝叶斯网络必须满足这一条件。如果图中存在环,则会抛出ValueError异常。


5.2 _add_nodes 方法
5.3 _add_parents 方法
5.4 _add_neighbors 方法

      以上方法的逻辑都是一样的,相信大家光看代码就能清楚。都是遍历边,然后生成对应的数据结构。不再赘述。


5.5_is_acyclic_graph 方法

         edges : 由一组二元组(边)组成的列表,表示网络中节点之间的依赖关系。
         nodes : 网络中所有节点的列表。
         neighbors : 每个节点的邻居节点列表。
      此方法用于检查网络中的图是否为有向无环图(DAG)。通过计算每个节点的入度并进行拓扑排序,验证图是否有环。如果存在环,方法将返回False ;否则返回True 。


5.6   add_cpds 方法

         cpds : 一个或多个Cpd实例,表示添加到网络中的条件概率分布。
      此方法用于将条件概率分布添加到贝叶斯网络中。它检查每个Cpd实例是否有效,即变量是否在网络中,并且父节点是否存在于网络的父节点列表中。如果Cpd实例无效,则抛出ValueError异常。


示例代码      

# 定义贝叶斯网络的边
edges = [
    ('A', 'B'),
    ('A', 'C'),
    ('B', 'D'),
    ('C', 'D')
]

# 创建贝叶斯网络实例
bn = Bayesian_Network(edges)

# 创建条件概率分布实例
cpd_A = Cpd(variable='A', variable_card=2, values=[, ])
cpd_B = Cpd(variable='B', variable_card=2, values=[, ], parents=['A'], parent_cards=)
cpd_C = Cpd(variable='C', variable_card=2, values=[, ], parents=['A'], parent_cards=)
cpd_D = Cpd(variable='D', variable_card=2, values=[, ], parents=['B', 'C'], parent_cards=)

# 将条件概率分布添加到贝叶斯网络中
bn.add_cpds(cpd_A, cpd_B, cpd_C, cpd_D)

static/image/hrline/4.gif

补充-2025-01-27      修改次数4
      
      贝叶斯网络,是一种概率图模型,它通过DAG表示一组随机变量及其条件依赖关系。当我们想要表示一组随机变量的因果关系时,最常使用贝叶斯网络。
      贝叶斯网络的局部独立性:对于贝叶斯网络内的任何随机变量X,若它的父级变量是Pa(X),那么X与自身的除父级节点外的任何非后代变量Non-Desc(X),都与X条件独立:记作X⊥Non-Desc(X) | Pa(X)。
      贝叶斯网络的基本结构:
            链式结构A→B→C
            V型结构A→B←C
            倒V型结构A←B→C
      贝叶斯网络的全局独立性:对于贝叶斯网络内的任意随机变量X,若对于任意不是X且不与X相邻的的随机变量Y,若X,Y之间任意路径path都存在阻断点W,则X,Y条件独立。这称为贝叶斯网络的全局独立性,记为X⊥Y | W。
      阻断点:
            对于路径path上的任意链式结构…A→B→C…,若随机变量B是给定的,则B是路径path上的阻断点;
            对于路径path上的任意V型结构…A→B←C…,若随机变量B是不定的,则B是路径path上的阻断点;
            对于路径path上的任意倒V型结构…A←B→C…,若随机变量B是给定的,则B是路径path上的阻断点。

补充-2025-01-28      修改次数5
      
      独立性能做一个事情,那就是化简概率表达式。事实上,任何一个贝叶斯网络,都可以唯一表示一个分布,那就是全体随机变量的联合分布。
      对于一个有N个随机变量的贝叶斯网络,它代表全体随机变量的联合分布,根据概率链式法则我们知道,P(X₁,X₂,X₃,…,Xɴ) = P(X₁|X₂,X₃,…,Xɴ) × P(X₂|X₃,X₄,…,Xɴ) × P(X₃|X₄,X₅,…,Xɴ) × … × P(Xɴ|Xɴ₋₁) × P(Xɴ)。
      对于我们所举的例子,就以这张图为例。
(节点,箭头代表依赖关系)
      假设一个贝叶斯网络的节点和边构成的DAG如图所示,它代表的联合概率分布为P(A,B,C,D,E,F,G) = P(A|B,C,D,E,F,G) × P(B|C,D,E,F,G) × P(C|D,E,F,G) × P(D|E,F,G) × P(E|F,G) × P(F|G) × P(G)
      让我们分析每个点的独立性。

-------------

      由局部独立性可得:
      A的父级变量是B,C,则A⊥D,E,F,G | B,C
      B的父级变量是D,则B⊥C,E,F,G | D
      C的父级变量是D,F,G,则C⊥B,E | D,F,G
      D的父级变量是E,则D⊥F,G | E
      E,F,G没有父级变量

------------

      由全局独立性可得:
      (A,B,C,D由于可能独立的点都找完了,剩下来的都是父级节点或后代节点,是绝对相关的,因此不分析)
      E到F有两条路径:
            E→D→C←F,C是阻断点
            E→D→B→A←C←F,A是阻断点
            因此E⊥F | A,C
      E到G有两条路径:
            E→D→C←G,C是阻断点
            E→D→B→A←C←G,A是阻断点
            因此E⊥G | A,C
      F到G有一条路径:
            F→C←G,C是阻断点
            因此F⊥G | C

------------

      由随机变量独立的性质,P(A,B,C,D,E,F,G)可化简为 P(A,B,C,D,E,F,G) = P(A|B,C) × P(B|D) × P(C|D,F,G) × P(D|E) × P(E) × P(F) × P(G)。
      此时此刻你会发现,贝叶斯网络全体随机变量的联合分布就是贝叶斯网络的全体随机变量的cpd(条件概率分布)的乘积,即P(X₁,X₂,X₃,…,Xɴ) = ΠP(Xᵢ | Pa(Xᵢ)) (i = 1 → N)。


补充-2025-01-28      修改次数2
      
      所谓边际化就是把联合分布中若干随机变量分离出来考虑,消除对需求无关的变量。边际化最简单的方式就是变量消除。
      假如我们只想知道P(A,B,C),我们就需要消除D,E,F,G的影响。
      我们知道了P(A,B,C,D,E,F,G) = P(A|B,C) × P(B|D) × P(C|D,F,G) × P(D|E) × P(E) × P(F) × P(G)。
      根据全概率公式,我们有P(A,B,C) = P(A|B,C) × Σᴅ(P(B|D) × ΣғΣɢ(P(C|D,F,G) × Σᴇ(P(D|E) × P(E) × P(F) × P(G))))。
      事实上,我们边际化对任何随机变量的联合分布都有效,最终能得到若干随机变量的联合分布或单一随机变量的边际分布。

zhnlwwdzz 发表于 2025-3-2 13:48:26

尽管整体给人的感觉较为繁杂,不过依旧能够剖析得十分清晰。

zhuovboyan 发表于 2025-2-9 21:44:31

0-0 再次星星眼jpg 明明分开这几个字都认识来着jpg

1051506056 发表于 2025-2-1 14:36:02

好高级惹,以后我就是泥潭大学毕业的高材生

白鸟探 发表于 2025-1-29 15:28:11

虽然看不懂一点但还是觉得很厉害的样子

DiederichYH 发表于 2025-1-29 14:38:46

楼主这个我们专业用不到,但是我看隔壁学院有和你类似的,感觉真的很难

hush 发表于 2025-1-27 20:51:19

这是什么新赛道 不觉明厉。。

Sam30 发表于 2025-1-27 16:42:11

感覺是比較特殊的東西呢, 看看之後有沒有機會可以用上的了:loveliness:

2297988 发表于 2025-1-27 13:06:21

大佬的分享……非本专业人士只能高呼牛犇了……

黑夜下的灯光 发表于 2025-1-27 11:04:59

相当实用的代码呢,虽然整体看起来很复杂,但还是能分析透彻呢~:loveliness:

万俟 发表于 2025-1-27 08:25:46

就围观一下,贝叶斯和条件概率我都懂,代码看不太懂

kisakuni 发表于 2025-1-27 02:28:26

不明觉厉,计算机盲路过

a5173347 发表于 2025-1-26 23:32:14

不明觉厉,显出膝盖及腰部以下

荆棘之环 发表于 2025-1-26 23:21:25

太深奥了,所以是用在?

aboab 发表于 2025-1-26 21:48:44

死去的统计与概率突然开始攻击我

没药 发表于 2025-1-26 21:08:09

吓鼠,本可人一个字都看不懂惹:$:$:$:$

天逸0 发表于 2025-1-26 20:58:53

概率论与数理统计??楼主是大学老师吗

娱乐法师火布偶 发表于 2025-1-26 20:10:31

论坛架构支持md估计只能靠架构官方的更新了
页: [1]
查看完整版本: 【Python】【原创】条件概率分布与贝叶斯网络