Viterbi原理、算法实现、应用

参考资料:

  1. 《数学之美》第26章
  2. 隐马尔科夫模型及其应用
  • 背景
    已知隐马尔科夫链的可见序列,要求隐含序列(可见序列可以产生若干个隐含序列),比如语音输入法中你输入的拼音就是可见序列,对应的汉字也就是隐含序列就是我们要求的。假设可见序列的长度为T,每个可见字符有N个字符可以与之对应,那么我们可以产生N^T 个隐含序列,分别计算他们的概率,选出最大概率就是,这种方法太暴力。假设有10个拼音,每个拼音产生的字(隐含序列),假定都是一样的,都是13个,那么总共的结果是13^10 个。

  • 原理
    如下图所示,隐含层的全部路径是指数级别的,黄线构成的路径是其中一条,这种不是普遍的图,而叫做篱笆网络,可以看到是有层次的。


    Viterbi原理、算法实现、应用_第1张图片

算法基于两个假设:

  1. 目标路径经过某点a,那么目标路径S->a一定是从S和a之间最短的,不然可以找另外一条代替它。
  2. 路径必须经常每一层其中一个。

算法实施:假设每个点都可能是路径的一部分,前面k层的所有点都已经是选择概率最大的,现在来考虑第k+1层,假设第i层有N(i)个点,对于第k+1层的N(k+1)个点中的具体每个点,都有N(k)个输入,我们可以计算得出其中概率一条,这么一来就减少了其他的N(k)-1个,每一层需要计算的最大为O(N^2)(假设N为每层最大的)。
假设层数是从S开始,S的概率为1,那么就可以从S推导到最后一层,然后最后一层哪个比较大,从这个点重新回去,看看最后一层是从哪个点过来的,最后可以得出概率最大的链条。

还是假设10个拼音,每个拼音可能有13个汉字,一共有10个拼音,那么最大计算次数10*13^2 = 1690 << 13^10

我们再来看知乎上面的例子的计算过程:
谁能通俗的讲解下viterbi算法? - 李大雷的回答 - 知乎

Viterbi原理、算法实现、应用_第2张图片

  • 算法实现
from collections import Counter

outer_status = {'正常':0,'头晕':1,'发冷':2}
inner_status = {'健康':0,'发烧':1}
inner_status_len = len(inner_status)

def main():
    inner_table = [[0.7,0.3],[0.4,0.6]]
    outer_table = [[0.5,0.1,0.4],[0.1,0.6,0.3]]
    start_to_others = [0.6,0.4]
    outer_sequences = [outer_status['正常'],outer_status['发冷'],outer_status['头晕']]
    sequences_len = len(outer_sequences)

    # 计算结果存放表
    table = [[None]*inner_status_len for _ in range(sequences_len)]
    temp = [0.0] * sequences_len
    for i in range(inner_status_len):
        # 第一层手动算,None表示这是第一层,前面没有再多的
        table[0][i] = [start_to_others[i] * outer_table[i][outer_sequences[0]],None]
    for layer_i in range(1,sequences_len):
        pre_layer = layer_i - 1
        for current_j in range(inner_status_len):
            for pre_k in range(inner_status_len):
                temp[pre_k] = table[pre_layer][pre_k][0] * inner_table[pre_k][current_j] * outer_table[current_j][outer_sequences[layer_i]]

            min_value = max(temp)
            min_index = temp.index(min_value)
            table[layer_i][current_j] = [min_value,min_index]

    print(table)

    # 下面这大段是反向查找
    lastest_values = []
    for value in table[-1]:
        lastest_values.append(value[0])
    index = lastest_values.index(max(lastest_values))

    inner_sequence = []
    inner_sequence.append(index)
    layer = sequences_len - 1
    while True:
        index = table[layer][index][1]
        if index is None:
            break
        else:
            inner_sequence.append(index)
        layer -= 1

    inner_sequence.reverse()
    print(inner_sequence)


if __name__ == '__main__':
    main()

输出结果:

[[[0.3, None], [0.04000000000000001, None]], [[0.084, 0], [0.027, 0]], [[0.00588, 0], [0.01512, 0]]]
[0, 0, 1]

第一个表中每个元素篱笆对应点算法计算出来的最大概率和这个概率的上一个节点。
第二个输出[0,0,1]代表[健康,健康,发烧]

  • 应用
  • 其他
    谁能通俗的讲解下viterbi算法? - UFO的回答 - 知乎

你可能感兴趣的:(Viterbi原理、算法实现、应用)