NLP深入学习(五):HMM 详解及字母识别/天气预测用法

文章目录

  • 0. 引言
  • 1. 什么是 HMM
  • 2. HMM 的例子
    • 2.1 字母序列识别
    • 2.2 天气预测
  • 3. 参考


0. 引言

前情提要:
《NLP深入学习(一):jieba 工具包介绍》
《NLP深入学习(二):nltk 工具包介绍》
《NLP深入学习(三):TF-IDF 详解以及文本分类/聚类用法》
《NLP深入学习(四):贝叶斯算法详解及分类/拼写检查用法》

1. 什么是 HMM

隐马尔可夫模型(Hidden Markov Model, HMM)是一种统计学习方法,用于描述含有隐藏状态的随机过程。在 HMM 中,系统的当前状态无法直接观测,但可以通过该状态下生成的可观测序列来推断。它由两部分构成:一个不可见的马尔可夫链(即隐藏状态),和每个隐藏状态生成观测值的概率分布。

基本结构与概念:

  1. 隐藏状态(Hidden States): 系统可能处于的一系列状态,通常用 S = S 1 , S 2 , . . . , S N S = S^{1}, S_{2}, ..., S_N S=S1,S2,...,SN 表示,其中 N 为状态的数量。这些状态是不直接可观测的。

  2. 观测序列(Observations): 每个隐藏状态生成一个观测值,观测值构成的时间序列是可见的。例如,在拼写检查器中,观测序列可能是字符序列,而在语音识别中,观测序列可能是声学特征序列。

  3. 初始概率分布(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

  4. 状态转移概率矩阵(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

  5. 发射概率(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的参数,包括初始状态概率、状态转移概率以及发射概率。

2. HMM 的例子

2.1 字母序列识别

假设我们有一个由"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的函数,它接收观测序列、初始状态分布、状态转移矩阵和发射概率作为输入,并返回前向概率数组以及观测序列的概率。通过调用该函数并传入相应参数,可以计算出给定观测序列的概率。

2.2 天气预测

假设我们有一个天气模型,用 HMM 来表示天气的变化。我们有两种天气状态:晴天(Sunny)和雨天(Rainy)。我们通过观察温度来获取观测序列,观测值为高温(Hot)、中温(Mild)和低温(Cold)。

模型参数:

  • 状态集合: {Sunny, Rainy}

  • 观测值集合: {Hot, Mild, Cold}

  • 初始概率分布:

    • P(Sunny) = 0.8
    • P(Rainy) = 0.2
  • 状态转移概率矩阵:

     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 的概率。

计算公式:

  1. 概率计算问题(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 的部分概率。

  2. 解码问题(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(XO,λ)=argmaxXP(O,Xλ)=argmaxXt=1TP(Ot,Xtλ)

    其中,T 表示观测序列的长度。

  3. 学习问题(Learning):

    给定观测序列 O,调整模型参数 λ=(A, B, π) 使得观测序列的概率最大。

    计算公式:

    • 初始概率分布: π i = P ( X 1 = i ∣ O , λ ) \pi_i = P(X_1=i|O,λ) πi=P(X1=iO,λ)
    • 状态转移概率: a i j = ∑ t = 1 T − 1 P ( X t = i , X t + 1 = j ∣ O , λ ) ∑ t = 1 T − 1 P ( X t = i ∣ O , λ ) a_{ij} = \frac{\sum_{t=1}^{T-1} P(X_t=i, X_{t+1}=j|O,λ)}{\sum_{t=1}^{T-1} P(X_t=i|O,λ)} aij=t=1T1P(Xt=iO,λ)t=1T1P(Xt=i,Xt+1=jO,λ)
    • 发射概率: b i j = ∑ t = 1 , O t = j T P ( X t = i ∣ O , λ ) ∑ t = 1 T P ( X t = i ∣ O , λ ) b_{ij} = \frac{\sum_{t=1, O_t=j}^{T} P(X_t=i|O,λ)}{\sum_{t=1}^{T} P(X_t=i|O,λ)} bij=t=1TP(Xt=iO,λ)t=1,Ot=jTP(Xt=iO,λ)

这是一个简单的天气模型的例子,展示了 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 函数用于解码问题。

3. 参考

《NLP深入学习(一):jieba 工具包介绍》
《NLP深入学习(二):nltk 工具包介绍》
《NLP深入学习(三):TF-IDF 详解以及文本分类/聚类用法》
《NLP深入学习(四):贝叶斯算法详解及分类/拼写检查用法》

欢迎关注本人,我是喜欢搞事的程序猿; 一起进步,一起学习;

也欢迎关注我的wx公众号:一个比特定乾坤

你可能感兴趣的:(NLP,自然语言处理,学习,人工智能,nlp)