HMM模型在很多领域 都是很有用的
比如语音识别 nlp中的分词 命名实体识别 词性标注 都需要用到HMM模型,并且是用到HMM模型中的预测算法,维特比算法。
这里我不详细讲维特比算法,主要是讲一下维特比算法的简单实现
# !/usr/bin/env Python3
# -*- coding: utf-8 -*-
# @Author : Meng Li
# @FILE : hmm_self.py
# @Time : 2022/4/15 16:39
# @Software : PyCharm
# 自己实现的HMM训练的模型
import time
import numpy as np
A = np.array([[0.5, 0.2, 0.3],
[0.3, 0.5, 0.2],
[0.2, 0.3, 0.5]])
B = np.array([[0.5, 0.5],
[0.4, 0.6],
[0.7, 0.3]])
PI = np.array([0.2, 0.4, 0.4])
def viterbi(obs):
state = PI * B[:, obs[0]] # 求得第一个观测值对应的三个状态并作为初始状态
sequence_indices = np.zeros((len(obs), len(A)), dtype=np.int32) # 创建一个矩阵 用来保存维特比算法推导过程中的最优节点
for t in range(1, len(obs)):
state = state.reshape(-1, 1)
transition_score = (state * A).max(axis=0) # 求解状态转移后的矩阵的每一行的最大值 每一行最大值表明 每一个状态最大可能性转移到的状态
print((state * A).argmax(axis=0))
sequence_indices[t] = (state * A).argmax(axis=0)
state = transition_score * B[:, obs[t]]
final_sequence = [state.argmax()]
for t in range(len(obs) - 1, 0, -1):
state_index = sequence_indices[t][final_sequence[-1]]
final_sequence.append(state_index)
print("final_sequence: {}".format(final_sequence[::-1]))
return final_sequence[::-1]
if __name__ == '__main__':
obs = [0, 1, 0] # red white red
viterbi(obs)
如代码所示,A矩阵表示的是状态转移矩阵,矩阵B表示的是观测矩阵
状态转移矩阵A 的大小是3×3 , 表明有三个状态,并且三个状态之间存在转移关系,矩阵就是存储的表示这种转移关系的概率值。这里的状态值 在nlp领域里面可表现为BMES性质的标签值,也可表现为词性的标签值如 v/动词 n/名词 adj/形容词 adv/副词等等
观测矩阵B 表示的是 每一个状态 表现为每一个观测值的概率值,这里B矩阵的大小是3×2 , 表示总共有三个状态 并且标下为两种观测值。这里的观测值在nlp中可表现为分词(命名实体识别)或者单个字(分词)
首先看veterbi函数
state = PI * B[:, obs[0]] # 求得第一个观测值对应的三个状态并作为初始状态
这行代码主要表示的是 初始化状态向量 也就是 Pi 值
sequence_indices = np.zeros((len(obs), len(A)), dtype=np.int32) # 创建一个矩阵 用来保存维特比算法推导过程中的最优节点
这行代码主要是初始化一个矩阵,其大小为 [len(obs) ,len(A)] len(obs)为句子长度,也可以理解为状态的转换次数,len(A) 表示的是状态的数量,在代码里面初始化的是3
state = state.reshape(-1, 1)
这里把行向量 转换为一个列向量 为了和后面的矩阵进行相乘
state : [0.2, 0.3 , 0.5 ] --> [0.2,0.3.0.5].T 这样的话 列向量的每一行 就表示当前step 迭代到当前状态的概率值
sequence_indices[t] = (state * A).argmax(axis=0)
这一行 就是获取当前状态 以最大概率值转移到的下一个状态
并将所有的前一刻的状态 最有可能转移到的下一个状态的索引 放在矩阵中
final_sequence = [state.argmax()]
这里是获取最后一的 状态概率最大值
for t in range(len(obs) - 1, 0, -1):
state_index = sequence_indices[t][final_sequence[-1]]
final_sequence.append(state_index)
这里是回溯,从最后一步的最大值 开始往回找该最大值经过的节点,并将节点保存在列表中进行返回