python实现维特比算法

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)

这里是回溯,从最后一步的最大值 开始往回找该最大值经过的节点,并将节点保存在列表中进行返回 

猜你喜欢

转载自blog.csdn.net/linxizi0622/article/details/127349615