维特比算法(Viterbi)-实例讲解(暴力破解+代码实现)

1、简介

  维特比算法是一个通用的求序列最短路径的动态规划算法,也可以用于很多其他问题,比如:文本挖掘、分词原理。既然是动态规划算法,那么就需要找到合适的局部状态,以及局部状态的递推公式。在HMM中,维特比算法定义了两个局部状态用于递推。

  第一个局部状态是在时刻i隐藏状态为i所有可能的状态转移路径i1,i2.......it中的最大概率

  第二个局部状态由第一个局部状态递推得到。

2、算法详解

  (1)从点S出发,对于第一个状态X1的各个节点,不妨假定有n1个,计算出S到它们的距离d(S,X1i),其中X1i代表任意状态1的节点。因为只有一步,所以这些距离都是S到它们各自的最短距离。

  (2)对于第二个状态X2的所有节点,要计算出从S到它们的最短距离。对于特点的节点X2i,从S到它的路径可以经过状态1的n1中任何一个节点X1i,对应的路径长度就是d(S,X2i) = d(S,X1i) + d(X1i,X2i)。

    由于i有n1种可能性,我们要一 一计算,找出最小值。即:d(S,X2i) min =d(S,X1i) + {d(X1i,X2i)}1→n1这样对于第二个状态的每个节点,需要n1次乘法计算。假定这个状态有n2个节点,把S这些节点的距离都算一遍,就有O(n1·n2)次计算。

  (3)接下来,类似地按照上述方法从第二个状态走到第三个状态,一直走到最后一个状态,就得到了整个网格从头到尾的最短路径。

  (4)假设这个隐含马尔可夫链中节点最多的状态有D个节点,也就是说整个网格的宽度为D,那么任何一步的复杂度不超过O(D2),由于网格长度是N,所以整个维特比算法的复杂度是O(N·D2)。

3、实例展示

  (1)天气只有三类(Sunny,Cloudy,Rainy),海藻湿度有四类{Dry,Dryish, Damp,Soggy },而且海藻湿度和天气有一定的关系。

  (2)隐藏的状态:Sunny, Cloudy, Rainy;

  (3)观察状态序列:{Dry, Damp, Soggy}

  (4)初始状态序列:

  

 (5)状态转移矩阵:

  

 (6)发射矩阵:

  

  假设连续观察3天的海藻湿度为(Dry,Damp,Soggy),求这三天最可能的天气情况。

  1、暴力破解:

  前提:第二天的情况只和第一天的天气有关,第三天的天气只和第二天的天气有关。

  所以第一天湿度为Dry的情况为:

    P(Dry|day1-Sunny)=0.63*0.6

    P(Dry|day1-Cloudy)=0.17*0.25

    P(Dry|day1-Rainy)=0.2*0.05

    P(Dry|day1-Suny)的概率最大,所以第一天最大概率为:Sunny

  第二天湿度为Damp的情况为:

    P(Damp|day2-Sunny)=max{P(Dry|day1-Sunny)*0.5,P(Dry|day1-Cloudy)*0.25,P(Dry|day1-Rainy)*0.25}*0.15

    P(Damp|day2-Cloudy)=max{P(Dry|day1-Sunny)*0.375,P(Dry|day1-Cloudy)*0.125,P(Dry|day1-Rainy)*0.625}*0.25

    P(Damp|day2-Rainy)=max{P(Dry|day1-Sunny)*0.125,P(Dry|day1-Cloudy)*0.625,P(Dry|day1-Rainy)*0.35}*0.35

    P(Damp|day2-Cloud)概率最大,所以第二天最大概率为:Cloud

  第三天湿度为Soggy的情况为:

    P(Soggy|day3-Sunny)=max{P(Damp|day2-Sunny)*0.5,P(Damp|day2-Cloudy)*0.25,P(Damp|day2-Rainy)*0.25}*0.05

    P(Soggy|day3-Cloudy)=max{P(Damp|day2-Sunny)*0.375,P(Damp|day2-Cloudy)*0.125,P(Damp|day2-Rainy)*0.625}*0.25

    P(Soggy|day3-Rainy)=max{P(Damp|day2-Sunny)*0.125,P(Damp|day2-Cloudy)*0.625,P(Damp|day2-Rainy)*0.375}*0.5

    P(Soggy|day3-Rainy)概率最大,所以第三天最大概率为:Rainy

  所以3天的天气最大概率为{Sunny,Cloud,Rainy}

  分析:

      第一天:对应每种天气下湿度为Dry放入最大概率则为可能的天气;

      第二天:对应第一天的各种天气分别转换Sunny,且转换的概率最大的情况下,取天气湿度为Damp的概率,Cloudy和Rainy同理,然后取这三种天气下概率最大的天气

      第三天:对应第二天的各种天气分别转换Sunny(注意这里第二天对应第一天,已经得到最大概率)且转换的概率最大的情况下,取天气湿度为Damp的概率,Cloudy和Rainy同理,然后取这三种天气下概率最大的天气

 

   2、python代码实现:

#-*- coding:utf-8 -*-
import numpy as np
def viterbi(trainsition_probability,emission_probability,pi,obs_seq):

    #转换为矩阵进行计算
    trainsition_probability=np.array(trainsition_probability)
    emission_probability=np.array(emission_probability)
    pi=np.array(pi)

    #定义要返回的矩阵
    F = np.zeros((Row, Col))

    #初始状态
    F[:,0]=pi*np.transpose(emission_probability[:,obs_seq[0]])


    for t in range(1,Col):
        list_max=[]
        for n in range(Row):
            list_x=list(np.array(F[:,t-1])*np.transpose(trainsition_probability[:,n]))

            #获取最大概率
            list_p=[]
            for i in list_x:
                list_p.append(i*10000)
            list_max.append(max(list_p)/10000)

        F[:, t] = np.array(list_max) * np.transpose(emission_probability[:,obs_seq[t]])

    return F


if __name__ == '__main__':
    #隐藏状态
    invisible=['Sunny','Cloud','Rainy']
    #初始状态
    pi=[0.63,0.17,0.20]
    #转移矩阵
    trainsition_probability=[[0.5,0.375,0.125],
                             [0.25,0.125,0.625],
                             [0.25,0.375,0.375]]
    #发射矩阵
    emission_probability=[[0.6,0.2,0.15,0.05],
                          [0.25,0.25,0.25,0.25],
                          [0.05,0.10,0.35,0.5]]
    #最后显示状态
    obs_seq=[0,2,3]

    #最后返回一个Row*Col的矩阵结果
    Row = np.array(trainsition_probability).shape[0]
    Col = len(obs_seq)

    F=viterbi(trainsition_probability, emission_probability, pi, obs_seq)

    print F

 

结果:

[[ 0.378     0.02835        0.00070875]
[ 0.0425    0.0354375    0.00265781]
[ 0.01        0.0165375    0.01107422]]

每列代表Dry,Damp,Soggy的概率,每行代表Sunny,Cloud,Rainy,所以可以看出最大概率的天气为{Sunny,Cloud,Rainy}。

 

转载于:https://www.cnblogs.com/ybf-yyj/p/8542172.html

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个使用PyTorch实现BiLSTM-CRF模的标准demo,并附有逐行注释和通俗易懂的说明: ```python import torch import torch.nn as nn # 定义BiLSTM-CRF模型 class BiLSTM_CRF(nn.Module): def __init__(self, num_tags, vocab_size, embedding_dim, hidden_dim): super(BiLSTM_CRF, self).__init__() self.embedding = nn.Embedding(vocab_size, embedding_dim) self.lstm = nn.LSTM(embedding_dim, hidden_dim // 2, bidirectional=True, batch_first=True) self.hidden2tag = nn.Linear(hidden_dim, num_tags) self.transitions = nn.Parameter(torch.randn(num_tags, num_tags)) self.start_transitions = nn.Parameter(torch.randn(num_tags)) self.end_transitions = nn.Parameter(torch.randn(num_tags)) self.num_tags = num_tags self.hidden_dim = hidden_dim def _forward_alg(self, feats): T = feats.size(1) dp = torch.full((1, self.num_tags), -10000.) dp[0][self.start_tag] = 0. for t in range(T): dp = dp + feats[:, t].unsqueeze(1) + self.transitions dp = torch.logsumexp(dp, dim=2).unsqueeze(2) dp = dp + self.end_transitions return torch.logsumexp(dp, dim=2)[0] def _score_sentence(self, feats, tags): T = feats.size(1) score = torch.zeros(1) tags = torch.cat([torch.tensor([self.start_tag], dtype=torch.long), tags]) for t in range(T): score = score + self.transitions[tags[t+1], tags[t]] + feats[:, t, tags[t+1]] score = score + self.end_transitions[tags[-1]] return score def _viterbi_decode(self, feats): backpointers = [] T = feats.size(1) dp = torch.full((1, self.num_tags), -10000.) dp[0][self.start_tag] = 0. for t in range(T): next_tag_var = dp + self.transitions viterbivars_t, bptrs_t = torch.max(next_tag_var, dim=2) dp = viterbivars_t + feats[:, t] backpointers.append(bptrs_t.tolist()) terminal_var = dp + self.end_transitions path_score, best_tag_id = torch.max(terminal_var, dim=1) best_path = [best_tag_id.item()] for bptrs_t in reversed(backpointers): best_tag_id = bptrs_t[0][best_tag_id.item()] best_path.append(best_tag_id) start = best_path.pop() assert start == self.start_tag best_path.reverse() return path_score, best_path def forward(self, sentence): lstm_feats = self._get_lstm_features(sentence) score, tag_seq = self._viterbi_decode(lstm_feats) return score, tag_seq def _get_lstm_features(self, sentence): embeds = self.embedding(sentence) lstm_out, _ = self.lstm(embeds) lstm_feats = self.hidden2tag(lstm_out) return lstm_feats # 定义隐状态和观测状态 states = ['B', 'I', 'O'] observations = ['PER', 'LOC', 'ORG'] # 定义标签数量、词汇表大小、词向量维度和隐藏状态维度 num_tags = len(states) vocab_size = len(observations) embedding_dim = 100 hidden_dim = 256 # 初始化BiLSTM-CRF模型 model = BiLSTM_CRF(num_tags, vocab_size, embedding_dim, hidden_dim) # 定义输入句子 sentence = torch.tensor([[1, 2, 0, 0, 0]]) # 假设输入句子是[1, 2, 0, 0, 0] # 使用BiLSTM-CRF模型进行解码 score, tag_seq = model(sentence) print(score) print(tag_seq) ``` 模型解释和原理技术说明: 1. BiLSTM-CRF是一种结合了双向长短时记忆网络(BiLSTM)和条件随机场(CRF)的序列标注模型,常用于命名实体识别、词性标注等任务。 2. 在上述代码中,首先导入了PyTorch库中的`nn.Module`模块。 3. 定义了一个BiLSTM_CRF模型类,继承自`nn.Module`。 4. 在BiLSTM_CRF模型类的初始化方法中,定义了模型的各个组件,包括嵌入层(Embedding)、双向LSTM层(LSTM)、线性映射层(Linear)、转移矩阵(transitions)、起始转移概率(start_transitions)和结束转移概率(end_transitions)。 5. `_forward_alg`方法实现了前向算法,用于计算给定观测序列的分数。 6. `_score_sentence`方法用于计算给定观测序列和标签序列的得分。 7. `_viterbi_decode`方法实现维特比算法,用于解码最优路径。 8. 在前向传播方法中,首先获取句子的嵌入表示,并通过双向LSTM层得到每个位置的特征表示。 9. 将LSTM输出结果经过线性映射层得到发射概率。 10. 使用维特比算法进行解码,得到最优路径的分数和标签序列。 11. 初始化BiLSTM_CRF模型实例,并定义隐状态和观测状态的集合、标签数量、词汇表大小、词向量维度和隐藏状态维度。 12. 定义输入句子。 13. 调用BiLSTM_CRF模型的前向传播方法进行解码,得到最优路径的分数和标签序列。 14. 打印最优路径的分数和标签序列。 通过以上代码和解释,一个NLP新手可以了解到: - BiLSTM-CRF是一种结合了双向长短时记忆网络(BiLSTM)和条件随机场(CRF)的序列标注模型,常用于命名实体识别、词性标注等任务。 - 在使用PyTorch实现BiLSTM-CRF模型时,需要定义一个继承自`nn.Module`的自定义模型类,并实现前向传播方法和一些辅助方法。 - 模型类中定义了各个组件,包括嵌入层、双向LSTM层、线性映射层、转移矩阵和起始/结束转移概率。 - 前向传播方法中,首先获取句子的嵌入表示,并通过双向LSTM层得到每个位置的特征表示。 - 然后,将LSTM输出结果经过线性映射层得到发射概率。 - 最后,使用维特比算法进行解码,得到最优路径的分数和标签序列。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值