草庐IT

贝叶斯网络入门教程(原理介绍+python代码实现)

spatial_coder 2023-11-08 原文

文章目录

贝叶斯网络原理

贝叶斯网络(Bayesian Network)是一种去除了条件概率独立性的概率图模型,其结构为有向无环图(direct acyclic graph,DAG), 图中每个节点代表一个随机变量,每个节点有对应的概率分布表,有向边表示各节点之间的依赖关系。

局部马尔科夫性

贝叶斯网络的一个性质是局部马尔可夫性。

Assumption 1.1 (Local Markov Assumption) 给定一个节点 X X X在有向无环图中的父节点,该节点独立于其所有非后继节点。

对于相互独立的多个变量的联合概率分布,有:
P ( x 1 , x 2 , . . . , x n ) = p ( x 1 ) p ( x 2 ∣ x 1 ) p ( x 3 ∣ x 1 , x 2 ) . . . p ( x n ∣ x n − 1 . . . x 1 ) ( 1.1 ) P(x_1,x_2,...,x_n)=p(x_1)p(x_2|x_1)p(x_3|x_1,x_2)...p(x_n|x_{n-1}...x_1) \qquad(1.1) P(x1,x2,...,xn)=p(x1)p(x2x1)p(x3x1,x2)...p(xnxn1...x1)(1.1)
若变量具有局部马尔可夫性,则联合概率分布可简化为:
P ( x 1 , x 2 , . . . , x n ) = P ( x i ∣ ∏ i = 1 n P a r e n t s ( x i ) ) ( 1.2 ) P(x_1,x_2,...,x_n)=P(x_i|\prod_{i=1}^nParents(x_i)) \qquad(1.2) P(x1,x2,...,xn)=P(xii=1nParents(xi))(1.2)

案例实战

基于pgmpy的python包实现一个用贝叶斯网络进行推理的例子。
安装:

pip install pgmpy

以学生获得推荐信的质量为例,来构造贝叶斯网络。各节点构成的有向无环图和对应的概率表如图所示。从图中可看出推荐信(letter)的质量受到学生成绩(grade)的直接影响,而考试难度(diff)和智力(intel)直接影响学生的成绩。智力还影响SAT的分数。

使用贝叶斯网络推理一个天赋较高的学生在考试较难的情况下获得推荐信的质量的概率分布,步骤如下。
step1: 定义贝叶斯网络结构。

from pgmpy.models import BayesianNetwork
letter_bn=BayesianNetwork([
    ('D','G'),('I','G'),('I','S'),('G','L') # 指向关系 D->I
])

step2: 构建各节点的条件概率分布。

from pgmpy.factors.discrete import TabularCPD
d_cpd=TabularCPD(variable='D',variable_card=2,values=[[0.6],[0.4]])
# 变量名,变量取值个数,对应概率
i_cpd=TabularCPD(variable='I',variable_card=2,values=[[0.7],[0.3]])
g_cpd=TabularCPD(variable='G',variable_card=3,values=[[0.3,0.05,0.9,0.5],[0.4,0.25,0.08,0.3],[0.3,0.7,0.02,0.2]],
 # 行数等于变量取值,列数等于依赖变量总取值数(3,4)
evidence=['I','D'],evidence_card=[2,2]) 
# 变量名,变量取值个数,对应概率,依赖变量名,依赖变量取值
s_cpd=TabularCPD(variable='S',variable_card=2,values=[[0.95,0.2],[0.05,0.8]],
                 evidence=['I'],evidence_card=[2])
l_cpd=TabularCPD(variable='L',variable_card=2,values=[[0.1,0.4,0.99],[0.9,0.6,0.01]],
                 evidence=['G'],evidence_card=[3]) # evidence_card必须是列表

注意:这里条件概率表的行数等于变量取值,列数等于依赖变量总取值组合数;evidence_card必须是列表。
step3: 添加概率表到贝叶斯网络,检查模型,并输出。

letter_bn.add_cpds(d_cpd,i_cpd,g_cpd,s_cpd,l_cpd)
letter_bn.check_model() # 检查构建的模型是否合理
letter_bn.get_cpds() # 网络中条件概率依赖关系

step4: 利用构建的贝叶斯网络进行具体的推理。这里推断一个天赋较高的学生在考试较难的情况下获得推荐信的质量的概率分布。

from pgmpy.inference import VariableElimination
letter_infer=VariableElimination(letter_bn) # 变量消除
prob_I=letter_infer.query(variables=['L'],evidence={'I':1,'D':1})
print(f"prob_I:{prob_I}")

输出结果如下,可看到得到劣质推荐信的概率为0.368,得到优质推荐信的概率为0.632。

prob_I:
+------+----------+
| L    |   phi(L) |
+======+==========+
| L(0) |   0.3680 |
+------+----------+
| L(1) |   0.6320 |
+------+----------+

pgmpy源码剖析

  • BayesianNetwork
    pgmpy中BayesianNetwork类继承于DAG类。DAG类是基于network的有向图模块构建的,该类返回一个有向无环图。

一个简要的贝叶斯网络的伪代码如下:

class BayesianNetwork(DAG):
    def __init__(self, ebunch=None, latents=set()):
        """
        构造一个贝叶斯网络模型;
        模型存储节点以及带有条件概率表和其他属性的边;
        边为有向边;
        """
        super(BayesianNetwork, self).__init__(ebunch=ebunch, latents=latents)
        self.cpds = []
        self.cardinalities = defaultdict(int)

    def add_cpds(self, *cpds):
        """
        # 添加条件概率分布到贝叶斯网络中
        cpds  :  list, set, tuple (array-like), List of CPDs which will be associated with the model
        """
        for cpd in cpds:
            if not isinstance(cpd, (TabularCPD, ContinuousFactor)):
                raise ValueError("Only TabularCPD or ContinuousFactor can be added.")

            if set(cpd.scope()) - set(cpd.scope()).intersection(set(self.nodes())):
                raise ValueError("CPD defined on variable not in the model", cpd)

            for prev_cpd_index in range(len(self.cpds)):
                if self.cpds[prev_cpd_index].variable == cpd.variable:
                    logging.info(f"Replacing existing CPD for {cpd.variable}")
                    self.cpds[prev_cpd_index] = cpd
                    break
            else:
                self.cpds.append(cpd)

    def get_cardinality(self, node=None):
        """
        返回节点(随机变量)的取值个数
        """
        if node is not None:
            return self.get_cpds(node).cardinality[0]
        else:
            cardinalities = defaultdict(int)
            for cpd in self.cpds:
                cardinalities[cpd.variable] = cpd.cardinality[0]
            return cardinalities

构造一个贝叶斯网络示例:

from pgmpy.models import BayesianNetwork
from pgmpy.factors.discrete import TabularCPD
student = BayesianNetwork([('diff', 'grade'), ('intel', 'grade')]) # 节点指向关系 diff->grade,intel->grade
cpd_diff = TabularCPD('diff', 2, [[0.6], [0.4]])
cpd_intel = TabularCPD('intel', 2, [[0.7], [0.3]])
cpd_grade = TabularCPD('grade', 2, [[0.1, 0.9, 0.2, 0.7],
 [0.9, 0.1, 0.8, 0.3]],['intel', 'diff'], [2, 2])
student.add_cpds(cpd_diff,cpd_intel,cpd_grade)
student.get_cardinality() # defaultdict(int, {'diff': 2, 'intel': 2, 'grade': 2})
print(f"grade节点的取值个数:{student.get_cardinality('grade')}") # grade节点的取值个数:2
  • TabularCPD
    TabularCPD类定义了各种条件概率分布表(conditional probability distribution table)。
    简要代码如下:

构建一个条件概率表示例:

cpd = TabularCPD(variable='grade', # 随机变量名
                 variable_card=3, # 随机变量的取值个数
                 values=[[0.1,0.1,0.1,0.1,0.1,0.1], # 该随机变量的概率表
                        [0.1,0.1,0.1,0.1,0.1,0.1],
                        [0.8,0.8,0.8,0.8,0.8,0.8]],
                evidence=['diff', 'intel'], # 该随机变量的依赖变量
                 evidence_card=[2,3]) # 依赖变量的取值个数
print(cpd)

参考资料

  1. https://pgmpy.org/detailed_notebooks/2.%20Bayesian%20Networks.html
  2. Brady Neal. 2020.Introduction to Causal Inference from a machine learning perspective[M], chapter 3, p28-30.
  3. Conrady S , Jouffe L . Bayesian Networks & BayesiaLab - A Practical Introduction for Researchers[M]. 2015.
  4. Pearl J . Causality: models, reasoning, and inference[M]. Cambridge University Press, 2000.
  5. A Bayesian network approach for population synthesis. https://www.sciencedirect.com/science/article/pii/S0968090X15003599

强烈推荐阅读贝叶斯网络的发明者朱迪亚·珀尔(Judea Pearl)的书Causality: models, reasoning, and inference,了解更多细节。

有关贝叶斯网络入门教程(原理介绍+python代码实现)的更多相关文章

  1. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  2. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  3. ruby-on-rails - Rails 源代码 : initialize hash in a weird way? - 2

    在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has

  4. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  5. ruby-on-rails - 浏览 Ruby 源代码 - 2

    我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru

  6. ruby - 模块嵌套代码风格偏好 - 2

    我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的

  7. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  8. ruby - 用 Ruby 编写一个简单的网络服务器 - 2

    我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b

  9. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  10. Python 相当于 Perl/Ruby ||= - 2

    这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:Pythonconditionalassignmentoperator对于这样一个简单的问题表示歉意,但是谷歌搜索||=并不是很有帮助;)Python中是否有与Ruby和Perl中的||=语句等效的语句?例如:foo="hey"foo||="what"#assignfooifit'sundefined#fooisstill"hey"bar||="yeah"#baris"yeah"另外,类似这样的东西的通用术语是什么?条件分配是我的第一个猜测,但Wikipediapage跟我想的不太一样。

随机推荐