开发也能看懂的大模型:概率图模型

概率图模型(Probabilistic Graphical Model, PGM)是将概率论与图论结合的一种工具,用于表示和处理复杂的随机变量之间的关系。它用图形结构直观地表达变量之间的依赖关系,同时利用概率计算进行推理和学习。


1. 概率图模型的核心概念

1.1 基本组成

  • 节点:代表随机变量,可以是离散变量(如天气=晴/雨)或连续变量(如温度=20℃)。

  • :表示随机变量之间的依赖关系。

    • 有向边:因果关系,表示一个变量直接影响另一个变量。
    • 无向边:表示变量之间的相互关联,没有特定方向。
  • 概率分布:描述每个变量的可能取值及其概率,并结合条件概率描述依赖关系。

1.2 两种主要类型

  1. 贝叶斯网络(Bayesian Network)

    • 有向无环图(Directed Acyclic Graph, DAG)。

    • 节点表示随机变量,边表示条件依赖关系。

    • 例如:

      • 节点:天气(晴、雨)、草坪湿润(是、否)。
      • 边:天气 → 草坪湿润(天气影响草坪湿润)。
    • 优点:能表达因果关系,适用于动态系统建模。

  2. 马尔科夫随机场(Markov Random Field, MRF)

    • 无向图

    • 节点表示随机变量,边表示相邻变量的关联性。

    • 例如:

      • 在图像分割中,相邻像素通常有相似的颜色。
    • 优点:适用于不需要方向性假设的模型,如图像处理和空间关系建模。


2. 概率图模型的主要功能

2.1 表达变量的依赖关系

  • PGM通过图结构直观地展示变量之间的条件独立性。
  • 例如,在贝叶斯网络中,如果 A→B→C,则 C 和 A 是条件独立的(给定 B)。

2.2 推理

  • 根据观测数据对未知变量进行推断。

  • 例如:

    • 已知天气是雨天,推断草坪湿润的概率。
    • 在医疗诊断中,已知某些症状,推断疾病的可能性。

2.3 学习

  • 从数据中学习模型的结构和参数。
  • 例如,从大量图像数据中学习像素之间的概率关系。

3. 概率图模型的优势

  1. 直观性:通过图结构表达变量之间的复杂依赖关系,比单纯用数学公式描述更易理解。
  2. 可扩展性:适合处理大规模多变量系统。
  3. 通用性:适用于从时间序列(动态贝叶斯网络)到空间关系(马尔科夫随机场)等多种场景。

4. 应用场景

  1. 自然语言处理(NLP)

    • 语法分析:词之间的依赖关系建模(如隐马尔可夫模型,HMM)。
    • 主题建模:文档中主题的概率分布建模(如LDA)。
  2. 计算机视觉

    • 图像分割:通过马尔科夫随机场建模像素之间的依赖关系。
    • 目标识别:通过条件随机场(CRF)建模物体之间的关系。
  3. 医疗诊断

    • 基于症状预测疾病(如贝叶斯网络表示疾病和症状的因果关系)。
    • 基于患者历史数据建模病情发展。
  4. 推荐系统

    • 利用用户行为和物品特征建模(如用户对电影评分的概率分布)。
  5. 机器人学

    • 路径规划和状态估计(如动态贝叶斯网络建模传感器数据)。

案例:基于贝叶斯网络的医疗诊断

1. 背景

医疗诊断中,医生经常需要根据症状(咳嗽、发烧)推断可能的疾病(如流感、肺炎)。这是一个典型的不确定性推理问题,非常适合用贝叶斯网络来建模。


2. 数据集:开源的"疾病与症状关系"数据

数据集Symptom Disease Data(公开健康数据集,例如 [Kaggle 的疾病与症状数据])。

包含以下信息:

  • 随机变量:疾病(Disease),症状(Symptoms)。

  • 示例数据:疾病 D 与症状 S1​,S2​,S3​ 的条件概率,如:

    • P(咳嗽∣流感)=0.8 (咳嗽患流感的概率0.8)
    • P(咳嗽∣肺炎)=0.7

3. 问题建模

随机变量:

  1. 疾病变量

    • D:可能的疾病(如流感、肺炎、普通感冒)。
  2. 症状变量

    • S1:是否有咳嗽
    • S2:是否有发烧
    • S3:是否感到乏力

网络结构:

贝叶斯网络用有向无环图(DAG)表示疾病和症状的因果关系:

markdown 复制代码
    疾病(D)
     ↘  ↙  ↘
    咳嗽   发烧   乏力

条件概率表(CPT)示例:

  1. 疾病的先验概率

    • P(流感)=0.3,P(肺炎)=0.2,P(普通感冒)=0.5
  2. 症状的条件概率(示例数据):

    • P(咳嗽∣流感)=0.8,P(咳嗽∣肺炎)=0.7,P(咳嗽∣普通感冒)=0.6
    • 类似地定义发烧、乏力的条件概率。

4. 任务

  • 目标 1:已知某人有咳嗽和发烧,计算他患流感的概率 P(流感∣咳嗽,发烧)
  • 目标 2:更新贝叶斯网络模型,基于更多症状推断概率。

5. 实现步骤

5.1 导入必要工具

我们使用 Python 和 pgmpy 库,专门用于概率图模型的构建和推断。

python 复制代码
import numpy as np
import pandas as pd
from pgmpy.models import BayesianNetwork
from pgmpy.inference import VariableElimination

5.2 构建贝叶斯网络

定义疾病和症状之间的关系及条件概率表(CPT)。

python 复制代码
# 定义贝叶斯网络结构
model = BayesianNetwork([('Disease', 'Cough'), 
                         ('Disease', 'Fever'), 
                         ('Disease', 'Fatigue')])

# 定义先验概率(疾病)
cpd_disease = pd.DataFrame({
    'Disease': ['Flu', 'Pneumonia', 'Cold'],
    'Probability': [0.3, 0.2, 0.5] # [流感, 肺炎, 普通感冒]
})

# 定义咳嗽症状的条件概率表
cpd_cough = pd.DataFrame({
    'Disease': ['Flu', 'Pneumonia', 'Cold'],
    'Cough_Yes': [0.8, 0.7, 0.6],
    'Cough_No': [0.2, 0.3, 0.4]
})
# 定义发烧症状的条件概率表
cpd_fever = pd.DataFrame({
    'Disease': ['Flu', 'Pneumonia', 'Cold'],
    'Fever_Yes': [0.9, 0.8, 0.3],
    'Fever_No': [0.1, 0.2, 0.7]
})

cpd_fatigue = pd.DataFrame({
    'Disease': ['Flu', 'Pneumonia', 'Cold'],
    'Fatigue_Yes': [0.7, 0.6, 0.4],
    'Fatigue_No': [0.3, 0.4, 0.6]
})
  • 如果患者患有流感(Flu):咳嗽的概率为 P=0.8,不咳嗽的概率为 P=0.2。
  • 如果患者患有肺炎(Pneumonia):咳嗽的概率为 P=0.7,不咳嗽的概率为 P=0.3。
  • 如果患者患有流感(Flu):发烧的概率为 P=0.9,不发烧的概率为 P=0.1。

5.3 推理(Inference)

pgmpy 的推理工具进行推断。

python 复制代码
# 加载条件概率到网络
from pgmpy.factors.discrete import TabularCPD

model.add_cpds(
    TabularCPD(variable='Disease', variable_card=3, 
               values=[[0.3], [0.2], [0.5]]),
    TabularCPD(variable='Cough', variable_card=2, 
               values=[[0.8, 0.7, 0.6], [0.2, 0.3, 0.4]], 
               evidence=['Disease'], evidence_card=[3]),
    TabularCPD(variable='Fever', variable_card=2, 
               values=[[0.9, 0.8, 0.3], [0.1, 0.2, 0.7]], 
               evidence=['Disease'], evidence_card=[3]),
    TabularCPD(variable='Fatigue', variable_card=2, 
               values=[[0.7, 0.6, 0.4], [0.3, 0.4, 0.6]], 
               evidence=['Disease'], evidence_card=[3])
)

# 验证模型
assert model.check_model()

# 推理工具
inference = VariableElimination(model)

# 计算:已知咳嗽和发烧,患流感的概率
result = inference.query(variables=['Disease'], evidence={'Cough': 1, 'Fever': 1})
print(result)

TabularCPD(variable='Fever', variable_card=2, values=[[0.9, 0.8, 0.3], [0.1, 0.2, 0.7]]解释:

字段解析

  1. variable='Fever'

    • 定义条件概率分布的目标变量是 Fever

    • Fever 是一个二值变量,即它可以取两个状态:

      • Fever=Yes(表示发烧)
      • Fever=No(表示不发烧)
  2. variable_card=2

    • 定义 Fever 变量的基数 (cardinality),即 Fever 只有两个可能的状态:YesNo
  3. values=[[0.9, 0.8, 0.3], [0.1, 0.2, 0.7]]

    • 定义 Fever 的条件概率表(CPT),每一列表示在不同疾病条件下 Fever=YesFever=No 的概率。

    • 具体含义如下:

      Disease Fever=Yes (P) Fever=No (P)
      Disease[0] (流感) 0.9 0.1
      Disease[1] (肺炎) 0.8 0.2
      Disease[2] (感冒) 0.3 0.7
      • 在流感的情况下(Disease[0]):

        • 发烧的概率P=0.9
        • 不发烧的概率 P=0.1
      • 在肺炎的情况下(Disease[1]):

        • 发烧的概率 P=0.8
        • 不发烧的概率 P=0.2
      • 在普通感冒的情况下(Disease[2]):

        • 发烧的概率 P=0.3
        • 不发烧的概率 P=0.7
  4. evidence=['Disease']

    • Fever 的概率是条件概率,依赖于随机变量 Disease 的取值。
    • 即,Fever 的状态由 Disease 的状态决定。
  5. evidence_card=[3]

    • 定义条件变量 Disease 的基数为 3(因为 Disease 有三种可能的状态:流感、肺炎、普通感冒)。

逻辑意义

该 CPD 表明:

  • 不同疾病对发烧的影响不同。

  • 比如:

    • 流感(Disease[0])的患者有很高的概率(0.9)发烧。
    • 肺炎(Disease[1])的患者也有较高的概率(0.8)发烧。
    • 而普通感冒(Disease[2])的患者发烧概率较低(0.3)。

通过这种条件概率表,可以用贝叶斯网络对症状(如发烧)和疾病之间的因果关系进行建模。


6. 结果分析

其中:

  • Disease(0)Disease(1)Disease(2) 分别代表三种疾病("流感"、"肺炎"和"普通感冒")。
  • phi(Disease) 表示每种疾病的后验概率,即在给定证据(咳嗽和发烧)下,各疾病的发生概率。

具体分析

  1. Disease(2) 的概率为 0.8861(88.61%):

    • 这是后验概率中最高的值,表明在观测到咳嗽和发烧的情况下,Disease(2) (普通感冒)最有可能是患者的疾病。
    • 高概率的原因可能是普通感冒的症状(咳嗽和发烧)条件概率较高,或该疾病的先验概率较大(如 50%)

完整代码

python 复制代码
import numpy as np
from pgmpy.models import BayesianNetwork
from pgmpy.factors.discrete import TabularCPD
from pgmpy.inference import VariableElimination

# 1. 定义贝叶斯网络结构
model = BayesianNetwork([('Disease', 'Cough'), 
                         ('Disease', 'Fever'), 
                         ('Disease', 'Fatigue')])

# 2. 定义条件概率表(CPT)
# 疾病的先验概率
cpd_disease = TabularCPD(
    variable='Disease', variable_card=3, 
    values=[[0.3], [0.2], [0.5]]  # [流感, 肺炎, 普通感冒]
)

# 咳嗽的条件概率
cpd_cough = TabularCPD(
    variable='Cough', variable_card=2,
    values=[[0.8, 0.7, 0.6],  # P(Cough=Yes | Disease)
            [0.2, 0.3, 0.4]], # P(Cough=No | Disease)
    evidence=['Disease'], evidence_card=[3]
)

# 发烧的条件概率
cpd_fever = TabularCPD(
    variable='Fever', variable_card=2,
    values=[[0.9, 0.8, 0.3],  # P(Fever=Yes | Disease)
            [0.1, 0.2, 0.7]], # P(Fever=No | Disease)
    evidence=['Disease'], evidence_card=[3]
)

# 乏力的条件概率
cpd_fatigue = TabularCPD(
    variable='Fatigue', variable_card=2,
    values=[[0.7, 0.6, 0.4],  # P(Fatigue=Yes | Disease)
            [0.3, 0.4, 0.6]], # P(Fatigue=No | Disease)
    evidence=['Disease'], evidence_card=[3]
)

# 3. 将CPT添加到模型中
model.add_cpds(cpd_disease, cpd_cough, cpd_fever, cpd_fatigue)

# 验证模型是否正确
assert model.check_model()

# 4. 创建推理工具
inference = VariableElimination(model)

# 5. 推理:已知咳嗽和发烧,计算疾病概率分布
result = inference.query(variables=['Disease'], evidence={'Cough': 1, 'Fever': 1})

# 输出结果
print(result)
相关推荐
MadPrinter11 分钟前
SpringBoot学习日记 Day11:博客系统核心功能深度开发
java·spring boot·后端·学习·spring·mybatis
dasseinzumtode11 分钟前
nestJS 使用ExcelJS 实现数据的excel导出功能
前端·后端·node.js
淦出一番成就14 分钟前
Java反序列化接收多种格式日期-JsonDeserialize
java·后端
Java中文社群16 分钟前
Hutool被卖半年多了,现状是逆袭还是沉寂?
java·后端
程序员蜗牛1 小时前
9个Spring Boot参数验证高阶技巧,第8,9个代码量直接减半!
后端
yeyong1 小时前
咨询kimi关于设计日志告警功能,还是有启发的
后端
库森学长1 小时前
2025年,你不能错过Spring AI,那个汲取了LangChain灵感的家伙!
后端·openai·ai编程
加载中3611 小时前
pnpm时代包版本不一致问题还是否存在
前端·面试·npm
Java水解1 小时前
Spring Boot 启动流程详解
spring boot·后端
学历真的很重要1 小时前
Claude Code Windows 原生版安装指南
人工智能·windows·后端·语言模型·面试·go