【NLP】第十一章:隐马尔可夫模型 HMM (Hidden Markov Model)

本来是想讲BERT的,但是BERT的重点是部署应用,而且用BERT跑一些NLP领域的很多任务时,一般做法都是BERT后面再串一个概率模型来约束输出,比如串联一个条件随机场CRF模型。而我们还没讲CRF呢,而且要了解CRF需要首先了解隐马尔可夫模型HMM(Hidden Markov Model),但HMM又牵扯了好几个比较晦涩的算法,比如前向、后向算法、维特比解码算法(viterbi algorothm)、EM算法等,内容有点难也有点多,所以这里我单独针对HMM和CRF写一两个章节,为BERT铺个垫。

九、隐马尔可夫模型 HMM (Hidden Markov Model)

隐马尔可夫模型,HMM (Hidden Markov Model),是一个拟合序列数据的概率模型,也叫统计模型,是用来描述马尔可夫过程的模型。本章节从原理、手动计算、调包实现,三方面来说清楚HMM。

(一)HMM的原理
网上搜HMM,大部分文章一上来就整篇整篇的数学推导,随意晦涩大量的符号和脚标,让大部分读者上来就一脸懵。我最讨厌这种卖弄了,其实底层的数学没那么难理解,搞一堆数学符号让人抓狂。本篇通过一个最通俗的例子,来抽取HMM的底层原理。

1、做一个简单的小概率题

这个概率题就是HMM的概型,理解了这道题你就理解了HMM。我拿这道题来对比理解HMM。
这道题的解答案其实不难,就是一系列概率的相乘相加,但我们的目的是从它抽取HMM的原理。所以我们先不急着解题。

这道题最大的难点可能就是有的人不理解矩阵A和矩阵B。这里我先解释一下:
(1)我们先按行看矩阵A:

第一行的0.5表示:我前一次是从盒子1中抽球的,那我这一次还从盒子1中抽球的概率。
第一行的0.4表示:我前一次是从盒子1中抽球的,那我这一次从盒子2中抽球的概率。
第一行的0.1表示:我前一次是从盒子1中抽球的,那我这一次从盒子3中抽球的概率。

同理A矩阵的第二行、第三行:
第二行就表示:我前一次是从盒子2中抽球的,那我这一次去盒子1、盒子2、盒子3中抽取的概率。
第三行就表示:我前一次是从盒子3中抽球的,那我这一次去盒子1、盒子2、盒子3中抽取的概率。

所以,矩阵A的每行3个数字之和都是1
而且A矩阵是一个方阵,就是行数=列数=盒子的种类数

(2)矩阵B就更好理解了,矩阵B的第一行表示我已经抽取的是盒子1,那我抽取白球的概率就是0.4,抽取黑球的概率就是0.6了,因为第一个盒子只有4白6黑嘛。同理矩阵B的第二行和第三行。所以矩阵B的每行数字之和也是1。但是B矩阵的行数=盒子的种类数,列数=小球的种类数

2、从上面的概率题,抽出HMM概型

(1)从上面的概率题,抽取HMM中的相关概念
一是,上题中的盒1盒2盒3,对应在HMM中就是隐含状态(states)。对应下图A处。

二是,上题中的最后抽取的序列是“白黑白白黑”,这个序列叫观测序列(observations),也叫可见状态链,对应下图C处。

三是,由于每次抽取的小球的颜色的概率不仅取决于,小球所在的盒子中的黑白球的比例,还取决于你是从哪个盒子中抽的,所以其实观测序列还对应着一个隐含状态序列,就是下图的B处(说明:B只是我胡乱做的一个隐藏状态序列,真实的B可能不是这个序列)。这个隐藏状态序列叫隐藏状态链
意思就是我们是可以观测到小球颜色序列,但是观测不到每次小球都是从哪个盒子中取的,也就是观测不到盒子序列。但是我们可以肯定的是:当我们得到“白黑白白黑”这个序列时,一定是存在一个对应的盒子序列的,虽然我们观测不到这个盒子序列,但它是真实存在的。这就好比我们只能看到股票的涨涨跌跌,但我们看不到涨跌背后的原因,但是我们笃定涨跌背后肯定是有原因的。

四是,上题的观测序列长度=隐藏状态序列长度=5。其实现实很多问题中,二者不一定非得相等。

可见:一个隐马尔科夫链,其实是一个双链条!一条C链条,另一条B链条。
而且B链和C链都是一个随机过程,所以隐马尔可夫链也是一个双重随机过程。也就是一个HMM有两个随机序列。
在很多现实问题中,有些现象不符合隐马尔可夫过程,但人们会通过扩展"可观测"和"隐藏"状态的概念来构造一个HMM过程,然后用HMM来拟合实际问题。一般我们的做法是:把C当成"果",把对应的B当成"因"。先寻找多种"因",并总结出因与因之间的关系(就是矩阵A)、因与果之间的关系(就是矩阵B),C又是可观测到的,就可以用HMM进行拟合了。

(2)从上面的概率题,抽取HMM中的两个假设
隐马尔科夫模型有两个基本假设:
一是,有限状态马尔科夫性假设(First-Order Markov Assumption):

这个假设表明,系统在某个时间点的状态只依赖于前一个时间点的状态,与更早的状态无关。
数学表示为:P(qt+1=sj∣qt=si,qt−1,…,q1)=P(qt+1=sj∣qt=si)
这个假设简化了状态转移的计算,使得计算量大大降低。

二是,观测独立性假设(Observation Independence Assumption):

这个假设表明,给定当前的隐藏状态,观测值与之前或之后的观测值是相互独立的。
数学表示为:P(Ot=vk∣qt=sj,Ot−1,Ot−2,…,O1)=P(Ot=vk∣qt=sj)
这一假设使得观测序列的概率计算更为简便,只需考虑当前隐藏状态下的观测概率。

如果上面的官话和数学表示看不懂,我给大家翻译成大白话,就是下图:

这两个假设也不难理解,第一个假设就是说隐藏状态只与其前一个隐藏状态相关,与其他无关。第二个假设就是说每个可观测状态都是由它对应的隐藏状态生成的,与其他可观测状态无关

又是车轱辘话,这不就是矩阵A和矩阵B嘛。矩阵A就是假设1的数学语言,矩阵B就是假设2的数学语言。也所以A矩阵我们叫状态转移概率矩阵B矩阵叫发射概率矩阵,就是从隐藏状态生成观测结果的过程。矩阵A是因与因之间的关系,矩阵B是每个因与果之间的关系。

(3)从上面的概率题,抽取HMM中的三元组:λ = (π, A, B)
一个隐马尔科夫模型可以用一个三元组:λ=(π,A,B)来表示。其中:π是初始状态分布向量;A是状态转移概率矩阵;B是观测概率矩阵。

还是车轱辘话,三元组里面的π,A,B,前面解释多次了。但是这里要说明的是:我们把(π,A,B)用λ来表示,λ是参数的意思,也就是说π,A,B都是模型的参数。也就是一旦π,A,B定下来后,那HMM概型就确定了。也就是说一组λ=(π,A,B)代表一个HMM。如果π,A,B中的任何一个或多个改变了,那就变成另外一个HMM了。

3、HMM的三个基本问题
隐马尔科夫模型(HMM)涉及三个基本问题:评估问题(Evaluation Problem)、解码问题(Decoding Problem)和学习问题(Learning Problem)。每个问题都有其特定的数学表达方式

很多人看到这里都会冒充一个疑问,为什么会有这三个问题?!很多资料也不解释就直接堆数学公式,真是云里雾里。这里我解释一下:

前面写的概率题,也就是小标题1,其实就是一个HMM问题。
前面写的小标题2,其实就是一个完整的HMM概型包括的所有东西:观测序列Observations、掩藏序列States、初始状态概率π、隐藏状态转换矩阵A、发射矩阵B。就这五大要素。

这就类似于,我告诉了你"2+3=5",也类似于我告诉了你HMM概型的五要素:

(1)你可以求2+3=?这就是评估问题,有的资料上叫计算问题。我们小标题1的概率题其实就是这个问题,就是计算问题,就是让你计算一下得到“白黑白白黑”这个观测序列的概率。而计算这个问题用的是前向算法或者后向算法。后面我会讲这两个算法。

(2)当你知道了2和5,你可以反推2+?=5,这就是解码问题,有的地方也叫预测问题。就好比我现在知道了观测序列Observations、初始状态概率π、隐藏状态转换矩阵A、发射矩阵B,我反推一下隐藏序列States。反推最大概率的隐藏序列使用的方法是维特比算法(Viterbi Algorithm)。后面也会讲该算法。
这里面还有一层逻辑,就是我们为什么要反推最大概率的隐藏序列?当然是为了预测喽。当你知道了隐藏序列,那是不是就知道了下一个最大概率的隐藏状态-->知道了下一个隐藏状态,是不是就知道了这个隐藏状态对应的最大概率观测值了,是不是就做了预测了?!是不是此时就是一个生成模型了?!

(3)当你知道了答案是5,你推测一下?+ ?=5,这就是学习问题。就是我们试着去学一个最优的(π, A, B),让在这个(π, A, B)下,观测序列Observations的概率最大。这种反推模型参数的任务就是学习问题。但是学习问题又分两种情况:
情况1:我们已知的是观测序列Observations和隐藏序列States的情况下,来反推(π, A, B),此时就是一个有监督的学习任务,使用极大似然估计法来估计参数即可。
情况2:我们只已知观测序列Observations,不知道隐藏序列States,来反推(π, A, B),此时是一个无监督的学习任务,此时得使用EM算法(也称为Baum-Welch算法)来估计参数。

(二)HMM的相关算法
与HMM相关的计算就是上面三大问题的计算。本部分给大家展示上面算法的手动计算过程。

1、前向算法
前向算法就是计算观测序列出现的概率。
我们的小概率题就是这个问题。小概率题就是已知观测序列Observations、初始状态概率π、隐藏状态转换矩阵A、发射矩阵B,求观测序列的概率。

我们先根据概率的定义,用最笨的方法(但是最容易理解呀)手动算一下:

上面的手动计算过程是基于最基本的概率常识来计算的。计算过程和答案肯定都不会错。但是这种计算方式太低效。我们回看这个计算过程,其实这个计算过程就是一个递推的过程,直到序列结束,递推也结束,最后加和所有状态下的概率即可。所以我们看很多资料上的前向算法是这样滴:

即使看不懂公式,我们也得用代码把算法捋一遍:

上述代码的计算结果和手动计算的结果是一致的。下面我再拿上面的代码跑一下李航的《统计学习方法》里面的例子,看和书里的计算结果是否一致:

# 初始化观测序列、隐藏状态集合
Observations = ['白球', '黑球', '白球', '白球', '黑球']
Obs_set = ['白球', '黑球']
State_set = ['盒子1', '盒子2', '盒子3']

# 将观测序列、状态集合 转换为索引
obs_seq = torch.tensor([0, 1, 0, 0, 1])  # 白黑白白黑
state_set = torch.tensor([0, 1, 2])   #盒1盒2盒3

# 初始化参数
Start_prob = torch.tensor([0.2, 0.5, 0.3])
Trans_prob = torch.tensor([[0.5, 0.4, 0.1], [0.2, 0.2, 0.6], [0.2, 0.5, 0.3]])
Emission_prob = torch.tensor([[0.4, 0.6],[0.8, 0.2],[0.5, 0.5]])

# 前向算法
def forward(obs_seq, state_set, start_prob, trans_prob, emission_prob):
    T = len(obs_seq)     #观测序列的长度
    N = len(state_set)   #状态种类个数
    alpha = torch.zeros((T, N))    
    alpha[0, :] = start_prob * emission_prob[:, obs_seq[0]]  
    for i in range(1, T):
        alpha[i] = torch.mm(alpha[i-1].unsqueeze(0), trans_prob) * emission_prob[:, obs_seq[i]]
    forward_sum = alpha[-1].sum()
    return alpha, forward_sum

#测试结果
alpha, forward_sum = forward(obs_seq=obs_seq, state_set=state_set, 
                            start_prob=Start_prob, trans_prob=Trans_prob, emission_prob=Emission_prob)
alpha, forward_sum
(tensor([[0.0800, 0.4000, 0.1500],
         [0.0900, 0.0374, 0.1465],
         [0.0327, 0.0934, 0.0377],
         [0.0170, 0.0405, 0.0353],
         [0.0142, 0.0065, 0.0183]]),
 tensor(0.0390))

2、后向算法
后向算法也是计算观测序列出现的概率。后向算法比较难以理解,因为不是常规思路嘛。网上很多资料都是模模糊糊一带而过,对于数学一般的人真是要想很久才能悟出来。我自己也是想了一天,才突然明白的。

但是当我想明白的那一刻,才知道后向算法也是计算序列出现的概率的,当时我的内心是崩溃的,因为前向算法都算出来了,还死磕后向算法干嘛?!吃饱了撑的嘛?!冷静想过以后,只有一个解释:两个算法同时使用可以并行计算,提升效率。假设一个序列有10000个观测值,从前往后一个个算得多费时间呀,正是由于后向算法的发明,我们就可以把这10000个观测值,比如分100个段,就可以并行计算这个100个子序列,效率是不是就大大提升了!所以后向算法还是值得你想破脑袋也要想明白的一个算法。

依旧使用我们前向算法用的小案例,下面我把这个小案例中的所有可能路径都画出来:

讲前向算法时,我就没画图,就直接开始手算概率的,那是因为前向计算太好理解了,就是根据状态概率一步步求结果就行。但是此处的后向算法,理解起来有点费劲,它是从结果一步步倒退所有可能的因!这个就比较麻烦,所以我把所有的可能路径都给大家标出来了,这样方便倒着推算。

下面我也先手动计算一下:

下面开始写后向算法的代码实现:

# 初始化观测序列、隐藏状态集合
Observations = ['白球', '黑球', '白球', '白球', '黑球']
Obs_set = ['白球', '黑球']
State_set = ['盒子1', '盒子2', '盒子3']

# 将观测序列、状态集合 转换为索引
obs_seq = torch.tensor([0, 1, 0, 0, 1])  # 白黑白白黑
state_set = torch.tensor([0, 1, 2])   #盒1盒2盒3

# 初始化参数
Start_prob = torch.tensor([0.2, 0.5, 0.3])
Trans_prob = torch.tensor([[0.5, 0.4, 0.1], [0.2, 0.2, 0.6], [0.2, 0.5, 0.3]])
Emission_prob = torch.tensor([[0.4, 0.6],[0.8, 0.2],[0.5, 0.5]])

# 后向算法
def backward(obs_seq, state_set, start_prob, trans_prob, emission_prob):
    T = len(obs_seq)     #观测序列的长度
    N = len(state_set)   #状态种类个数
    beta = torch.zeros((T, N))    
    beta[0] = torch.mm(Trans_prob,Emission_prob[:, obs_seq[-1]].unsqueeze(1)).reshape(1,-1)
    for i in range(1, T-1):
        beta[i] = (Trans_prob * Emission_prob[:, obs_seq[T-1-i]] *beta[i-1]).sum(1)
    beta[-1] = Start_prob * Emission_prob[:,obs_seq[0]] * beta[-2]        
    backward_sum = beta[-1].sum()
    return beta, backward_sum

# 测试结果
beta, backward_sum = backward(obs_seq=obs_seq, state_set=state_set, 
                            start_prob=Start_prob, trans_prob=Trans_prob, emission_prob=Emission_prob)
beta, backward_sum
(tensor([[0.4300, 0.4600, 0.3700],
         [0.2517, 0.2190, 0.2739],
         [0.1341, 0.1373, 0.1488],
         [0.0587, 0.0662, 0.0522],
         [0.0047, 0.0265, 0.0078]]),
 tensor(0.0390))

不放心的话,我用这个代码也跑一下李航的那个例子:

网上这篇博文也针对这个例子进行了计算: 【大道至简】机器学习算法之隐马尔科夫模型(Hidden Markov Model, HMM)详解(2)---计算问题:前向算法和后向算法原理详解公式推导及Python实现_pycharm中hmm算法-CSDN博客 ,感兴趣的可以参考,写得非常不错,我一开始也是看这篇博文才得到的启发。

3、维特比算法(Viterbi Algorithm)

待续。。。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/969261.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

APP端网络测试与弱网模拟!

当前APP网络环境比较复杂,网络制式有2G、3G、4G网络,还有越来越多的公共Wi-Fi。不同的网络环境和网络制式的差异,都会对用户使用app造成一定影响。另外,当前app使用场景多变,如进地铁、上公交、进电梯等,使…

使用k3s高可用部署rancher

本次部署采用3节点的etcd服务2master节点的k3s使用helm部署的ranchervip(keepalived) 一、安装etcd服务 # 准备 3 个节点部署 etcd cd /hskj/tmp wget https://github.com/etcd-io/etcd/releases/download/v3.3.15/etcd-v3.3.15-linux-amd64.tar.gz tar xzvf etcd-v3.3.15-…

Nginx进阶篇 - nginx多进程架构详解

文章目录 1. nginx的应用特点2. nginx多进程架构2.1 nginx多进程模型2.2 master进程的作用2.3 进程控制2.4 worker进程的作用2.5 worker进程处理请求的过程2.6 nginx处理网络事件 1. nginx的应用特点 Nginx是互联网企业使用最为广泛的轻量级高性能Web服务器,其特点是…

uniapp开发h5部署到服务器

1.发行>网站-PC Web或手机H5(仅适用于uniapp) 2.填写网站域名 3.编译成功后会生成一个unpackage文件夹找到下面的h5 4.接下来会使用一个工具把h5里面的文件放到服务器上面(WinSCP使用其他能部署的工具也行) 5.登录 6.登录成功后…

【C/C++算法】从浅到深学习---滑动窗口(图文兼备 + 源码详解)

绪论:冲击蓝桥杯一起加油!! 每日激励:“不设限和自我肯定的心态:I can do all things。 — Stephen Curry” 绪论​: 本章是算法训练的第二章----滑动窗口,它的本质是双指针算法的衍生所以我将…

AWTK-WEB 快速入门(4) - JS Http 应用程序

XMLHttpRequest 改变了 Web 应用程序与服务器交换数据的方式,fetch 是 XMLHttpRequest 继任者,具有更简洁的语法和更好的 Promise 集成。本文介绍一下如何使用 JS 语言开发 AWTK-WEB 应用程序,并用 fetch 访问远程数据。 用 AWTK Designer 新…

html 点击弹出视频弹窗

一、效果: 点击视频按钮后,弹出弹窗 播放视频 二、代码 <div class="index_change_video" data-video-src="</

FPGA实现UltraScale GTH光口视频转USB3.0传输,基于FT601+Aurora 8b/10b编解码架构,提供2套工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐我已有的所有工程源码总目录----方便你快速找到自己喜欢的项目我这里已有的 GT 高速接口解决方案本博已有的FPGA驱动USB通信方案 3、工程详细设计方案工程设计原理框图输入Sensor之-->OV5640摄像头动态彩条输入视频之-->ADV…

HCIA项目实践--静态路由的总结和简单配置

七、静态路由 7.1 路由器获取未知网段的路由信息&#xff1a; &#xff08;1&#xff09;静态路由&#xff1a;网络管理员手工配置的路由条目&#xff0c;它不依赖网络拓扑的变化进行自动更新&#xff0c;而是根据管理员预先设定的路径来转发数据包。其优点是配置简单、占用系…

Java中如何高效地合并多个对象的List数据:方法与案例解析!

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云/阿里云/华为云/51CTO&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互…

【网络安全 | 漏洞挖掘】价值3133美元的Google IDOR

未经许可,不得转载。 文章目录 正文正文 目标URL:REDACTED.google.com。 为了深入了解其功能,我查阅了 developer.google.com 上的相关文档,并开始进行测试。 在测试过程中,我发现了一个 XSS 漏洞,但它触发的域名是经过正确沙盒化的 *.googleusercontent.com,这符合 …

sqlilabs--小实验

一、先盲注判断 ?id1 and sleep(2)-- 如果发现页面存在注点&#xff0c;使用时间盲注脚本进行注入 import requestsdef inject_database(url):name for i in range(1, 20): # 假设数据库名称长度不超过20low 48 # 0high 122 # zmiddle (low high) // 2while low &l…

QT 异步编程之多线程

一、概述 1、在进行桌面应用程序开发的时候&#xff0c;假设应用程序在某些情况下需要处理比较复制的逻辑&#xff0c;如果只有一个线程去处理&#xff0c;就会导致窗口卡顿&#xff0c;无法处理用户的相关操作。这种情况下就需要使用多线程&#xff0c;其中一个线程处理窗口事…

编码格式大全解释以及相关编码特性

目录 说明: 1. Base64 Base64编码的字符集通常包括&#xff1a; Base64的工作原理&#xff1a; Base64编码在安全渗透中的应用场景 常见的Base64编码绕过场景 如何防范Base64绕过攻击 2. URL编码&#xff08;Percent Encoding&#xff09; URL编码与安全渗透的关系 示…

软件工程-模块化设计

分解&#xff08;decomposition&#xff09; C&#xff08;P1P2&#xff09;> C&#xff08;P1&#xff09;C&#xff08;P2&#xff09; E&#xff08;P1P2&#xff09;> E&#xff08;P1&#xff09;E&#xff08;P2&#xff09; C为问题的复杂程度&#xff0c;E为解…

LabVIEW显微镜成像偏差校准

在高精度显微镜成像中&#xff0c;用户常常需要通过点击图像的不同位置&#xff0c;让电机驱动探针移动到指定点进行观察。然而&#xff0c;在实际操作中&#xff0c;经常会遇到一个问题&#xff1a;当点击位于图像中心附近的点时&#xff0c;探针能够相对准确地定位&#xff1…

【AI实践】deepseek支持升级git

当前Windows 11 WSL的git是2.17&#xff0c;Android Studio提示需要升级到2.19版本 网上找到指导文章 安装git 2.19.2 cd /usr/src wget https://www.kernel.org/pub/software/scm/git/git-2.19.2.tar.gz tar xzf git-2.19.2.tar.gz cd git-2.19.2 make prefix/usr/l…

在Windows 7操作系统,基于llama.cpp本地化部署 deepseek-r1模型的方法 2025-02-08

一、概述 现在已经是大模型时代。 个人认为&#xff0c;deepseek效果惊艳&#xff0c;大模型已进入实用阶段。 有些电脑&#xff0c;由于种种原因&#xff0c;还在用 Windows 7&#xff0c; Windows XP 等操作系统。 为了让这些电脑用上大模型&#xff0c;本教程在 llama.c…

消息中间件:RabbitMQ镜像集群部署配置全流程

目录 1、特点 2、RabbitMQ的消息传递模式 2.1、简单模式&#xff08;Simple Mode&#xff09; 2.2、工作队列模式&#xff08;Work Queue Mode&#xff09; 2.3、发布/订阅模式&#xff08;Publish/Subscribe Mode&#xff09; 2.4、路由模式&#xff08;Routing Mode&am…

【MySQL — 数据库基础】深入解析 MySQL 的联合查询

1. 插入查询结果 语法 insert into table_name1 select* from table_name2 where restrictions ;注意&#xff1a;查询的结果集合&#xff0c;列数 / 类型 / 顺序 要和 insert into 后面的表相匹配&#xff1b;列的名字不要求相同&#xff1b; create table student1(id int , …