咱们可以把隐马尔可夫模型比作一个有神秘预言能力的算命先生。这位先生虽然看不见事情的全貌,但他可以通过一些显而易见的迹象来推测事情背后可能发生的故事。这个模型在我们的日常生活中其实随处可见,比如说,警探通过现场留下的脚印猜测罪犯的逃跑路线,或者医生通过病人的症状推测病因。
隐马尔可夫模型里面,有两个层次的序列。第一个层次是我们看不见的,称为“隐序列”,它代表了事物内部可能发生的状态变化;第二个层次是我们能看见的,称为“观测序列”,它代表了隐序列状态下可能产生的一系列可观测的结果。这两个序列之间有一种神秘的联系,通过观察可见的迹象,我们可以尝试去理解和推测那些我们看不见的事物状态。
假设你是一位医生,你的病人因为一些症状来找你看病。这些症状可能包括发烧、咳嗽、头痛等等。不过,仅凭这些症状,你并不能直接看到病人体内的情况,也就是说,病因是隐藏的,你无法直接观察到。但你可以根据这些症状来推测病人可能患了哪种疾病。
在这里,病人的症状就是观测序列,因为它们是你可以直接观察到的。而病人的实际病因,比如是感冒、肺炎还是其他疾病,则构成了隐序列,也就是隐藏状态,因为你需要通过一些方法来推断它们。隐马尔可夫模型就像是一本医生的诊断手册,告诉你在不同的症状组合下,最可能的病因是什么。
举个具体的例子,如果一个病人既有发烧又有持续性干咳,你可能会根据经验或者诊断手册(也就是我们的隐马尔可夫模型)推测病人患了流感。如果病人有发烧、咳嗽并伴有胸痛,你可能会考虑是不是肺炎。这个模型通过计算不同症状出现的概率,以及这些症状和特定病因之间的关联概率,来帮助医生做出最有可能的诊断。
通过这个模型,医生可以更系统地分析症状和病因之间的关系,而不仅仅依靠直觉。随着医生积累越来越多的病例,这个模型也会变得越来越精确,就像是医生的经验在不断丰富。
所以,简单来说,隐马尔可夫模型就是一种工具,帮助我们通过一些可见的线索来推测那些隐藏的信息。在医学领域,这就像是医生利用症状来判断病因,而在其他领域,这个模型也能以类似的方式发挥作用。
隐马尔可夫模型(Hidden Markov Model,HMM)是一种统计模型,用来描述一个含有隐含未知参数的马尔可夫过程。在隐马尔可夫模型中,系统被假设为一个马尔可夫过程,拥有一系列不可观测(隐藏)的状态,而每一个状态在某个时间点会根据一定的概率分布生成一个观测。这些观测序列可以是符号、连续的量或是其他的形式,取决于具体应用场景。
隐马尔可夫模型(HMM)的主要思想是系统包括观测状态和隐藏状态两部分,其过程是由不可见的隐藏状态驱动的。我们无法直接观测到隐藏状态,但是可以通过观测变量去推断隐藏状态。
传统的马尔可夫模型是基于一个简单的随机过程,模型中的状态序列是直接可见的。也就是说,在这种模型里,观察者可以直接观察到每一时刻系统所处的状态,而预测的任务就是基于当前状态来预测下一个状态。这些状态之间的转换遵循马尔可夫性质,即下一个状态的出现只依赖于当前状态,而与之前的状态无关。
相比之下,隐马尔可夫模型(HMM)则更加复杂,因为它涉及到两个随机过程。首先,它有一系列不可直接观察到的隐藏状态,这些状态之间的转换仍然遵循马尔可夫性质。其次,每个隐藏状态会以某种概率产生可观察到的输出,这些输出构成了观察序列。也就是说,观察者看到的是输出结果,而这些输出结果是由隐藏状态通过某种概率分布生成的。在隐马尔可夫模型中,我们的目标是基于观察到的输出序列来推断隐藏状态的序列,以及状态转移和输出发生的概率模型。
在明确隐马尔可夫模型的定义以后,我们得到隐马尔可夫模型的五项基本要素,可以概括为五元组
下面我们逐一详细解释这些元素:
:隐藏状态集合。这是模型中所有可能存在的不可见状态的集合。这些状态是系统的内在属性,我们无法直接观察到它们,但它们决定了系统的行为。例如,在语言处理中的词性标注问题里,词语的词性如名词、动词、形容词等就是隐藏状态。
:观察状态集合。与隐藏状态不同,观察状态是我们能够直接观测到的。在上述的词性标注问题中,观察状态就是实际的词语序列。
:状态转移概率矩阵。这个矩阵包含了从一个隐藏状态转移到另一个隐藏状态的概率。如果我们用 表示从隐藏状态 转移到隐藏状态 的概率,那么矩阵 中的元素就表示如下:
:发射概率矩阵(也称为观测概率矩阵)。这个矩阵描述了每个隐藏状态生成观察状态的概率。如果我们用 表示隐藏状态 生成观察状态 的概率,那么矩阵 中的元素就表示如下:
:初始概率分布。这是一个向量,包含了模型在最开始时处于每个隐藏状态的概率。如果我们用 表示开始时位于隐藏状态 的概率,那么向量 中的元素就表示如下:
这五个元素共同确定了一个隐马尔可夫模型的全部行为。在实际的应用中,我们通常需要通过观测到的数据来估计这五个参数,这个过程称为模型的训练或学习。一旦我们有了这些参数,就可以使用模型来预测未知的隐藏状态,解码观测序列来找出最可能的隐藏状态序列,或者生成可能的观测序列等。
隐马尔可夫模型在预测问题上的应用的主要任务是:在给定模型参数和一串已知的观测数据的条件下,计算模型在序列的最后一个时间点上,可能处于各个不同隐藏状态的概率分布。
要解决这个问题,关键在于理解并使用所谓的“前向概率”。前向概率是HMM中的一个概念,用于计算在模型某个时间点之前,某个特定的观测序列出现的概率。换句话说,它帮助我们估算在已知隐马尔可夫模型和观测序列的情况下,这串观测数据在当前时间点之前发生的可能性。
数学上,我们用 来表示前向概率,它的具体含义是:在时间点 ,模型处于隐藏状态 ,并且已经观测到序列 的概率。而它的计算方法可以通过下面的递推公式得到:
这个公式里的 表示在时间点 模型处于隐藏状态 的前向概率。 是状态转移概率,即从隐藏状态 转移到隐藏状态 的概率。 则是发射概率,表示隐藏状态 生成观测值 的概率。
有了这些定义和计算方法,我们就可以进一步计算整个观测序列的概率 ,这里的 代表模型的参数集合。这个概率可以通过将时间点 所有隐藏状态的前向概率加起来得到:
简单来说,就是把在最后一个时间点上,所有可能的隐藏状态的前向概率相加,得到的就是整个观测序列发生的概率。通过这个过程,我们就能够预测在序列的最后时刻,不同隐含状态的概率分布,从而为进一步的分析或决策提供依据。
平滑问题就是如何在已知模型参数和观测序列的条件下,计算中间某个时间点的隐状态概率分布。为了解决这个问题,我们需要用到后向概率和前向概率。
后向概率是指在特定的时间点,向后看,基于这个时间点之后的一系列观测结果,推测之前某个隐藏状态出现的概率。
数学上,后向概率 表示在时间点 ,基于该时间点之后所有观测结果,计算当前处于隐状态 的概率。用公式表示,后向概率的定义是:
这里, 是状态 转移到状态 的转移概率, 是状态 生成观测 的概率, 是下一个时间点 处于状态 的后向概率。
结合前向概率和后向概率,我们可以得到给定观测序列时,在任意时间点 处于状态 的概率:
最后,我们通过对所有可能的状态求和,得到时间点 在给定观测序列下的隐状态概率分布:
这种结合前向概率和后向概率的方法被称作前向-后向算法,能够高效计算在观测序列给定的条件下,任何时间点的隐状态概率分布,从而解决隐马尔可夫模型中的平滑问题。
解码问题是指确定最有可能产生一系列特定观察结果的隐藏状态序列。
为了解决解码问题,我们经常采用维特比算法(Viterbi Algorithm)。维特比算法是一种基于动态规划思想的算法,它可以有效地找出概率最大的隐藏状态序列,也就是我们所说的最优路径。
具体来说,我们定义一个变量 ,它表示在时间点 ,隐藏状态为 ,并且考虑了从起始到当前时间点 的所有观测结果 的条件下,所有可能路径中概率最大路径的概率值。我们的目标是寻找最大的 值,因为这代表了整个观测序列的最有可能的隐藏状态序列。
维特比算法的核心是其递推公式:
这里, 表示前一时间点 的隐藏状态为 的所有路径中最大概率的值, 表示从状态 转移到状态 的转移概率, 表示在隐藏状态 下观测到 的概率。
在使用维特比算法时,我们首先要做的是初始化 值,接着利用上述的递推公式进行逐步计算,最后通过回溯的方式找到整个序列的最优路径。这样一来,我们就能够确定出对于给定的观测序列,最可能的隐藏状态序列,进而解决了隐马尔可夫模型的解码问题。
1import numpy as np
2
3# 初始状态分布
4start_probability = np.array([0.6, 0.4])
5
6# 状态转移概率
7transition_probability = np.array([[0.7, 0.3],
8 [0.4, 0.6]])
9
10# 发射概率(观察概率)
11emission_probability = np.array([[0.1, 0.4, 0.5],
12 [0.6, 0.3, 0.1]])
13
14# 观察序列
15obs_sequence = [0, 1, 2]
16
17def forward(obs_sequence, start_probability, transition_probability, emission_probability):
18 alpha = np.zeros((len(obs_sequence), len(start_probability)))
19
20 # 初始化
21 alpha[0, :] = start_probability * emission_probability[:, obs_sequence[0]]
22
23 # 递归计算
24 for t in range(1, len(obs_sequence)):
25 for j in range(len(start_probability)):
26 # 累加所有可能的状态路径
27 alpha[t, j] = alpha[t - 1].dot(transition_probability[:, j]) * emission_probability[j, obs_sequence[t]]
28
29 # 终止
30 return alpha, np.sum(alpha[-1, :])
31
32alpha, prob = forward(obs_sequence, start_probability, transition_probability, emission_probability)
33print('Alpha:\n', alpha)
34print('Probability of the observation sequence:', prob)
1import numpy as np
2
3# 初始状态分布
4start_probability = np.array([0.6, 0.4])
5
6# 状态转移概率
7transition_probability = np.array([[0.7, 0.3],
8 [0.4, 0.6]])
9
10# 发射概率(观察概率)
11emission_probability = np.array([[0.1, 0.4, 0.5],
12 [0.6, 0.3, 0.1]])
13
14# 观察序列
15obs_sequence = [0, 1, 2]
16
17def forward(obs_sequence, start_probability, transition_probability, emission_probability):
18 alpha = np.zeros((len(obs_sequence), len(start_probability)))
19
20 # 初始化
21 alpha[0, :] = start_probability * emission_probability[:, obs_sequence[0]]
22
23 # 递归计算
24 for t in range(1, len(obs_sequence)):
25 for j in range(len(start_probability)):
26 # 累加所有可能的状态路径
27 alpha[t, j] = alpha[t - 1].dot(transition_probability[:, j]) * emission_probability[j, obs_sequence[t]]
28
29 # 终止
30 return alpha, np.sum(alpha[-1, :])
31
32alpha, prob = forward(obs_sequence, start_probability, transition_probability, emission_probability)
33print('Alpha:\n', alpha)
34print('Probability of the observation sequence:', prob)
这段代码实现了一个前向算法,用于计算给定观察序列的概率,并生成状态序列的概率矩阵。
start_probability
表示初始状态分布,表示系统在初始时刻各个状态的概率。transition_probability
表示状态转移概率,表示系统在经过时间步时,从一个状态转移到另一个状态的概率。emission_probability
表示发射概率或观察概率,表示系统在某个状态下观察到某个观测值的概率。obs_sequence
表示观察序列,即观察到的连续观测值序列。forward
函数利用前向算法,基于给定的观察序列、初始状态分布、状态转移概率和发射概率,计算观察序列的概率矩阵alpha
。alpha
矩阵保存了每个时间步和每个状态的联合概率,即观察到某个观测序列且处于某个状态的概率。alpha
矩阵和观察序列的概率prob
。输出结果:
1Alpha:
2 [[0.06 0.24 ]
3 [0.0552 0.0486 ]
4 [0.02904 0.004572]]
5Probability of the observation sequence: 0.033612
1Alpha:
2 [[0.06 0.24 ]
3 [0.0552 0.0486 ]
4 [0.02904 0.004572]]
5Probability of the observation sequence: 0.033612
1import numpy as np
2
3# 初始状态
4start_probability = np.array([0.6, 0.4])
5
6# 状态转移概率
7transition_probability = np.array([[0.7, 0.3],
8 [0.4, 0.6]])
9
10# 发射概率
11emission_probability = np.array([[0.1, 0.4, 0.5],
12 [0.6, 0.3, 0.1]])
13
14# 观察序列
15obs_sequence = [0, 1, 2]
16
17def forward_backward(obs_sequence, start_probability, transition_probability, emission_probability):
18 # 前向算法
19 def forward(obs_sequence, start_probability, transition_probability, emission_probability):
20 alpha = np.zeros((len(obs_sequence), len(start_probability)))
21
22 # 初始化
23 alpha[0, :] = start_probability * emission_probability[:, obs_sequence[0]]
24
25 # 递归计算
26 for t in range(1, len(obs_sequence)):
27 alpha[t,:] = alpha[t - 1].dot(transition_probability) * emission_probability[:, obs_sequence[t]]
28
29 return alpha
30
31 # 后向算法
32 def backward(obs_sequence, transition_probability, emission_probability):
33 beta = np.zeros((len(obs_sequence), len(start_probability)))
34
35 # 初始化
36 beta[-1, :] = 1
37
38 # 递归计算
39 for t in range(len(obs_sequence) - 2, -1, -1):
40 beta[t,:] = (beta[t + 1] * emission_probability[:, obs_sequence[t+1]]).dot(transition_probability.T)
41
42 return beta
43
44 # 前向后向算法
45 alpha = forward(obs_sequence, start_probability, transition_probability, emission_probability)
46 beta = backward(obs_sequence, transition_probability, emission_probability)
47
48 # 在最后时刻,各个隐含状态的概率分布
49 prob_distribution = alpha[-1, :] * beta[-1, :] / np.sum(alpha[-1, :] * beta[-1, :])
50
51 return prob_distribution
52
53prob_distribution = forward_backward(obs_sequence, start_probability, transition_probability, emission_probability)
54print('在最后时刻,各个隐含状态的概率分布:\n', prob_distribution)
1import numpy as np
2
3# 初始状态
4start_probability = np.array([0.6, 0.4])
5
6# 状态转移概率
7transition_probability = np.array([[0.7, 0.3],
8 [0.4, 0.6]])
9
10# 发射概率
11emission_probability = np.array([[0.1, 0.4, 0.5],
12 [0.6, 0.3, 0.1]])
13
14# 观察序列
15obs_sequence = [0, 1, 2]
16
17def forward_backward(obs_sequence, start_probability, transition_probability, emission_probability):
18 # 前向算法
19 def forward(obs_sequence, start_probability, transition_probability, emission_probability):
20 alpha = np.zeros((len(obs_sequence), len(start_probability)))
21
22 # 初始化
23 alpha[0, :] = start_probability * emission_probability[:, obs_sequence[0]]
24
25 # 递归计算
26 for t in range(1, len(obs_sequence)):
27 alpha[t,:] = alpha[t - 1].dot(transition_probability) * emission_probability[:, obs_sequence[t]]
28
29 return alpha
30
31 # 后向算法
32 def backward(obs_sequence, transition_probability, emission_probability):
33 beta = np.zeros((len(obs_sequence), len(start_probability)))
34
35 # 初始化
36 beta[-1, :] = 1
37
38 # 递归计算
39 for t in range(len(obs_sequence) - 2, -1, -1):
40 beta[t,:] = (beta[t + 1] * emission_probability[:, obs_sequence[t+1]]).dot(transition_probability.T)
41
42 return beta
43
44 # 前向后向算法
45 alpha = forward(obs_sequence, start_probability, transition_probability, emission_probability)
46 beta = backward(obs_sequence, transition_probability, emission_probability)
47
48 # 在最后时刻,各个隐含状态的概率分布
49 prob_distribution = alpha[-1, :] * beta[-1, :] / np.sum(alpha[-1, :] * beta[-1, :])
50
51 return prob_distribution
52
53prob_distribution = forward_backward(obs_sequence, start_probability, transition_probability, emission_probability)
54print('在最后时刻,各个隐含状态的概率分布:\n', prob_distribution)
这段代码实现了前向-后向算法,用于计算在给定观察序列的情况下,系统在最后时刻各个隐含状态的概率分布。
start_probability
表示初始状态的概率分布,表示系统在初始时刻各个状态的概率。transition_probability
表示状态转移概率,表示系统从一个状态转移到另一个状态的概率。emission_probability
表示发射概率,表示系统在某个状态下观察到某个观测值的概率。obs_sequence
表示观察序列,即观察到的连续观测值序列。forward_backward
函数内部包含了前向算法和后向算法两个函数。forward
函数利用前向算法,基于给定的观察序列、初始状态分布、状态转移概率和发射概率,计算观察序列的前向概率矩阵alpha
。backward
函数利用后向算法,基于给定的观察序列、状态转移概率和发射概率,计算观察序列的后向概率矩阵beta
。prob_distribution
表示在最后时刻各个隐含状态的概率分布,通过对alpha
矩阵和beta
矩阵进行归一化得到。prob_distribution
。总结而言,这段代码实现了前向-后向算法,用于计算在给定观察序列的情况下,系统在最后时刻各个隐含状态的概率分布。
输出结果:
1在最后时刻,各个隐含状态的概率分布:
2 [0.86397715 0.13602285]
1在最后时刻,各个隐含状态的概率分布:
2 [0.86397715 0.13602285]
请注意,对于这种实现方式,假设倒数第二个观察状态之后的所有状态转移均是等概率的(因此
被初始化为1)。对于实际问题,可能需要根据具体的应用背景去修正这个假设。
1import numpy as np
2
3# 初始状态
4start_probability = np.array([0.6, 0.4])
5
6# 状态转移概率
7transition_probability = np.array([[0.7, 0.3],
8 [0.4, 0.6]])
9
10# 发射概率
11emission_probability = np.array([[0.1, 0.4, 0.5],
12 [0.6, 0.3, 0.1]])
13
14# 观察序列
15obs_sequence = [0, 1, 2]
16
17def viterbi(obs_sequence, start_probability, transition_probability, emission_probability):
18 num_states = len(start_probability)
19 num_obs = len(obs_sequence)
20
21 # DP matrix
22 dp = np.zeros((num_states, num_obs))
23 # 存储路径信息
24 path = np.zeros((num_states, num_obs), dtype=int)
25
26 # 初始化
27 dp[:, 0] = start_probability * emission_probability[:, obs_sequence[0]]
28
29 # 动态规划
30 for t in range(1, num_obs):
31 for s in range(num_states):
32 for pre_s in range(num_states):
33 score = dp[pre_s, t-1] * transition_probability[pre_s, s] * emission_probability[s, obs_sequence[t]]
34 if score > dp[s, t]:
35 dp[s, t] = score
36 path[s, t] = pre_s
37
38 # 回溯最优路径
39 best_path = []
40 best_state = np.argmax(dp[:, -1])
41 for t in range(num_obs-1, -1, -1):
42 best_path.append(best_state)
43 best_state = path[best_state, t]
44
45 best_path.reverse()
46
47 return best_path
48
49best_path = viterbi(obs_sequence, start_probability, transition_probability, emission_probability)
50print('最可能的隐藏状态序列:', best_path)
1import numpy as np
2
3# 初始状态
4start_probability = np.array([0.6, 0.4])
5
6# 状态转移概率
7transition_probability = np.array([[0.7, 0.3],
8 [0.4, 0.6]])
9
10# 发射概率
11emission_probability = np.array([[0.1, 0.4, 0.5],
12 [0.6, 0.3, 0.1]])
13
14# 观察序列
15obs_sequence = [0, 1, 2]
16
17def viterbi(obs_sequence, start_probability, transition_probability, emission_probability):
18 num_states = len(start_probability)
19 num_obs = len(obs_sequence)
20
21 # DP matrix
22 dp = np.zeros((num_states, num_obs))
23 # 存储路径信息
24 path = np.zeros((num_states, num_obs), dtype=int)
25
26 # 初始化
27 dp[:, 0] = start_probability * emission_probability[:, obs_sequence[0]]
28
29 # 动态规划
30 for t in range(1, num_obs):
31 for s in range(num_states):
32 for pre_s in range(num_states):
33 score = dp[pre_s, t-1] * transition_probability[pre_s, s] * emission_probability[s, obs_sequence[t]]
34 if score > dp[s, t]:
35 dp[s, t] = score
36 path[s, t] = pre_s
37
38 # 回溯最优路径
39 best_path = []
40 best_state = np.argmax(dp[:, -1])
41 for t in range(num_obs-1, -1, -1):
42 best_path.append(best_state)
43 best_state = path[best_state, t]
44
45 best_path.reverse()
46
47 return best_path
48
49best_path = viterbi(obs_sequence, start_probability, transition_probability, emission_probability)
50print('最可能的隐藏状态序列:', best_path)
这段代码实现了维特比算法,用于找到在给定观察序列的情况下,最可能的隐藏状态序列。
start_probability
表示初始状态的概率分布,表示系统在初始时刻各个状态的概率。transition_probability
表示状态转移概率,表示系统从一个状态转移到另一个状态的概率。emission_probability
表示发射概率,表示系统在某个状态下观察到某个观测值的概率。obs_sequence
表示观察序列,即观察到的连续观测值序列。dp
矩阵存储了在每个时间步和每个状态的最大概率。path
矩阵存储了路径信息,用于回溯最优路径。dp
矩阵和path
矩阵,然后通过动态规划的方式递推计算dp
矩阵的值,并更新path
矩阵。path
矩阵,找到在最后时刻的最优状态,并逆序构建出最可能的隐藏状态序列。best_path
。总结而言,这段代码实现了维特比算法,用于找到在给定观察序列的情况下,最可能的隐藏状态序列。
输出结果:
1最可能的隐藏状态序列: [1, 0, 0]
1最可能的隐藏状态序列: [1, 0, 0]
适用于以下几个方面的问题:
优点:
缺点: