今天看啥  ›  专栏  ›  Plenari

14.维比特

Plenari  · 简书  ·  · 2019-06-20 17:22

想象一个乡村诊所。村民有着非常理想化的特性,要么健康要么发烧。他们只有问诊所的医生的才能知道是否发烧。 聪明的医生通过询问病人的感觉诊断他们是否发烧。村民只回答他们感觉正常、头晕或冷。

假设一个病人每天来到诊所并告诉医生他的感觉。医生相信病人的健康状况如同一个离散马尔可夫链。病人的状态有两种“健康”和“发烧”,但医生不能直接观察到,这意味着状态对他是“隐含”的。每天病人会告诉医生自己有以下几种由他的健康状态决定的感觉的一种:正常、冷或头晕。这些是观察结果。 整个系统为一个隐马尔可夫模型(HMM)。

医生知道村民的总体健康状况,还知道发烧和没发烧的病人通常会抱怨什么症状。 换句话说,医生知道隐马尔可夫模型的参数。 这可以用Python语言表示如下:

在这段代码中, 起始概率start_probability 表示病人第一次到访时医生认为其所处的HMM状态,他唯一知道的是病人倾向于是健康的。这里用到的特定概率分布不是均衡的,如转移概率大约是{'Healthy': 0.57, 'Fever': 0.43}。 转移概率transition_probability表示潜在的马尔可夫链中健康状态的变化。在这个例子中,当天健康的病人仅有30%的机会第二天会发烧。放射概率emission_probability表示每天病人感觉的可能性。假如他是健康的,50%会感觉正常。如果他发烧了,有60%的可能感觉到头晕

An_example_of_HMM.png
  • 参考文献
  1. https://zh.wikipedia.org/wiki/%E7%BB%B4%E7%89%B9%E6%AF%94%E7%AE%97%E6%B3%95
# -*- coding: utf-8 -*-
"""
Created on Wed Jun 13 09:08:18 2018

@author: 维基百科
"""
states = ('Healthy', 'Fever')
 
observations = ('normal', 'cold', 'dizzy')
 
start_probability = {'Healthy': 0.6, 'Fever': 0.4}
 
transition_probability = {
   'Healthy' : {'Healthy': 0.7, 'Fever': 0.3},
   'Fever' : {'Healthy': 0.4, 'Fever': 0.6},
   }
 
emission_probability = {
   'Healthy' : {'normal': 0.5, 'cold': 0.4, 'dizzy': 0.1},
   'Fever' : {'normal': 0.1, 'cold': 0.3, 'dizzy': 0.6},
   }

# Helps visualize the steps of Viterbi.
def print_dptable(V):
    print("    ")
    for i in range(len(V)): 
        print("%7d" % i)
    

    for y in V[0].keys():
        print("%.5s: " % y)
        for t in range(len(V)):
            print("%.7s" % ("%f" % V[t][y]))
        
obs, states, start_p, trans_p, emit_p=observations,states, start_probability, transition_probability,emission_probability
def viterbi(obs, states, start_p, trans_p, emit_p):
    '''
    obs 为观察结果序列, 例如 ['normal', 'cold', 'dizzy']; 
    states 为一组隐含状态; ('Healthy', 'Fever')
    start_p 为起始状态概率;  {'Healthy': 0.6, 'Fever': 0.4}
    trans_p 为转移概率; 隐藏状态之间的转移概率
    而 emit_p 为放射概率。 隐藏装到观察状态的转移概率
    为了简化代码,我们假设观察序列 obs 非空且 trans_p[i][j] 和 emit_p[i][j] 对所有状态 i,j 有定义。
    '''
    V = [{}]#观察状态的概率
    path = {}

    # Initialize base cases (t == 0)
    for y in states:
        '''
        计算所有初始隐藏态的到第一个观测态的概率
        '''
        V[0][y] = start_p[y] * emit_p[y][obs[0]]
        path[y] = [y]

    # Run Viterbi for t > 0
    for t in range(1,len(obs)):
        V.append({})
        newpath = {}

        for y in states:
            '''
            上一步隐藏态对应下一步隐藏态的概率。
            概率计算=等于上一步隐藏态所有状态概率*可以转移的所有状态概率*转移后的状态对应观测态的概率。
            所有的然后计算最大值。            
            '''
            #      上一个隐藏天A状态 *到另一个B隐藏的概率--- 从B到
            (prob, state) = max([(V[t-1][y0] * trans_p[y0][y] * emit_p[y][obs[t]], y0) for y0 in states])
            V[t][y] = prob
            newpath[y] = path[state] + [y]

        # Don't need to remember the old paths
        path = newpath

    print_dptable(V)
    (prob, state) = max([(V[len(obs) - 1][y], y) for y in states])
    return (prob, path[state]),path,V

def example():
    return viterbi(observations,
                   states,
                   start_probability,
                   transition_probability,
                   emission_probability)
print(example())



原文地址:访问原文地址
快照地址: 访问文章快照