Viterbi算法

1、Vierbi算法介绍

Viterbi算法是一种动态规划算法,用于寻找最有可能产生观测事件序列的--viterbi路径--隐含状态序列,特别是在马尔可夫信息源上下文和隐马尔可夫模型中。

在语音识别中,声音信号作为观察到的事件序列,而文本字符串被看作是隐含的产生声音信号的原因,因此可对声音信号应用维特比算法寻找最有可能的文本字符串。

Viterbi算法解决的是栅栏(Lattice)图的最短路径问题,图的节点按列组织,每一列的节点只能和相邻的列的节点相连,不能跨列相连,节点之间有着不同的距离。

Viterbi算法可以概括成以下三点:

Viterbi算法_第1张图片

(1)如果概率最大的路径p(或者说最短路径)经过某个点,比如途中的X22,那么这条路径上的起始点start到X22的这段子路径Q,一定是start到X22之间的最短路径。否则,用start到X22的最短路径R替代Q,便构成一条比P更短的路径,这显然是矛盾的。证明了满足最优性原理。

(2)从start到end的路径必定经过第i个时刻的某个状态,假定第i个时刻有k个状态,那么如果记录了从S到第i个状态的所有k个节点的最短路径,最终的最短路径必经过其中一条,这样在任意时刻,只要考虑非常有限的最短路径即可。

(3)结合以上两点,假定当我们从状态i进入状态i+1时,从start到状态i上各个节的最短路径已经找到,并且记录在这些节点上,那么在计算从起点S到第i+1状态的某个节点X(i+1)的最短路径时,只要考虑从start到前一个状态i所有的k个节点的最短路径,以及从这个节点到X(i+1, j)的距离即可。

举例:

用Viterbi算法针对HMM三大问题中的解码问题(给定模型和观测序列,如何找到与此观测序列最匹配的状态序列的问题)进行求解。

问题描述如下:

首先,已经知道状态序列X会产生观测序列O:

Viterbi算法_第2张图片

但是观测序列O对应的状态序列X有很多种可能,每种都有着不同概率,如下图所示:

Viterbi算法_第3张图片

因此问题就转变成了,在图中求从start到end的最短路径的形式,如下图所示:

Viterbi算法_第4张图片

也可以描述为下图所示:

Viterbi算法_第5张图片

解法描述如下:

Viterbi算法_第6张图片

如上图,为了在start和end之间找一条最短的路径,从start开始从左到右一列一列的进行计算。

首先从start到A列的路径,有三种可能:start-A1、start-A2、start-A3,如下图:

Viterbi算法_第7张图片

不能决定start-A1、start-A2、start-A3中的哪一条路径是最短路径中的一部分。接下来在看B列,对B列的B1、B2、B3一一分析。先看B1:

Viterbi算法_第8张图片

如上图,经过B1的路径有3条:start-A1-B1、start-A2-B1、start-A3-B1这三条路径中肯定可以确定哪一条是最短的,假设start-A1-B1是最短的,就得出了经过B1的所有路径当中start-A1-B1是最短的,其它两条路径路径start-A2-B1和start-A3-B1可以删掉了。

Viterbi算法_第9张图片

接下来,继续看B2:

Viterbi算法_第10张图片

如上图,经过B2的路径有3条:start-A1-B2、start-A2-B2、start-A3-B2这三条路径中肯定可以确定其中哪一条是最短的,假设start-A1-B2是最短的,那么就得出了经过B2的所有路径当中start-A1-B2是最短的,其它两条路径路径start-A2-B2和start-A3-B1可以删掉了。

Viterbi算法_第11张图片

接下来继续看B3:

Viterbi算法_第12张图片

如上图,经过B3的路径有3条:start-A1-B3、start-A2-B3、start-A3-B3这三条路径中肯定可以确定其中哪一条是最短的,假设start-A2-B3是最短的,那么就得出了经过B3的所有路径当中start-A2-B3是最短的,其它两条路径路径start-A1-B3和start-A3-B3可以删掉了。

Viterbi算法_第13张图片

现在对于B列的所有节点都遍历了一遍,可以看到最终留下了三个可能的最短路径:start-A1-B1、start-A1-B2、start-A2-B3,汇总到下图:

Viterbi算法_第14张图片

start-A1-B1、start-A1-B2、start-A2-B3都有可能是全局的最短路径,依然不能确定地说哪一条一定是全局最短路径的一部分。

接下来继续往下看C列,依然从C1、C2、C3一个个节点分析。经过C1节点的路径有:start-A1-B1-C1、start-A1-B2-C1、start-A2-B3-C1。

Viterbi算法_第15张图片

可以从这三条路径中过找到最短的那条,假如最短路径是start-A1-B2-C1,其它两条就可以删掉了。

Viterbi算法_第16张图片

同理,可以找到经过C2和C3节点的最短路径,则到C列的最短路径start-A1-B2-C1,start-A2-B3-C2,start-A2-B3-C3汇总如下:

Viterbi算法_第17张图片

在C列也剩下3条备选的最短路径,仍然不能确定哪条路径最短。接下里继续看D列了。到D列最后也只有3种可能性:start-A1-B2-C1-D1,start-A1-B2-C1-D2,start-A2-B3-C3-D3汇总如下:

Viterbi算法_第18张图片

接下来就到了最后一个节点end了。因为在D列剩下3条最短路径,可以在从D列到end的三条路径中找一条最短的路径(即红色的路径start-A1-B2-C1-D2-end为start到end的最短路径)即可。

Viterbi算法_第19张图片

在效率方面相对于粗暴的遍历所有路径,维特比算法到达每一列的时候都会删除不符合最短路径要求的路径【剪枝】,大大降低时间复杂度。

2、Vierbi算法python实现

import numpy as np

"""
A1(0.7)     B1(0.2)     C1(0.4)     D1(0.3)
A2(0.2)     B2(0.2)     C2(0.4)     D2(0.3)
A3(0.1)     B3(0.6)     C3(0.2)     D3(0.4)
state = [[0.7, 0.2, 0.1],   ---> A1 A2 A3
         [0.2, 0.2, 0.6],   ---> B1 B2 B3
         [0.4, 0.4, 0.2],   ---> C1 C2 C3
         [0.3, 0.3, 0.4]]   ---> D1 D2 D3

A1(0.7)  -->   B1 (0.1)        B1   -->   C1(0.8)        C1   -->   D1(0.7)
               B2 (0.4)             -->   C2(0.1)             -->   D2(0.2)
               B3 (0.5)             -->   C3(0.1)             -->   D3(0.1)
A2(0.2)  -->   B1 (0.4)        B2   -->   C1(0.4)        C2   -->   D1(0.6)
               B2 (0.3)             -->   C2(0.3)             -->   D2(0.2)
               B3 (0.3)             -->   C3(0.3)             -->   D3(0.2)
A3(0.1)  -->   B1 (0.3)        B3   -->   C1(0.1)        C3   -->   D1(0.4)
               B2 (0.6)             -->   C2(0.2)             -->   D2(0.1)
               B3 (0.1)             -->   C3(0.7)             -->   D3(0.5)
其他也如上所示,组成weight数组
"""

state = [[0.7, 0.2, 0.1],
         [0.2, 0.2, 0.6],
         [0.4, 0.4, 0.2],
         [0.3, 0.3, 0.4]]

weight = [[[0.1, 0.4, 0.5], [0.4, 0.3, 0.3], [0.3, 0.6, 0.1]],
          [[0.8, 0.1, 0.1], [0.4, 0.3, 0.3], [0.1, 0.2, 0.7]],
          [[0.7, 0.2, 0.1], [0.6, 0.2, 0.2], [0.4, 0.1, 0.5]]]

def viterbi(state, weight):
    """
        :param state: 状态矩阵
        :param weight: 权重矩阵
        :return:
    """
    state = np.array(state)
    weight = np.array(weight)
    row, col = state.shape
    assert weight.shape == (row - 1, col, col), 'state not match path!'

    # 路径矩阵,元素值表示当前节点从前一层的那一个节点过来是最优的
    # 因为:每层有3个节点,共4层;因此每条路径都有4个几点,即A-B-C-D。
    # 每次保留最优的3个路径。
    path = np.zeros(shape=(col, row))

    for i in range(row):
        print(f'进入第 {i} 层')
        if i == 0:
            # 此时,只遍历到了A列,因此path中保留了A1;A2;A3。
            path[:, i] = np.array(range(col)) + 1
            continue

        # i不等于0时,遍历到了B、C、D列。并更新path中的数据
        for j in range(col):
            # 循环遍历B列的所有:B1、B2、B3;C列的所有:C1、C2、C3;C列的所有:D1、D2、D3;
            # 以A1-->B1, A2--->B1, A3--->B1为例,求出到B1的最优路径(temp最大)。
            temp = state[i - 1, :] * weight[i - 1, :, j]
            temp_max = max(temp)
            temp_index = np.where(temp == temp_max)
            path[j, i] = temp_index[0] + 1
            # 保存中间过程的值
            state[i, j] = max(temp) * state[i, j]
    print(state)
    print(path)

if __name__ == '__main__':
    viterbi(state, weight)

运行结果:

Viterbi算法_第20张图片

可以用手算结果和代码运行结果,进行比较。

你可能感兴趣的:(语音,神经网络共同学习,算法)