“用一个真实生活的例子和Python代码解释隐马尔可夫模型”

使用一个真实生活例子和Python代码解析隐马尔可夫模型

作者提供的图片

隐马尔可夫模型(Hidden Markov Models)是一种概率模型,用于解决从每个人每周至少思考一次的问题,如明天天气会如何?[1],到难以预测肽结合至人体MHC II级分子的分子生物学问题[2]。

隐马尔可夫模型是马尔可夫链的亲属,但它们的隐藏状态使它们成为确定一系列随机变量的概率时的独特工具。

在本文中,我们将分解隐马尔可夫模型的不同组件,并逐步使用数学和Python代码查看导致您的狗在培训考试中取得的结果的每个情绪状态。我们将使用维特比算法来确定观察到特定序列观测的概率,以及在给定一系列隐藏状态序列的情况下,如何使用前向算法确定观测序列的可能性。

现实世界充满了我们可以看到最终结果,但无法实际观察到生成这些结果的潜在因素的现象。一个例子是预测天气,根据过去的天气观测和不同天气结果的观察概率来确定明天是下雨还是晴天。

虽然受到我们无法观察的因素驱动,但通过隐马尔可夫模型,可以将这些现象建模为概率系统。

带有隐藏状态的马尔可夫模型

隐马尔可夫模型,简称HMM,是一种作为序列标记问题的统计模型。这些问题描述了可观察事件的演变,这些事件本身取决于无法直接观察到的内部因素——它们是隐藏的[3]。

隐马尔可夫模型由两个不同的随机过程组成,这些过程可以定义为一系列随机变量——依赖于随机事件的变量。

有一个不可见过程和一个可观察过程。

不可见过程是一个马尔可夫链,类似于将多个隐藏状态连接在一起,随时间遍历以达到结果。这是一个概率过程,因为马尔可夫链的所有参数以及每个序列的评分实际上都是概率[4]。

隐马尔可夫模型描述了可观察事件的演变,这些事件本身取决于无法直接观察到的内部因素——它们是隐藏的[3]。

就像任何其他马尔可夫链一样,为了知道你下一步要去哪个状态,唯一重要的是你现在在哪里——您当前处于马尔可夫链的哪个状态。过去的状态历史不重要,关键是了解您下一步要去哪里。

这种短期记忆是HMM的关键特征之一,被称为马尔可夫假设,表示达到下一个状态的概率仅取决于当前状态的概率。

马尔可夫假设。 (图片由作者提供)

HMM的另一个关键特征是它还假设每个观测值仅取决于产生它的状态,因此完全独立于链中的任何其他状态[5]。

马尔可夫假设认为到达下一个状态的概率只取决于当前状态的概率。

这些都是关于HMM的背景信息,但是,在实际中它们用于解决什么类别的问题呢?

HMM有助于模拟现象的行为。除了建模和运行模拟,您还可以问关于这些现象的不同类型的问题:

  • 似然度得分,即确定观察到一个序列的概率
  • 解码生成特定观察的最佳状态序列
  • 学习导致观察到给定序列的HMM参数,遍历特定状态集合

我们来实践一下吧!

作为HMM的犬只训练表现

今天你不太担心天气预报,你在意的是你的狗是否即将毕业,毕竟经历了这么多时间、精力和狗零食,你只希望它成功。

在犬只训练课程中,你的四脚朋友被期望执行一些动作或技巧,这样训练师可以观察和评估它们的表现。在结合三次试验的分数后,他们将决定你的狗是否毕业或需要额外的训练。训练师只看到了结果,但实际上有很多无法直接观察到的因素,比如,你的狗是否累了,开心了,根本不喜欢训练师或周围的其他狗。

除非有一种特定的动作可以明确表达它们的感受,否则这些都无法直接观察到。如果它们可以用言语表达自己的感受,那将是很棒的,也许将来可以实现!

有了新鲜的隐藏马尔可夫模型的知识,这看起来就是试图预测你的狗在考试期间感觉如何的绝佳机会。它们可能得到一定的分数是因为感到疲倦,也许它们感到饥饿,或者它们对训练师感到恼火。

你的狗已经学了一段时间了,在那段训练期间收集到的数据,你拥有构建隐藏马尔可夫模型所需的所有组件。

为了构建一个模拟您的狗在训练评估中的表现的HMM,您需要:

  • 隐藏状态
  • 转移矩阵
  • 观测序列
  • 观测似然矩阵
  • 初始概率分布

隐藏状态是影响观测序列的不可见因素。您只需考虑您的狗是否疲劳或开心。

隐藏状态在HMM中的不同变化。 (作者提供的图像)

根据你对你的狗的了解,会影响它在考试中的表现的不可见因素很简单,就是疲倦或开心。

接下来,你需要知道从一个状态转移到另一个状态的概率,这体现在一个转移矩阵中。这个矩阵还必须是行随机矩阵,意味着链中一个状态到任何其他状态的概率,矩阵中的每一行,都必须总和为一。

转移矩阵:表示从一个状态转移到另一个状态的概率。(作者提供的图像)

无论你解决的是什么类型的问题,你总是需要一个观测序列。每个观测都表示马尔可夫链的遍历结果。每个观测都来自于特定的词汇。

词汇表 (作者提供的图像)

在你的狗的考试中,你观察到每次试验后得到的分数,分数可以是Fail(失败)、OK(合格)或Perfect(完美)。这些都是观察词汇的可能术语。

你还需要观察似然矩阵,这是观察值从特定状态生成的概率。

观察似然矩阵。 (作者提供的图像)

最后,还有初始概率分布。这是马尔可夫链在每个特定隐藏状态下开始的概率。

在马尔可夫链中,有些状态永远不会成为起始状态。在这些情况下,它们的初始概率为零。和转移矩阵中的概率一样,所有初始概率的总和必须为一。

初始概率 (作者提供的图像)

初始概率分布、转移矩阵和观察似然值构成了隐马尔可夫模型的参数。这些是你计算的概率,如果你有一系列观察和隐藏状态,并尝试学习哪个具体的HMM可能生成了它们。

将所有这些部分组合在一起,这就是表示你的狗在培训考试上表现的隐马尔可夫模型的样子

隐藏状态及其之间的转移概率。(作者提供的图像)

在考试过程中,你的狗将进行三次试验,只有在这些试验中有两次不失败时才能毕业。

最后,如果你的狗需要更多的训练,你会同样关心它们。你心中循环的最大问题是它们在考试期间的感受。

假设他们以OK(合格) – Fail(失败) – Perfect(完美)的顺序毕业,那么他们会处于什么样的情绪状态序列?他们会一直感到疲倦或饥饿,或者可能同时有这两种感觉?

这类问题正好属于HMM可以应用的解码问题。在这种情况下,你想要弄清楚什么是生成特定观察序列OK – Fail – Perfect的最佳状态序列。

解码生成给定观察序列的状态序列的问题使用了Viterbi算法。然而,值得稍作停顿,瞥一眼如何使用前向算法计算给定观察序列的概率(概率任务)。这将为更好地理解Viterbi算法的工作原理奠定基础。

前向算法

如果你将这个问题建模成一个常规的马尔可夫链,并想计算观察到序列OK、Fail、Perfect的可能性,你会通过落在生成所需结果的每个具体状态中来遍历该链。在每个步骤中,你将考虑观察到当前结果的条件概率,以及从一个状态转移到另一个状态的转移概率,然后将这个概率乘以前一个结果的观察条件概率。

最大的区别在于,常规的马尔可夫链中,所有状态都是众所周知且可观察到的。但在隐马尔可夫模型中不是这样!在隐马尔可夫模型中,你观察到一系列结果,不知道要观察到这个序列需要经过哪个特定的隐藏状态序列。

最大的区别在于,常规的马尔可夫链中,所有状态都是众所周知且可观察到的。但在隐马尔可夫模型中不是这样!

此时你可能会想,我可以简单地遍历所有可能的路径,最终找到一个规则来选择等价路径。这种方法的数学定义大致如下

计算观测序列的概率,遍历所有可能的隐藏状态序列。(图片作者)

这确实是一种策略!你将不得不计算观察到序列“OK,Fail,Perfect”的每个隐藏状态组合的概率。

当你的隐藏状态数量和观察结果序列足够小的时候,可以在合理的时间内进行计算。

隐藏马尔可夫模型中可能路径的轮廓(图片作者)

谢天谢地,你刚刚定义的隐藏马尔可夫模型相对简单,有3个观测结果和2个隐藏状态。

对于长度为L的观测序列,在具有M个隐藏状态的HMM中,你有“M的L次方”个可能的状态,对于你的情况,意味着对于序列OK-Fail-Perfect,有2的3次方,即8个可能的路径,涉及到指数级的计算复杂度O(M^L L),在大O符号中有所描述。随着模型复杂性的增加,你需要考虑的路径数量呈指数级增长。

随着模型复杂性的增加,你需要考虑的路径数量呈指数级增长。

这就是前向算法的优势所在。

前向算法可以计算观测序列中新符号的概率,而无需计算构成该序列的所有可能路径的概率[3]。

该算法不是计算构成该序列的所有可能路径的概率,而是定义了前向变量并通过递归计算其值。

如何递归计算前向变量。(图片作者)

它使用递归的方式计算是这个算法比计算所有可能路径的概率更快的关键原因。事实上,它只需进行“L次M平方”次计算就能计算出观测序列x的概率,而不是“M的L次方乘以L”。

在你的情况下,有2个隐藏状态和一个序列的3个观测结果,这是计算概率的次数从O(MˆL L) = 2³x3 = 8×3 = 24次,减少到O(L Mˆ2)=3*2²=3×4 = 12次。

通过动态规划这种编程技术,可以实现计算数量的减少。它使用辅助数据结构来存储中间信息,从而确保不会多次执行相同的计算。

每当算法要计算新的概率时,它会检查是否已经计算过,如果已经计算过,则可以轻松地在中间数据结构中访问该值。否则,计算概率并将值存储起来。

让我们回到你的解码问题,使用维特比算法。

维特比算法

思考伪代码,如果你要强制解码生成特定观测序列的隐藏状态序列,你只需要做以下操作:

  • 生成所有可能的路径排列,以达到所需的观察序列
  • 使用前向算法计算每个可能的隐藏状态序列对应的观察序列的可能性
  • 选择具有最高概率的隐藏状态序列
生成观察序列 OK — Fail — Perfect 的所有可能隐藏状态序列(图片来源:作者)

对于您特定的HMM,有8条可能的路径可以导致 OK — Fail — Perfect 的结果。只需再增加一次观察,您将获得两倍数量的可能隐藏状态序列!与前向算法类似,您很容易得到指数级复杂的算法并达到性能瓶颈。

而维特比算法可以帮助您解决这个问题。

当遍历HMM中的隐藏状态序列时,每一步中的概率 vt(j) 是HMM在观察后并通过导致 j 的最可靠状态遍历时处于隐藏状态 j 的概率。

时间步骤 t 上到达隐藏状态 j 的维特比路径(图片来源:作者)

解码生成特定观察序列的隐藏状态序列的关键在于”最可靠路径”的概念。也称为”维特比路径”,最可靠路径是从可以导致任何给定隐藏状态的所有路径中具有最高可能性的路径。

解码生成特定观察序列的隐藏状态序列的关键是使用维特比路径。即导致任何给定隐藏状态的最可靠路径。

您可以将前向算法和维特比算法进行类比。前向算法通过总结所有可能性来计算到达某个状态的可能性,同时考虑到所有导向该状态的路径;而维特比算法并不想要探索所有可能性,而是专注于导致任何给定状态的最可靠路径。

回到解码导致考试成绩为 OK — Fail — Perfect 的隐藏状态序列的任务上,手动运行维特比算法的步骤如下所示:

维特比路径和解码的序列(图片来源:作者)

维特比算法的另一个独特特点是,它必须有一种方法来跟踪导致任何给定隐藏状态的所有路径,以便比较它们的概率。为此,它使用了辅助数据结构,典型的动态规划算法,来追踪到每个隐藏状态的”反馈指针”。通过这种方式,它可以轻松访问过去遍历的任何维特比路径的概率。

“反馈指针是找出导致观察序列的最可靠路径的关键。

在您的狗狗考试的示例中,当计算维特比路径v3(Happy)和v3(Tired)时,您选择具有最高概率的路径,并开始往回追溯,即回溯到导致您目前所处位置的所有路径。

Python中的维特比算法

手动完成所有这些工作非常耗时且容易出错。如果错漏了一个重要数字,您可能需要重新开始并重新检查所有概率!

好消息是,您可以利用像hmmlearn这样的软件库,只需几行代码,就可以解码导致您的狗狗在测试中以 OK — Fail — Perfect 的顺序顺利毕业的隐藏状态序列。

从hmmlearn中导入hmm
import numpy as np
## 第1部分。使用特定参数生成HMM并模拟考试
print("使用参数设置HMM模型")
# init_params是用于初始化模型进行训练的参数
# s->开始概率
# t->转移概率
# e->发射概率
model = hmm.CategoricalHMM(n_components=2, random_state=425, init_params='ste')
# 初始概率
# 在疲倦状态开始的概率=0
# 在快乐状态开始的概率=1
initial_distribution = np.array([0.1, 0.9])
model.startprob_ = initial_distribution
print("步骤1. 完成 - 定义初始分布")
# 转移概率
#        疲倦    快乐
# 疲倦   0.4    0.6
# 快乐   0.2    0.8
transition_distribution = np.array([[0.4, 0.6], [0.2, 0.8]])
model.transmat_ = transition_distribution
print("步骤2. 完成 - 定义转移矩阵")
# 观察概率
#        失败    正常     完美
# 疲倦   0.3    0.5    0.2
# 快乐   0.1    0.5    0.4
observation_probability_matrix = np.array([[0.3, 0.5, 0.2], [0.1, 0.5, 0.4]])
model.emissionprob_ = observation_probability_matrix
print("步骤3. 完成 - 定义观察概率矩阵")
# 模拟进行10万次试验,即能力测试
trials, simulated_states = model.sample(100000)
# 输出模拟试验的样本
# 0->失败
# 1->正常
# 2->完美
print("\n模拟试验的样本 - 基于模型参数")
print(trials[:10])
## 第2部分 - 解码导致观察序列为正常-失败-完美的隐藏状态序列
# 将数据分成训练集和测试集(50/50比例)
X_train = trials[:trials.shape[0] // 2]
X_test = trials[trials.shape[0] // 2:]
model.fit(X_train)
# 考试有3次试验,你的狗得分为:正常、失败、完美(1,0,2)
exam_observations = [[1, 0, 2]]
predicted_states = model.predict(X=[[1, 0, 2]])
print("预测导致考试得分为正常、失败、完美的隐藏状态转换:\n 0->疲倦,1->快乐")
print(predicted_states)

几秒钟后,你会得到与手动计算结果相匹配的输出,速度更快,错误更少。

上述代码运行后的输出(图片来自作者)

结论

隐藏马尔可夫模型的有趣之处在于这个在20世纪60年代中期创造的统计工具[6]如此强大并适用于现实世界中不同领域的问题,从气象预报到句子中下一个单词的查找。

在本文中,你有机会了解到HMM的不同组成部分,以及它们如何应用于不同类型的任务,并发现了前向算法和维特比算法之间的相似之处。这两个非常相似的算法都使用动态规划来处理涉及的指数计算。

无论是手动计算还是将参数插入TensorFlow代码中,希望您喜欢深入探究HMM世界。

谢谢阅读!

参考资料

  1. D. Khiatani and U. Ghose, “Weather forecasting using Hidden Markov Model,” 2017 International Conference on Computing and Communication Technologies for Smart Nation (IC3TSN), Gurgaon, India, 2017, pp. 220–225, doi: 10.1109/IC3TSN.2017.8284480.
  2. Noguchi H, Kato R, Hanai T, Matsubara Y, Honda H, Brusic V, Kobayashi T. Hidden Markov model-based prediction of antigenic peptides that interact with MHC class II molecules. J Biosci Bioeng. 2002;94(3):264–70. doi: 10.1263/jbb.94.264. PMID: 16233301.
  3. Yoon BJ. Hidden Markov Models and their Applications in Biological Sequence Analysis. Curr Genomics. 2009 Sep;10(6):402–15. doi: 10.2174/138920209789177575. PMID: 20190955; PMCID: PMC2766791.
  4. Eddy, S. What is a hidden Markov model?. Nat Biotechnol 22, 1315–1316 (2004). https://doi.org/10.1038/nbt1004-1315
  5. Jurafsky, Dan and Martin, James H.. Speech and language processing : an introduction to natural language processing, computational linguistics, and speech recognition. Upper Saddle River, N.J.: Pearson Prentice Hall, 2009.
  6. Baum, Leonard E., and Ted Petrie. “Statistical Inference for Probabilistic Functions of Finite State Markov Chains.” The Annals of Mathematical Statistics 37, no. 6 (1966): 1554–63.