用Python构建贝叶斯信念网络解决Monty Hall三门问题

简介: 本文将向你展示如何利用Python构建简单的贝叶斯信念网络,并用它来进行严格的推理。我们要建模的问题是著名的蒙提霍尔问题(也叫三门问题)。

用Python构建贝叶斯信念网络解决Monty Hall三门问题

简介

本文将向你展示如何利用Python构建简单的贝叶斯信念网络,并用它来进行严格的推理。

我们要建模的问题是著名的 蒙提霍尔问题 (也叫三门问题)。选择蒙提霍尔问题是因为它具有如下3个特征,这3个特征使得它非常适合作为教程举例:

  • 它很简单,只有3个变量;
  • 它的结论非常有趣且反经验;
  • 它的条件概率很简单,只需要几行python代码即可实现。

问题描述

蒙提霍尔问题来自一个电视游戏节目:主持人蒙提霍尔邀请一位嘉宾选择3扇门中的其中一扇,并告知嘉宾三扇门中有一扇后面放的是汽车,其余两扇后面放的是山羊。主持人知道每扇门后面放的是什么但是他不会告诉嘉宾。

备注
我们假设嘉宾对门后的物品没有任何先验知识。同时我们假设嘉宾都想获得汽车而不是山羊。

当嘉宾做出了初次选择后,为了让节目更加有趣,主持人会先打开另外2扇门中的一扇。

备注
因为主持人知道哪扇门后有汽车,他绝不会打开有汽车的门,而是打开那个有山羊的门。

当打开了其中一扇没有汽车的门后,主持人会给嘉宾以此选择的机会--是否换一扇门。

我们要解答的问题是:嘉宾应该坚持最初的选择还是应该换一扇门?

很多人认为换不换都一样。然而事实真的如此吗?接下来我们用贝叶斯信念网络来推理一下这个问题。

5f61dd4e90bc497593c290ea839bd066 (1).png

用Python构建贝叶斯信念网络模型

构建贝叶斯信念网络非常简单直接,只要按照规则和约定一步一步走即可。

识别变量

首先,对于任何要建模的问题,识别变量(随机变量)是第一步。对蒙提霍尔问题来说,识别过程中的 事件 有助于我们找到 随机变量

蒙提霍尔问题中的事件按照时间发生顺序如下:

  1. 汽车事先藏在3扇门中的一扇;
  2. 嘉宾做出初次选择;
  3. 主持人打开另外两扇门中的一扇;

上面每一个事件都是模型中的一个变量,我们将这些变量命名为:

  1. prize_door
  2. guest_door
  3. monty_door

识别变量的可能取值

当创建贝叶斯网络时,我们不仅需要识别变量还需要知道变量可能的取值。

本案例比较简单:有3扇门,我们将其命名为 $A, B, C$。因此三个变量可以取 $[A, B, C]$ 中的任意值。

注意
我们称变量可取值的集合为变量的定义域。不是所有变量总有相同的定义域。

创建网络的心智图

现在我们可以开始构建贝叶斯信念网络(BBN)。贝叶斯信念网络是个有向无环图(DAG),这意味着我们需要构建一个包含节点(也叫顶点)和有向边的图。

每一个变量将作为网络中的节点。我们可以在图中放置3个节点表示每一个变量:

69e928367977452c937e788987553f0d (1).png

接下来我们需要添加边。边在概率图模型中表示边连接的两个节点其中一个影响另外一个的事实。在贝叶斯信念网络中,由于边是有向的,这意味节点关系是"父子"关系,即子节点条件依赖父节点。这意味着子节点的取值依赖父节点的取值。考虑蒙提霍尔问题中的事件,当支持人打开后面是样的门时,他的决策依赖车藏在哪个门后(因为他永远不能暴露奖品),当然主持人也不可能选择嘉宾选择的门,这有悖游戏规则。于是我们需要从prize_door节点连一条边到monty_door,同理我们需要从guest_door节点连一条边到monty_door。完成后的贝叶斯信念网络的结构如下图:

39fbe68fcb9f461f8f41be897b23e935 (1).png

创建Python函数

现在我们有了网络结构图,接下来就可以开始编程了。图中所有节点用一个Python函数来表示。函数的参数表示模型中的变量。以prize_door节点为例,它的python代码为:

def node_prize_door(prize_door):
    pass

同理,guest_door节点代码为:

def node_guest_door(prize_door):
    pass

monty_door节点来说,python代码稍有不同,因为这个节点有2条入边,我们需要将prize_doorguest_door两个变量加入到函数的参数列表中:

def node_monty_door(prize_door, guest_door, monty_door):
    pass

对于上面创建的三个函数,有几点需要说明:

  1. 函数的参数名与模型中的变量名一致;
  2. 函数代表图中的节点;
  3. 为了区分节点名和变量名,我在函数名加了node_前缀;
  4. 如果节点有父节点,则需要将父节点的变量添加到参数列表中;
  5. 三个函数对应三个变量,三个变量代表模型中的按个事件;
  6. 每个函数引入一个新变量到模型中。

完成函数细节

贝叶斯信念网络不仅需要图,还需要分布来控制变量。接下来我们将加入每个变量的先验和条件概率。要完成这一步可以用下面的方法思考函数:

每一个函数必须返回[0, 1]之间的一个实数,代表这个变量取其定义域上某个值的概率。

node_prize_door为例,我们想返回奖品藏在A,B,C某扇门后的概率。假设奖品是随机藏在门后的,那么奖品出现在每扇门后的概率是1/3。我们需要将node_prize_door函数的返回值修改为1/3:

def node_prize_door(prize_door):
    return 0.33333333

同理,由于嘉宾没有先验的知识,他的选择也是随机的,所以嘉宾选择某扇门的概率也是1/3:

def node_guest_door(prize_door):
    return 0.33333333

node_monty_door要有趣一些,我们需要计算在给定父节点prize_doorguest_door的情况下monty_door变量取A, B, C三个值中每一个的似然值。如果嘉宾有幸选对了,支持人可以随意选择剩下的2扇门;如果嘉宾选错了,此时支持人既不能选择嘉宾选择的门也不能选择有奖品的门,那么他只有一个选择:两扇门中有山羊的门。python代码如下:

def node_monty_door(prize_door, guest_door, monty_door):
    if guest_door == prize_door:# 嘉宾选对了
        if monty_door == prize_door:
            return 0    # 主持人绝不会暴露奖品
        else:
            return 0.5    # 随意选择其余2扇门中的一扇
    elif monty_door == prize_door:
        return 0    # 主持人绝不会暴露奖品
    elif monty_door == guest_door:
        return 0    # 支持人也绝不会选择嘉宾选择的门
    else:
        return 1    # 嘉宾没有猜对,支持人只能选择剩下两扇门中有山羊的那扇

组合在一起

接下来我们要完成整个代码并在其之上运行一些推断。要创建图,我们需要调用build_bnn模块。在程序顶部加入下面一行代码经模块引入程序中:

from bayesian.bbn import build_bbn

接下来加入主函数

if __name__ == "__main__":
    g = build_bnn(node_prize_door, node_guest_door, node_monty_door,
                  domains=dict(
                      prize_door=['A', 'B', 'C'],
                      guest_door=['A', 'B', 'C'],
                      monty_door=['A', 'B', 'C']
                  ))

上面的代码创建了一个贝叶斯信念网络的实例。工厂方法build_bbn接受所有代表图中节点的函数作为参数,附加一个可选参数domainsdomains接受一个字典用于指明每个变量的取值范围。注意:图的结构是用过函数和参数推理得到的。

将整个python代码保存为monty_hall.py文件

用BBN网络进行推断

执行下面命令

$ python -i monty_hall.py

-i参数指示python解释器在执行完给定python脚本后运行在交互模式下。这样我们接下来访问模型会更方便。BBN类主要通过query方法来查询。库中提供了一个用户体验友好的查询方法--名叫q。我们可以不带任何参数尝试调用一下q函数,看会输出什么:

>>> g.q()
+------------+-------+----------+
| Node       | Value | Marginal |
+------------+-------+----------+
| guest_door | A     | 0.333333 |
| guest_door | B     | 0.333333 |
| guest_door | C     | 0.333333 |
| monty_door | A     | 0.333333 |
| monty_door | B     | 0.333333 |
| monty_door | C     | 0.333333 |
| prize_door | A     | 0.333333 |
| prize_door | B     | 0.333333 |
| prize_door | C     | 0.333333 |
+------------+-------+----------+
>>>

要如何解读上面的输出呢?q方法基本上就是用相同的参数调用query方法,然后将query方法返回的结果格式化成易读的表格。表格的列为节点(Node)、值(Value)和边缘概率(Marginal)。你会注意到表格显示的是每一个节点的每一个可取值的边缘概率(Marginal)。表格中所有的边缘概率都是0.333333。这是因为我们没有为模型提供任何额外的证据。在缺乏证据的情况下,模型估计每个门的概率是1/3。

注意
在BBN的术语中我们称给变量赋值为证据。我们称任意为0个或多个变量赋值的集合为图的配置。

接下来让我们为BBN提供一些证据,然后再查询一遍。假设我们观察到嘉宾选择了A门,输入下面的python交互指令:

>>> g.q(guest_door='A')
+-------------+-------+----------+
| Node        | Value | Marginal |
+-------------+-------+----------+
| guest_door  | B     | 0.000000 |
| guest_door  | C     | 0.000000 |
| guest_door* | A*    | 1.000000 |
| monty_door  | A     | 0.000000 |
| monty_door  | B     | 0.500000 |
| monty_door  | C     | 0.500000 |
| prize_door  | A     | 0.333333 |
| prize_door  | B     | 0.333333 |
| prize_door  | C     | 0.333333 |
+-------------+-------+----------+
>>>

注意当我们观察到嘉宾选择了A门后边缘概率的变化。变量guest_door取值A的概率变为1,这是理所当然的,因为我们本来就观察到嘉宾选择了A门。同理变量guest_door取B和C的概率变为0,因为我们知道嘉宾一定不会选B门和C门。同时我们注意到作为证据的变量在表格里标记上了星号,以提示我们为本次查询提供了哪些证据。接下来看monty_door变量。monty_door变量取值A的概率变为0,这是因为游戏规则设定为主持人不能选择嘉宾选择的门。monty_door变量取B和取C的概率一半一半,表示在没有其他额外证据的情况下主持人选择打开剩下的两扇门的中哪一扇的概率是相等的。

如果此时我们观察到支持人打开了门B,并询问嘉宾要不要换门。将新的证据加入后再查询得到:

>>> g.q(guest_door='A', monty_door='B')
+-------------+-------+----------+
| Node        | Value | Marginal |
+-------------+-------+----------+
| guest_door  | B     | 0.000000 |
| guest_door  | C     | 0.000000 |
| guest_door* | A*    | 1.000000 |
| monty_door  | A     | 0.000000 |
| monty_door  | C     | 0.000000 |
| monty_door* | B*    | 1.000000 |
| prize_door  | A     | 0.333333 |
| prize_door  | B     | 0.000000 |
| prize_door  | C     | 0.666667 |
+-------------+-------+----------+
>>>

观察prize_door的边缘概率,里面包含了我们最初提出问题的答案。显而易见,奖品在C门后的概率是A门后的2倍。

目录
相关文章
|
2天前
|
机器学习/深度学习 人工智能 算法
猫狗宠物识别系统Python+TensorFlow+人工智能+深度学习+卷积网络算法
宠物识别系统使用Python和TensorFlow搭建卷积神经网络,基于37种常见猫狗数据集训练高精度模型,并保存为h5格式。通过Django框架搭建Web平台,用户上传宠物图片即可识别其名称,提供便捷的宠物识别服务。
91 55
|
12天前
|
机器学习/深度学习 人工智能 算法
【宠物识别系统】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+图像识别
宠物识别系统,本系统使用Python作为主要开发语言,基于TensorFlow搭建卷积神经网络算法,并收集了37种常见的猫狗宠物种类数据集【'阿比西尼亚猫(Abyssinian)', '孟加拉猫(Bengal)', '暹罗猫(Birman)', '孟买猫(Bombay)', '英国短毛猫(British Shorthair)', '埃及猫(Egyptian Mau)', '缅因猫(Maine Coon)', '波斯猫(Persian)', '布偶猫(Ragdoll)', '俄罗斯蓝猫(Russian Blue)', '暹罗猫(Siamese)', '斯芬克斯猫(Sphynx)', '美国斗牛犬
82 29
【宠物识别系统】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+图像识别
|
2天前
|
存储 监控 安全
单位网络监控软件:Java 技术驱动的高效网络监管体系构建
在数字化办公时代,构建基于Java技术的单位网络监控软件至关重要。该软件能精准监管单位网络活动,保障信息安全,提升工作效率。通过网络流量监测、访问控制及连接状态监控等模块,实现高效网络监管,确保网络稳定、安全、高效运行。
27 11
|
8天前
|
云安全 人工智能 安全
|
13天前
|
数据采集 分布式计算 大数据
构建高效的数据管道:使用Python进行ETL任务
在数据驱动的世界中,高效地处理和移动数据是至关重要的。本文将引导你通过一个实际的Python ETL(提取、转换、加载)项目,从概念到实现。我们将探索如何设计一个灵活且可扩展的数据管道,确保数据的准确性和完整性。无论你是数据工程师、分析师还是任何对数据处理感兴趣的人,这篇文章都将成为你工具箱中的宝贵资源。
|
13天前
|
机器学习/深度学习 人工智能 算法
深度学习入门:用Python构建你的第一个神经网络
在人工智能的海洋中,深度学习是那艘能够带你远航的船。本文将作为你的航标,引导你搭建第一个神经网络模型,让你领略深度学习的魅力。通过简单直观的语言和实例,我们将一起探索隐藏在数据背后的模式,体验从零开始创造智能系统的快感。准备好了吗?让我们启航吧!
39 3
|
15天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于贝叶斯优化CNN-GRU网络的数据分类识别算法matlab仿真
本项目展示了使用MATLAB2022a实现的贝叶斯优化、CNN和GRU算法优化效果。优化前后对比显著,完整代码附带中文注释及操作视频。贝叶斯优化适用于黑盒函数,CNN用于时间序列特征提取,GRU改进了RNN的长序列处理能力。
|
17天前
|
网络安全 Python
Python网络编程小示例:生成CIDR表示的IP地址范围
本文介绍了如何使用Python生成CIDR表示的IP地址范围,通过解析CIDR字符串,将其转换为二进制形式,应用子网掩码,最终生成该CIDR块内所有可用的IP地址列表。示例代码利用了Python的`ipaddress`模块,展示了从指定CIDR表达式中提取所有IP地址的过程。
35 6
|
20天前
|
数据采集 XML 存储
构建高效的Python网络爬虫:从入门到实践
本文旨在通过深入浅出的方式,引导读者从零开始构建一个高效的Python网络爬虫。我们将探索爬虫的基本原理、核心组件以及如何利用Python的强大库进行数据抓取和处理。文章不仅提供理论指导,还结合实战案例,让读者能够快速掌握爬虫技术,并应用于实际项目中。无论你是编程新手还是有一定基础的开发者,都能在这篇文章中找到有价值的内容。
|
1天前
|
SQL 安全 网络安全
网络安全与信息安全:知识分享####
【10月更文挑战第21天】 随着数字化时代的快速发展,网络安全和信息安全已成为个人和企业不可忽视的关键问题。本文将探讨网络安全漏洞、加密技术以及安全意识的重要性,并提供一些实用的建议,帮助读者提高自身的网络安全防护能力。 ####
34 17