石榴1.0.0 Python pomegranate库实现基于概率分布、贝叶斯网络、混合隐马尔可夫模型等, Python 的图模型和概率模型工具包

GitHub - jmschrei/pomegranate: Fast, flexible and easy to use probabilistic modelling in Python.

首先列举当前主要构建贝叶斯网络的一些工具:

1. 软件:Netica、SMILE

2. MATLAB包:BNT

3. Python包:pgmpy、libpgm、pomegranate

4. R包:dbnlearn

如果是单纯的进行贝叶斯网络的学习与推理的话,那我建议用Netica或者是pgmpy这两个。
其中Netica软件中文教程比较多,教育版免费使用,而且不需要写代码,只用鼠标操作即可
除了贝叶斯网络还可以很方便的手动构建动态贝叶斯网络),但是要注意这个软件是在windows系统下面的
。如果是在Python环境下的话,我强烈推荐pgmpy包,因为这个pgmpy目前还有作者在进行日常维护,
内部的样例代码也很好用,给作者发邮件也会得到很好的解答,而且这个包还有一个gitter的group讨论,
代码也很简单,初学者很容易上手,比较适合在Python环境下有后续任务需要调用贝叶斯网络输出结果的情况。

用pgmpy进行参数学习的代码我就不放了,因为在pgmpy的github主页上已经有很详细的BN参数学习包括推理的的代码了
,直接按需要修改就行。

但是关于动态贝叶斯网络(Dynamic Bayesian Network)
DBN目前在pgmpy官方教程里只有通过设置CPD的方式进行后续推理而没有进行参数学习/结构学习的方法,
询问过作者是因为在DBN上进行参数学习还是有些困难的。

omegranate 简介

pomegranate 是基于 Python 的图模型和概率模型工具包,它使用 Cython 实现以加快反应速度。它源于 YAHMM,可实现快速、高效和极度灵活的概率模型,如概率分布、贝叶斯网络、混合隐马尔可夫模型等。概率建模最基础的级别是简单的概率分布。以语言建模为例,概率分布就是是一个人所说的每个单词出现频率的分布。

1. 概率分布

第二个级别是以更复杂的方式使用简单分布的概率模型。马尔可夫链可以扩展简单的概率分布,仍旧以语言建模为例,即某个单词的概率依赖于先前所说单词。隐马尔可夫模型中某个单词的概率依赖于前一个词的潜在/隐藏状态,如名词通常在形容词后面。

马尔可夫链贝叶斯分类器和朴素贝叶斯一般混合模型隐马尔可夫模型贝叶斯网络因子图

第三个级别是概率模型的堆叠,可以建模更复杂的现象。如果单个隐马尔可夫模型可以捕捉口音(如某个人的说话习惯),那么混合隐马尔可夫模型可以将其调整以适应特定情况。比如,一个人可能在工作时使用更为正式的语言,在与朋友交流时使用稍微随意的语言。通过将其建模为混合隐马尔可夫模型,我们将这个人的语言表示为各种口音的「混合」。

1. GMM-HMMs

2. 混合模型

3. 模型的贝叶斯分类器

注意重要提示:石榴1.0.0是石榴的全新重写,使用PyTorch代替Cython作为计算后端。虽然支持相同的功能,但API有很大的不同。请查看教程和示例文件夹以获得重写代码的帮助。

阅读文档 | 教程 | 例子

石榴是一个概率建模库,由它的模块化实现和所有模型的概率分布处理来定义。模块化实现允许人们容易地将正态分布放入混合模型中以创建高斯混合模型,就像将伽马和泊松分布放入混合模型中以创建异质混合一样容易。但这还不是全部!因为每个模型都被视为一个概率分布,所以贝叶斯网络可以像正态分布一样容易地被放入混合模型中,并且隐马尔可夫模型可以被放入贝叶斯分类器中以对序列进行分类。这两种设计选择共同实现了任何其他概率建模包所没有的灵活性。

最近,石榴(v1.0.0)使用PyTorch彻底重写,替换了过时的Cython后端。这次重写给了我一个机会来修正我作为一个bb软件工程师所做的许多糟糕的设计选择。不幸的是,这些变化中有许多并不是向后兼容的,并且会扰乱工作流程。另一方面,这些变化大大加快了大多数方法的速度,改进并简化了代码,修复了社区多年来提出的许多问题,并使其更容易做出贡献。我在下面写了更多,但你现在可能在这里,因为你的代码坏了,这是TL;博士

特别大声喊到数字焦点用特别发展基金来支持这项工作。

装置

pip install pomegranate

如果在重写之前需要最新的Cython版本,请使用pip install pomegranate==0.14.8。您可能需要手动安装v3之前的Cython版本。

为什么要重写?

这种重写的动机有四个主要原因:

  • 速度:原生PyTorch通常比我编写的手动调整的Cython代码快得多。
  • 特征:PyTorch有很多特性,比如序列化、混合精度和GPU支持,现在可以直接在石榴中使用,而不需要我做额外的工作。
  • 社区贡献:许多人在使用石榴时面临的一个挑战是,他们不能修改或扩展它,因为他们不了解Cython。即使他们知道Cython,每次我尝试添加新功能、修复错误或发布新版本时,编写代码都是一种痛苦。使用PyTorch作为后端大大减少了添加新特性所需的工作量。
  • 互用性:像PyTorch这样的库提供了一个宝贵的机会,不仅可以利用它们的计算后端,还可以更好地集成到现有资源和社区中。这种重写将使人们更容易将概率模型与神经网络集成为损失、约束和结构正则化,以及与基于PyTorch的其他项目集成。

高层变动

  1. 一般
  • PyTorch重写了整个代码库,所有模型都是torch.nn.Module
  • 这个代码库由超过800个单元测试组成的综合套件检查,调用assert语句数千次,比以前的版本多得多。
  • 安装问题现在可能来自PyTorch,有无数的资源可以帮助解决。
  1. 特征
  • 所有型号现在都有GPU支持
  • 所有型号现在都支持半/混合精度
  • 序列化现在由PyTorch处理,产生了更紧凑和高效的I/O
  • 现在通过支持缺失值torch.masked.MaskedTensor目标
  • 先验概率现在可以被传递给所有相关的模型和方法,并实现比以前更全面/灵活的半监督学习
  1. 模型
  • 默认情况下,所有分布现在都是多元的,并且独立处理每个要素(除了正态分布)
  • “分发”已从名称中删除,例如,NormalDistribution就是现在Normal
  • FactorGraph现在被支持为一等公民,拥有所有的预测和训练方法
  • 隐马尔可夫模型已经被分成DenseHMMSparseHMM转换矩阵编码方式不同的模型DenseHMM对象在真正密集的图上速度明显更快
  1. 差异
  • NaiveBayes已被永久删除,因为它与BayesClassifier
  • MarkovNetwork尚未实施
  • 贝叶斯网络的约束图和约束结构学习还没有实现
  • 隐马尔可夫模型的静默状态尚未实现
  • 用于隐马尔可夫模型的维特比还没有实现

速度

石榴v1.0.0中的大多数模型和方法都比早期版本中的模型和方法更快。这通常根据复杂性进行扩展,其中对于小数据集上的简单分布只看到小的加速,但是对于大数据集上的更复杂的模型,例如隐马尔可夫模型训练或贝叶斯网络推理,则看到大得多的加速。目前值得注意的例外是,除了Chow-Liu树构建之外,贝叶斯网络结构学习仍然是不完整的,也没有快多少。在下面的例子中,torchegranate指用于开发石榴v1.0.0和的临时存储库pomegranate指石榴v0.14.8。

k均值

谁知道这里发生了什么?狂野。

​编辑

隐马尔可夫模型

密集转移矩阵(CPU)

​编辑

稀疏转移矩阵

​编辑

用密集转移矩阵训练125节点模型

​编辑

贝叶斯网络

​编辑 ​编辑

特征

注意请参阅教程代码示例的文件夹。

从Cython后端切换到PyTorch后端已经启用或扩展了大量功能。因为重写是PyTorch的一个薄薄的包装,随着PyTorch新特性的发布,它们可以应用于石榴模型,而不需要我发布新的版本。

GPU支持

石榴中的所有发行版和方法现在都有GPU支持。因为每个发行版都是一个torch.nn.Module对象,其用法与用PyTorch编写的其他代码相同。这意味着模型和数据都必须由用户移动到GPU。例如:

>>> X = torch.exp(torch.randn(50, 4))

# Will execute on the CPU
>>> d = Exponential().fit(X)
>>> d.scales
Parameter containing:
tensor([1.8627, 1.3132, 1.7187, 1.4957])

# Will execute on a GPU
>>> d = Exponential().cuda().fit(X.cuda())
>>> d.scales
Parameter containing:
tensor([1.8627, 1.3132, 1.7187, 1.4957], device='cuda:0')

同样,所有的模型都是发行版,因此可以在GPU上类似地使用。当一个模型被移动到GPU时,与之相关联的所有模型(例如,发行版)也被移动到GPU。

>>> X = torch.exp(torch.randn(50, 4)).cuda()
>>> model = GeneralMixtureModel([Exponential(), Exponential()]).cuda()
>>> model.fit(X)
[1] Improvement: 1.26068115234375, Time: 0.001134s
[2] Improvement: 0.168121337890625, Time: 0.001097s
[3] Improvement: 0.037841796875, Time: 0.001095s
>>> model.distributions[0].scales
Parameter containing:
>>> model.distributions[1].scales
tensor([0.9141, 1.0835, 2.7503, 2.2475], device='cuda:0')
Parameter containing:
tensor([1.9902, 2.3871, 0.8984, 1.2215], device='cuda:0')

混合精度

从理论上讲,石榴模型可以在与其他PyTorch模块相同的混合或低精度模式下运行。然而,由于石榴使用比大多数神经网络更复杂的操作,这有时在实践中不起作用或没有帮助,因为这些操作没有在低精度机制中优化或实现。所以,希望这个特性随着时间的推移会变得更加有用。

>>> X = torch.randn(100, 4)
>>> d = Normal(covariance_type='diag')
>>>
>>> with torch.autocast('cuda', dtype=torch.bfloat16):
>>>     d.fit(X)

序列化

石榴发行版都是torch.nn.Module所以序列化和其他PyTorch模型是一样的。

保存:

>>> X = torch.exp(torch.randn(50, 4)).cuda()
>>> model = GeneralMixtureModel([Exponential(), Exponential()], verbose=True)
>>> model.cuda()
>>> model.fit(X)
>>> torch.save(model, "test.torch")

加载:

>>> model = torch.load("test.torch")

火炬.编译

注意 torch.compilePyTorch团队正在积极开发,可能会快速改进。现在,你可能需要通过check_data=False初始化模型以避免一个兼容性问题。

在PyTorch v2.0.0中,torch.compile是作为一个灵活的工具包装器引入的,它将操作融合在一起,使用CUDA图,并通常尝试消除GPU执行中的I/O瓶颈。因为这些瓶颈在许多石榴用户面临的中小型数据设置中非常明显,torch.compile看起来它非常有价值。而不是针对整个模型,后者通常只是编译forward方法时,应该从对象中编译单独的方法。

# Create your object as normal
>>> mu = torch.exp(torch.randn(100))
>>> d = Exponential(mu).cuda()

# Create some data
>>> X = torch.exp(torch.randn(1000, 100))
>>> d.log_probability(X)

# Compile the `log_probability` method!
>>> d.log_probability = torch.compile(d.log_probability, mode='reduce-overhead', fullgraph=True)
>>> d.log_probability(X)

不幸的是,我很难找到torch.compile以嵌套方式调用方法时工作,例如,编译predict方法,该方法在混合模型内部调用log_probability每次分配的方法。我试图以一种避免这些错误的方式组织代码,但是因为现在的错误消息是不透明的,我遇到了一些困难。

缺少值

石榴支持通过处理缺失值的数据torch.masked.MaskedTensor对象。简单地说,我们只需要在丢失的值上加一个面具。

>>> X = <your tensor with NaN for the missing values>
>>> mask = ~torch.isnan(X)
>>> X_masked = torch.masked.MaskedTensor(X, mask=mask)
>>> d = Normal(covariance_type='diag').fit(X_masked)
>>> d.means
Parameter containing:
tensor([0.2271, 0.0290, 0.0763, 0.0135])

目前所有算法都将遗漏视为可以忽略的东西。例如,当计算缺失值的列的平均值时,平均值将只是当前值的平均值。缺失值不会被估算,因为不适当的估算会使您的数据产生偏差,产生扭曲分布的不太可能的估计值,还会缩小方差。

由于并非所有运算都适用于MaskedTensors,因此对于缺失值,尚不支持以下分布:伯努利分布、分类分布、全协方差正态分布、均匀分布

先验概率和半监督学习

石榴1.0.0版的一个新特性是能够为混合模型、贝叶斯分类器和隐马尔可夫模型的每个观察值传递先验概率。这些是在评估可能性之前观察值属于模型组件的先验概率,范围应该在0到1之间。当这些值包括一个观察值1.0时,它被视为一个标签,因为在将该观察值分配给一个状态时,可能性不再重要。因此,当每个观察值对于某个状态为1.0时,可以使用这些先验概率来进行标记训练,当观察值的子集(包括当序列对于隐马尔可夫模型只是部分标记时)时,可以使用半监督学习,或者当值在0和1之间时,可以使用更复杂形式的加权。

​编辑

关于

Python中快速、灵活且易于使用的概率建模。

pomegranate.readthedocs.org/en/latest/

主题

大蟒 机器学习 pytorch 概率图形模型

资源

许可证

麻省理工学院许可证

 

  • 实现文字拼写检查

     

    概要

    贝叶斯网络是一种基于概率的图模型,可用于建立变量之间的条件概率关系。在拼写检查器中,贝叶斯网络可以通过建立一个隐含状态、错误观察值和正确观察值三个节点之间的概率关系来实现自动拼写校正。本文将介绍如何使用Python和pomegranate库实现基于贝叶斯网络的拼写检查器。

    一、准备数据

    我们使用Peter Norvig的“big.txt”文本文件作为样本数据集。该数据集包含了大量英语文章的单词,大小写已经被统一为小写。我们需要按行读取该文件,并利用Python中的re库对文本进行初步处理:

    1

    2

    3

    4

    5

    6

    7

    8

    import re

    # 读取文本并进行预处理

    with open('big.txt') as f:

        texts = f.readlines()

    # 清洗数据,去掉数字和标点符号

    words = []

    for t in texts:

        words += re.findall(r'\w+', t.lower())

    二、构建贝叶斯网络

    我们需要建立一个贝叶斯网络来处理拼写检查器任务,该网络包含3个节点:隐含状态(正确拼写)、错误观察和正确观察。其中隐含状态是因果节点,而错误观察节点和正确观察节点直接依赖隐含状态节点。

    以下是建立贝叶斯网络的代码:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    from pomegranate import *

    # 建立隐因节点

    correct_spell = State(DiscreteDistribution(dict.fromkeys(words, 1)), name='Correct_Spelling')

    # 建立观察节点(错误拼写和正确拼写)

    letter_dist = {}

    for w in words:

        for l in w:

            if l not in letter_dist:

                letter_dist[l] = len(letter_dist)

    error_spelling = State(DiscreteDistribution(letter_dist), name='Error_Spelling')

    correct_spelling_observed = State(DiscreteDistribution(letter_dist), name='Correct_Spelling_Observed')

    # 建立连边关系

    model = BayesianNetwork('Spelling Correction')

    model.add_states(correct_spell, error_spelling, correct_spelling_observed)

    model.add_edge(correct_spell, error_spelling)

    model.add_edge(correct_spell, correct_spelling_observed)

    model.bake()

    三、训练模型

    数据准备好后,我们可以开始训练贝叶斯网络。训练期间,我们需要根据观察数据来估计网络参数。

    以下是训练贝叶斯网络的代码:

    1

    2

    3

    4

    5

    # 利用语料库训练贝叶斯网络

    for word in words:

        model.predict(word)

    # 打印结果(即每个字母在不同位置出现的统计概率)

    print(error_spelling.distribution.parameters[0])

    从上述代码中生成的结果可以看到,在训练过程中,BayesianNetwork通过学习样本数据中单词中不同字母出现次数的概率分布,可以更好地捕捉英语单词的正确语法结构。

    四、测试模型

    训练完成后,我们可以通过贝叶斯网络并使用Viterbi算法来查找最优路径,以进行拼写校正。

    以下是测试贝叶斯网络的代码:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    from pomegranate import *

    # 定义输入单词

    test_word = 'speling'

    # 将输入单词转换为列表

    letters = list(test_word)

    # 遍历该输入单词中的所有字母,并将每个字母的错误概率加起来(实际上就是计算“错误观察”节点的联合概率)

    error_prob = sum([error_spelling.distribution.probability(l) for l in letters])

    # 构建“正确观察”节点的联合概率矩阵

    correct_prob = [[''.join(letters[k:j]) for j in range(k+1, len(letters)+1)] for k in range(len(letters))]

    # 利用Viterbi算法查找最优路径(即最可能的正确单词)

    corrected_word = max(model.viterbi(correct_prob)[1], key=lambda x: x[1])[0]

    # 打印结果

    print('Original word:', test_word)

    print('Corrected word:', corrected_word)

    在上述代码中,我们将输入单词转化构建“正确观察”节点的联合概率矩阵。最找词),并将其作为自动校正的结果输出。

  • 8
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值