前情提要:
《NLP深入学习(一):jieba 工具包介绍》
《NLP深入学习(二):nltk 工具包介绍》
《NLP深入学习(三):TF-IDF 详解以及文本分类/聚类用法》
《NLP深入学习(四):贝叶斯算法详解及分类/拼写检查用法》
隐马尔可夫模型(Hidden Markov Model, HMM)是一种统计学习方法,用于描述含有隐藏状态的随机过程。在 HMM 中,系统的当前状态无法直接观测,但可以通过该状态下生成的可观测序列来推断。它由两部分构成:一个不可见的马尔可夫链(即隐藏状态),和每个隐藏状态生成观测值的概率分布。
基本结构与概念:
隐藏状态(Hidden States): 系统可能处于的一系列状态,通常用 S = S 1 , S 2 , . . . , S N S = S^{1}, S_{2}, ..., S_N S=S1,S2,...,SN 表示,其中 N 为状态的数量。这些状态是不直接可观测的。
观测序列(Observations): 每个隐藏状态生成一个观测值,观测值构成的时间序列是可见的。例如,在拼写检查器中,观测序列可能是字符序列,而在语音识别中,观测序列可能是声学特征序列。
初始概率分布(Initial Probability Distribution): 定义了系统开始时处于各个状态的概率,记作 π = ( π 1 , π 2 , . . . , π N ) \pi = ( \pi_{1}, \pi_{2}, ..., \pi_{N} ) π=(π1,π2,...,πN),满足 ∑ i = 1 N π i = 1 \sum_{i=1}^{N}\pi_i = 1 ∑i=1Nπi=1。
状态转移概率矩阵(Transition Probability Matrix): 表示从一个状态转移到另一个状态的概率,记作 A = [ a i j ] A = [a_{ij}] A=[aij],其中 a i j a_{ij} aij 是从状态 S i S_i Si转移到状态 S j S_j Sj的概率。满足对于所有 i i i, ∑ j = 1 N a i j = 1 \sum_{j=1}^{N}a_{ij} = 1 ∑j=1Naij=1。
发射概率(Emission Probability): 表示在某个隐藏状态下生成特定观测值的概率,通常定义为条件概率分布 b i ( o k ) b_i(o_k) bi(ok),表示在状态 S i S_i Si下产生观测值 o k o_k ok的概率。
三个基本问题:
评估问题(Evaluation Problem): 给定一个 HMM 模型和一个观测序列,计算该观测序列出现的概率。
解码问题(Decoding Problem): 给定一个 HMM 模型和一个观测序列,找出最有可能生成这个观测序列的状态序列,也称为最大似然路径问题,常通过维特比算法(Viterbi Algorithm)解决。
学习问题(Learning Problem): 根据已有的观测序列数据集估计出HMM的参数,包括初始状态概率、状态转移概率以及发射概率。
假设我们有一个由"A"和"B"两个隐藏状态组成的 HMM 模型,用来识别由"X"和"Y"两个字母构成的观测序列。
模型参数:
状态集合(A, B)
观测值(X, Y)
初始状态分布 π:
π = [0.6, 0.4] # 开始时是状态A的概率为0.6,是状态B的概率为0.4
状态转移矩阵 A:
A = [
[0.7, 0.3], # 从状态A转移到状态A的概率为0.7,转移到状态B的概率为0.3
[0.4, 0.6] # 从状态B转移到状态A的概率为0.4,转移到状态B的概率为0.6
]
发射概率 B:
B(A) = [0.8, 0.2] # 在状态A下生成观测值'X'的概率为0.8,生成观测值'Y'的概率为0.2
B(B) = [0.1, 0.9] # 在状态B下生成观测值'X'的概率为0.1,生成观测值'Y'的概率为0.9
观测序列与问题描述:
给定观测序列 O = [“X”, “Y”],我们的目标是计算在给定模型 λ=(π,A,B) 下观测序列出现的概率 P(O|λ)。
前向算法求解过程:
使用前向算法,我们可以按照以下步骤计算 α
序列:
def forward_algorithm(observations, pi, A, B):
num_states = len(pi)
T = len(observations)
alpha = [[0 for _ in range(num_states)] for _ in range(T)]
# 初始化
for s in range(num_states):
alpha[0][s] = pi[s] * B[s][observations[0]]
# 迭代计算
for t in range(1, T):
for s in range(num_states):
alpha[t][s] = sum(alpha[t - 1][prev_s] * A[prev_s][s] * B[s][observations[t]] for prev_s in range(num_states))
# 计算总概率
prob_observation = sum(alpha[T - 1][s] for s in range(num_states))
return alpha, prob_observation
# 实例化模型参数
pi = [0.6, 0.4]
A = [[0.7, 0.3], [0.4, 0.6]]
B = [[0.8, 0.2], [0.1, 0.9]]
# 观测序列
observations = ['X', 'Y']
# 调用函数计算观察序列概率
alpha, prob_observation = forward_algorithm(observations, pi, A, B)
print(f"The probability of the observation sequence is: {prob_observation}")
以上Python代码定义了一个名为forward_algorithm
的函数,它接收观测序列、初始状态分布、状态转移矩阵和发射概率作为输入,并返回前向概率数组以及观测序列的概率。通过调用该函数并传入相应参数,可以计算出给定观测序列的概率。
假设我们有一个天气模型,用 HMM 来表示天气的变化。我们有两种天气状态:晴天(Sunny)和雨天(Rainy)。我们通过观察温度来获取观测序列,观测值为高温(Hot)、中温(Mild)和低温(Cold)。
模型参数:
状态集合: {Sunny, Rainy}
观测值集合: {Hot, Mild, Cold}
初始概率分布:
状态转移概率矩阵:
A = [
[ P(Sunny|Sunny) P(Rainy|Sunny) ]
[ P(Sunny|Rainy) P(Rainy|Rainy) ]
]
A = [0.7, 0.3],
[0.4, 0.6]
其中,A[i, j] 表示在时刻 t 处于状态 i 的情况下,在时刻 t+1 转移到状态 j 的概率。
发射概率矩阵:
B = [
[ P(Hot|Sunny) P(Mild|Sunny) P(Cold|Sunny) ]
[ P(Hot|Rainy) P(Mild|Rainy) P(Cold|Rainy) ]
]
B = [0.2 0.4 0.4],
[0.5 0.4 0.1]
其中,B[i, j] 表示在时刻 t 处于状态 i 的情况下生成观测值 j 的概率。
计算公式:
概率计算问题(Evaluation):
给定观测序列 O 和模型 λ = ( A , B , π ) λ=(A, B, π) λ=(A,B,π),计算在模型 λ 下观测序列 O 出现的概率 P(O|λ)。
计算公式:
P ( O ∣ λ ) = ∑ i P ( O , X t = i ∣ λ ) = ∑ i α t ( i ) P(O|λ) = \sum_{i} P(O, X_t=i|λ) = \sum_{i} \alpha_t(i) P(O∣λ)=∑iP(O,Xt=i∣λ)=∑iαt(i)
其中, α t ( i ) \alpha_t(i) αt(i) 是在时刻 t 处于状态 i 的情况下,观测序列 O 的部分概率。
解码问题(Decoding):
给定观测序列 O 和模型 λ = ( A , B , π ) λ=(A, B, π) λ=(A,B,π),找到使得观测序列的概率最大的状态序列。
计算公式:
arg max X P ( X ∣ O , λ ) = arg max X P ( O , X ∣ λ ) = arg max X ∏ t = 1 T P ( O t , X t ∣ λ ) \arg \max_{X} P(X|O,λ) = \arg \max_{X} P(O, X|λ) = \arg \max_{X} \prod_{t=1}^{T} P(O_t, X_t|λ) argmaxXP(X∣O,λ)=argmaxXP(O,X∣λ)=argmaxX∏t=1TP(Ot,Xt∣λ)
其中,T 表示观测序列的长度。
学习问题(Learning):
给定观测序列 O,调整模型参数 λ=(A, B, π) 使得观测序列的概率最大。
计算公式:
这是一个简单的天气模型的例子,展示了 HMM 模型的基本结构和计算公式。在实际应用中,通常会使用算法来进行计算和解码。
import numpy as np
# 模型参数
states = ['Sunny', 'Rainy']
observations = ['Hot', 'Mild', 'Cold']
initial_prob = np.array([0.8, 0.2])
transition_prob = np.array([
[0.7, 0.3],
[0.4, 0.6]
])
emission_prob = np.array([
[0.2, 0.4, 0.4],
[0.5, 0.4, 0.1]
])
# 观测序列
observations_sequence = ['Hot', 'Mild', 'Cold']
# 概率计算问题(Evaluation)
def forward_algorithm(observations_sequence):
T = len(observations_sequence)
alpha = np.zeros((T, len(states)))
# 初始化时刻 t=1 的 alpha
alpha[0] = initial_prob * emission_prob[:, observations.index(observations_sequence[0])]
# 递推计算 alpha
for t in range(1, T):
for j in range(len(states)):
alpha[t, j] = np.sum(alpha[t-1] * transition_prob[:, j] * emission_prob[j, observations.index(observations_sequence[t])])
# 返回观测序列的概率
return np.sum(alpha[T-1])
# 解码问题(Decoding)
def viterbi_algorithm(observations_sequence):
T = len(observations_sequence)
delta = np.zeros((T, len(states)))
psi = np.zeros((T, len(states)), dtype=int)
# 初始化时刻 t=1 的 delta
delta[0] = initial_prob * emission_prob[:, observations.index(observations_sequence[0])]
# 递推计算 delta 和 psi
for t in range(1, T):
for j in range(len(states)):
delta[t, j] = np.max(delta[t-1] * transition_prob[:, j]) * emission_prob[j, observations.index(observations_sequence[t])]
psi[t, j] = np.argmax(delta[t-1] * transition_prob[:, j])
# 回溯得到最优路径
best_path = np.zeros(T, dtype=int)
best_path[T-1] = np.argmax(delta[T-1])
for t in range(T-2, -1, -1):
best_path[t] = psi[t+1, best_path[t+1]]
return best_path
# 执行概率计算问题
probability = forward_algorithm(observations_sequence)
print(f"Probability of observing {observations_sequence}: {probability:.4f}")
# 执行解码问题
best_path = viterbi_algorithm(observations_sequence)
print("Most likely states:")
for t, state in enumerate(best_path):
print(f"Time {t+1}: {states[state]}")
请注意,这是一个简单的演示,实际应用中可能需要更复杂的模型和算法。上述代码中的 forward_algorithm
函数用于概率计算问题,而 viterbi_algorithm
函数用于解码问题。
《NLP深入学习(一):jieba 工具包介绍》
《NLP深入学习(二):nltk 工具包介绍》
《NLP深入学习(三):TF-IDF 详解以及文本分类/聚类用法》
《NLP深入学习(四):贝叶斯算法详解及分类/拼写检查用法》
欢迎关注本人,我是喜欢搞事的程序猿; 一起进步,一起学习;
也欢迎关注我的wx公众号:一个比特定乾坤