viterbi算法理解从0到1

版权声明:本文为博主原创文章,转载请注明原文链接 https://blog.csdn.net/sailist/article/details/83064985

viterbi算法是什么

viterbi算法简要的概括一下,是一种最优路径的计算方法,它是向前算法的一种变体,比向前算法的复杂度要低很多,并且最终能够得到最优解。

手动理解

在理解viterbi算法之前,我们先来跟随步骤计算一下下面这幅图从左到右的最大路径
在这里插入图片描述

下面我们计算的步骤,就是viterbi算法的整个过程:
1.计算从s到所有的x的路径长度,因为s到每一个x只有一条路径,因此不需要做选择
在这里插入图片描述
2.计算每个x到每个y的路径,并分别选出每个x到每一个y的最大路径,对于图中,就是x1->y2和x2->y1
在这里插入图片描述

3.重复上述步骤,计算每个y到e的最大路径,如图,即y2->e
在这里插入图片描述

那么,最长路径即为25,并且该条路径为s->x1->y2->e,至此,viterbi算法结束。

为了方便推导,这里求的最大路径,计算是通过路径相加求得,可以换个思路,求最小路径是不是这个道理?如果换成状态,求条件概率,路径长度为状态转移概率,是不是这个道理?

缺点分析

viterbi算法主要有两个主要的限制(摘自《自然语言处理综论》P157):

  • 对于给定的声学输入,该算法不计算具有最大概率的单词序列,而是计算与这一的单词序列近似的。对于给定的输入具有最大概率的状态序列(即音子序列或次音子序列)。但是有时概率最大的音子序列并不对应于概率最大的单词序列。如:在一个语音识别系统中,当词表中的每个单词都有多个发音时,就会出现音子序列与单词序列不对应的情况。因为具有多个发音的单词模型的正确的发音路径的概率的原因,可能计算后会小于不正确的但只有一个发音的单词的概率。
  • 不能用于所有可能的语言模型。

缺点解决:修改算法,每个状态返回前N个值,即缩小了搜索空间,也能更好的适用于其他模型求得最优解。

算法详解

算法推论

理解这个算法,首先需要想明白以下几点:

  • 如果路径最大的路径P经过了某个节点,如y2,那么从该路径(s->x1->y2),一定是从sy2的最长路径。(否则的话,从sy2一定会有一条更长的路径能替代这条路径,路径P就不是路径最长的了)
  • SE的路径必定经过第i时刻的某个状态(以上图为例,则必定经过某个x和某个y),假定第i时刻有k个状态,那么如果记录了从S到第i个状态的所有k个节点的最长路径,最终的最短路径必经过其中的一条。(如经过y时有y1和y2,那么最终的最长路径一定经过从s到某个y的最长路径中的一条,即必定经过s->x1->y2s->x2->y1)这样,在任何时刻,只需要考虑非常有限条最短路径即可。
  • 因此,当我们已经计算出了从s到状态i的最大距离,并且准备进入状态i+1时,我们只需要计算从状态ik个节点到状态i+1的k个节点的距离并选出到状态i+1的每个节点的最大距离即可。

viterbi与隐马尔可夫

很多人都用隐马尔科夫模型来回答viterbi算法,其实viterbi算法只是解决隐马第三个问题(求观察序列的最可能的标注序列)的一种实现方式。这个问题可以用于viterbi算法实现,也可以用其他方式实现(如穷举法);
而viterbi算法可以用于解决隐马第三问题,也可以用于解决其他问题。所以千万不要把viterbi算法和隐马尔科夫模型等价了。
作者:岳席文
链接:https://www.zhihu.com/question/20136144/answer/154753703
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

隐马尔科夫链的三个基本问题

  • 概率计算问题 / 评估问题:即给定模型λ=(A,B,π)和观测序列O,计算在模型λ下观测序列出现的最大概率P(O|λ)
  • 学习问题:即给定观测序列O,估计模型的参数λ, 使得在该参数下观测序列出现的概率最大,即P(O|λ)最大;
  • 解码问题 / 预测问题:给定模型λ=(A,B,π)和观测序列O,计算最有可能产生这个观测序列的隐含序列X, 即使得概率P(X|O,λ)最大的隐含序列X

而viterbi算法就是用来解决解码问题的一种方式

隐马尔科夫链的五元组

HMM是一个五元组(O , Q , O0,A , B):

  • O:{o1,o2,…,ot}是状态集合,也称为观测序列。
  • Q:{q1,q2,…,qv}是一组输出结果,也称为隐序列。
  • Aij = P(qj|qi):转移概率分布
  • Bij = P(oj|qi):发射概率分布
  • O0是初始状态,有些还有终止状态。
更详细的解释
  • 观测序列: 就是表面能看到的,以网上很常见的例子,简陋的条件下检测一个人是否发烧,观测到的序列中每一个状态就有{正常,发冷,头晕}三种状态
  • 隐序列: 就是透过表面现象的本质,以观测序列的例子为准,隐序列的每个状态就有{发烧,未发烧}两个状态
  • 转移概率分布: 隐序列中,从一个隐状态转移到另一个隐序列状态的概率分布
  • 发射概率分布: 在当前的观测状态下,相应的隐状态的概率,比如在正常条件下,发烧的概率是0.2,未发烧的概率是0.8,这些都是发射概率分布
  • 初始状态: 初始状态

因此viterbi算法解隐马尔可夫模型的第三个问题,实际上就是给定一组观测序列(当然还有相应的转移概率分布,发射概率分布,初始状态/初始状态的概率分布),求最可能的隐序列
在这里插入图片描述
这是网上找来的一张图片,是通过观测序列(干湿程度)来判断天气情况(晴云雨),可以体会一下,和开头所给的求法是一样的,不同的就是路径由数字换成了概率。

代码实现

算法实现和测试clone自Github

实现

package com.hankcs.algorithm;

import static com.hankcs.algorithm.WeatherExample.Weather.*;
import static com.hankcs.algorithm.WeatherExample.Activity.*;

public class WeatherExample
{
    enum Weather
    {
        Rainy,
        Sunny,
    }
    enum Activity
    {
        walk,
        shop,
        clean,
    }
    static int[] states = new int[]{Rainy.ordinal(), Sunny.ordinal()};
    static int[] observations = new int[]{walk.ordinal(), shop.ordinal(), clean.ordinal()};
    static double[] start_probability = new double[]{0.6, 0.4};
    /**
     * 意思是从一个隐状态转移到另一个隐状态的概率
     */
    static double[][] transititon_probability = new double[][]{
            {0.7, 0.3},
            {0.4, 0.6},
    };
    /**
     * 在观测状态下,隐状态的概率
     */
    static double[][] emission_probability = new double[][]{
            {0.1, 0.4, 0.5},
            {0.6, 0.3, 0.1},
    };

    public static void main(String[] args)
    {
        int[] result = Viterbi.compute(observations, states, start_probability, transititon_probability, emission_probability);
        for (int r : result)
        {
            System.out.print(Weather.values()[r] + " ");
        }
        System.out.println();
    }
}

测试

package com.hankcs.algorithm;

import static com.hankcs.algorithm.WeatherExample.Weather.*;
import static com.hankcs.algorithm.WeatherExample.Activity.*;

public class WeatherExample
{
    enum Weather
    {
        Rainy,
        Sunny,
    }
    enum Activity
    {
        walk,
        shop,
        clean,
    }
    static int[] states = new int[]{Rainy.ordinal(), Sunny.ordinal()};
    static int[] observations = new int[]{walk.ordinal(), shop.ordinal(), clean.ordinal()};
    static double[] start_probability = new double[]{0.6, 0.4};
    /**
     * 意思是从一个隐状态转移到另一个隐状态的概率
     */
    static double[][] transititon_probability = new double[][]{
            {0.7, 0.3},
            {0.4, 0.6},
    };
    /**
     * 在观测状态下,隐状态的概率
     */
    static double[][] emission_probability = new double[][]{
            {0.1, 0.4, 0.5},
            {0.6, 0.3, 0.1},
    };

    public static void main(String[] args)
    {
        int[] result = Viterbi.compute(observations, states, start_probability, transititon_probability, emission_probability);
        for (int r : result)
        {
            System.out.print(Weather.values()[r] + " ");
        }
        System.out.println();
    }
}

参考

谁能通俗的讲解下viterbi算法?
维特比算法(Viterbi Algorithm)

猜你喜欢

转载自blog.csdn.net/sailist/article/details/83064985