维特比算法实现的几个例子

实现1:有观测序列,发射概率,状态转移矩阵返回最佳路径

# --*--coding:utf-8--*--
import numpy as np
# 隐状态
hidden_state=['sunny','rainy']
# 观测序列
observation=['walk','shop','clean']
#根据观测序列,发射概率,状态转移矩阵返回最佳路径
def viterbi(obs,states,start_p,trans_p,emit_p):
    '''

    :param obs: 观测序列
    :param states: 状态序列
    :param start_p: 初始化状态序列的概率
    :param trabs_p: 转移概率
    :param emit_p: 发射概率
    :return: 最佳路径
    '''
    # max_p为3*2的矩阵,每一列存储第一列不同隐状态的最大概率
    max_p=np.zeros((len(obs),len(states)))
    #path(2*3的矩阵),每一行存储max_p对应列的路径
    path=np.zeros((len(states),len(obs)))

    #初始化
    for i in range(len(states)):
        max_p[0][i]=start_p[i]*emit_p[i][obs[0]]
        path[i][0]=i
    for k in range(1,len(obs)):
        newpath=np.zeros((len(states),len(obs)))
        for v in range(len(states)):
            prob=-1
            for u in range(len(states)):
                nprob=max_p[k-1][u]*trans_p[u][v]*emit_p[v][obs[k]]
                if nprob>prob:
                    prob=nprob
                    state=u

                    #记录路径
                    max_p[k][v]=prob
                    for m in range(k):
                        newpath[v][m]=path[state][m]
                    newpath[v][k]=v
        path=newpath

    max_prob=-1
    path_state=0
    #返回最大概率的路径
    for v in range(len(states)):
        if max_p[len(obs)-1][v]>max_prob:
            max_prob=max_p[len(obs)-1][v]
            path_state=v
    return path[path_state]

state_s=[0,1]
obser=[0,1,2]
# 初始状态,测试集中,0.6概率观测序列以sunny开始
start_probability=[0.6,0.4]
# 转移概率,0.7:sunny的下一天还是sunny的概率
transition_probability=np.array([[0.7,0.3],[0.4,0.6]])
# 发射概率,0.4:sunny条件下0.4的概率去shop
emit_probability=np.array([[0.1,0.4,0.5],[0.6,0.3,0.1]])

result=viterbi(obser,state_s,start_probability,transition_probability,emit_probability)
for idx in range(len(result)):
    print(hidden_state[int(result[idx])])

 

实现2:

# --*--coding:utf-8 --*--
import numpy as np
def viterbi2(transiton_prob,emit_prob,pi,obs_seq):
    '''

    :param transiton_prob: 转移概率矩阵[len(隐状态),len(隐状态)]
    :param emit_prob:发射概率矩阵[len(观测序列),len(隐状态)]
    :param pi:初始概率
    :param obs_seq:观测序列
    :return:
    '''
    # 转换为矩阵进行运算
    tansition_prob=np.array(transiton_prob)
    emit_prob=np.array(emit_prob)
    pi=np.array(pi)
    obs_seq=[0,2,3]
    #最后返回一个row*col的矩阵
    Row=tansition_prob.shape[0]
    Col=len(obs_seq)
    F=np.zeros((Row,Col))
    #初始状态
    #emit_prob[:,obs_seq[0]]表示从初始状态转移到观测到的第一个状态的转移概率
    F[:,0]=pi*np.transpose(emit_prob[:,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(tansition_prob[:,n]))    #相当于pi(0,u,v)*p(v|u)
            #获取最大概率
            list_p=[]
            for i in list_x:
                list_p.append(i*10000)
            list_max.append(max(list_p)/10000)
        #相当与pi(0,u,v)*p(v|u)   再乘p(x|v)
        F[:,t]=np.array(list_max)*np.transpose(emit_prob[:,obs_seq[t]])
    return F

if __name__ == '__main__':
    # 隐藏状态
    invisible = ['Sunny', 'Cloud', 'Rainy']
    # 初始状态
    pi = [0.63, 0.17, 0.20]
    # 转移矩阵
    trainsion_probility = [[0.5, 0.375, 0.125], [0.25, 0.125, 0.625], [0.25, 0.375, 0.375]]
    # 发射矩阵
    emission_probility = [[0.6, 0.2, 0.15, 0.05], [0.25, 0.25, 0.25, 0.25], [0.05, 0.10, 0.35, 0.5]]
    # 最后显示状态:每列代表Dry,Damp,Soggy的概率
    obs_seq = [0, 2, 3]
    # 最后返回一个Row*Col的矩阵结果
    F = viterbi2(trainsion_probility, emission_probility, pi, obs_seq)
    print(F)


实现3:

# --*-- coding:utf-8 --*--
import numpy as np
states = ('Healthy', 'Fever') #所有可能的隐含状态
observations = ('normal', 'cold', 'dizzy') #实际观测到的现象序列
start_probability = {'Healthy': 0.8, 'Fever': 0.2} #每个隐含状态的初始概率
transition_probability = {'Healthy' : {'Healthy': 0.8, 'Fever': 0.2},'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},}
#某种隐含状态产生某种观测现象的概率
def Viterbit(obs, states, s_pro, t_pro, e_pro):
    path = { s:[] for s in states}#先生成一个初始的状态序列。
    print(path)# init path: path[s] represents the path ends with s
    curr_pro = {} #当前状态可能。
    for s in states:#每种状态枚举,当前为health
        print("obs[0]",obs[0]) #第一天是normal
        print("e_pro[%s][%s]"%(s,e_pro[s][obs[0]])) #打印normal,   healthy状况下发射为normal的概率
        print("s_pro[s]=",s_pro[s])   #健康对应的初始状态的概率
        print("s_pro[s]*e_pro[s][obs[0]]",s_pro[s]*e_pro[s][obs[0]])    #初始为healthy后,从health发射到normal的概率
        curr_pro[s] = s_pro[s]*e_pro[s][obs[0]] #Health状态为0.6*0.5=0.3,fever状态为0.1*0.4=0.04;第一天为health可能性最大。
    for i in range(1,len(obs)): #从第2天开始计算
        last_pro = curr_pro
        print("---------------------------")
        print("last_pro",last_pro)
        #每次都把当前态存起来。
        curr_pro = {}#先设置最初态。
        for curr_state in states:#逐一比对隐含状态。
            print("curr_state=%s"%curr_state)
            for last_state in states:
                print("****************************last_state=%s"%last_state)
                print("last_pro[%s]=%s"%(last_state,last_pro[last_state])) #最后状态
                print("t_pro[%s][%s]=%s"%(last_state,curr_state,t_pro[last_state][curr_state]))#last_states转移到当前状态。
                print("e_pro[%s][%s]=%s" % (curr_state, obs[i], e_pro[curr_state][obs[i]]))  # 当前状态到观测值的发射概率。
                #health-health,health-fever的概率。前一天health,今天health和fever的概率,选出最大的那个,得出今天的健康状况
                max_pro, last_sta = max(((last_pro[last_state] * t_pro[last_state][curr_state] * e_pro[curr_state][obs[i]],last_state) for last_state in states))
                print(max_pro)
                curr_pro[curr_state] =max_pro
                print(last_pro[last_state])
                curr_pro[curr_state]=curr_pro[curr_state].append(max(last_pro[last_state]))
                # find the final largest probability
                max_pro = -1
                max_path = None
        for s in states:
            path[s].append(s)
            if curr_pro[s] > max_pro:
                max_path = path[s]
                max_pro = curr_pro[s]
    return max_path

if __name__ == '__main__':
    obs = ['normal', 'cold', 'dizzy']  # 观测序列。
    print(Viterbit(obs, states, start_probability, transition_probability, emission_probability))


实现4:

# --*-- coding:utf-8 --*--
states = ('Rainy', 'Sunny')

observations = ('walk', 'shop', 'clean')

start_probability = {'Rainy': 0.6, 'Sunny': 0.4}

transition_probability = {
    'Rainy': {'Rainy': 0.7, 'Sunny': 0.3},
    'Sunny': {'Rainy': 0.4, 'Sunny': 0.6},
}

emission_probability = {
    'Rainy': {'walk': 0.1, 'shop': 0.4, 'clean': 0.5},
    'Sunny': {'walk': 0.6, 'shop': 0.3, 'clean': 0.1},
}


# 打印路径概率表
def print_dptable(V):
    print("    ",end='')
    for i in range(len(V)):
        print("%7d" % i,end='')
    print()

    for y in V[0].keys():
        print("%.5s: " % y,end='')
        for t in range(len(V)):
            print("%.7s" % ("%f" % V[t][y]),end='')
        print()


def viterbi(obs, states, start_p, trans_p, emit_p):
    """

    :param obs:观测序列
    :param states:隐状态
    :param start_p:初始概率(隐状态)
    :param trans_p:转移概率(隐状态)
    :param emit_p: 发射概率 (隐状态表现为显状态的概率)
    :return:
    """
    # 路径概率表 V[时间][隐状态] = 概率
    V = [{}]
    # 一个中间变量,代表当前状态是哪个隐状态
    path = {}

    # 初始化初始状态 (t == 0)
    for y in states:
        V[0][y] = start_p[y] * emit_p[y][obs[0]]
        path[y] = [y]

    # 对 t > 0 跑一遍维特比算法
    for t in range(1, len(obs)):
        V.append({})
        newpath = {}

        for y in states:
            # 概率 隐状态 =    前状态是y0的概率 * y0转移到y的概率 * y表现为当前状态的概率
            (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
            # 记录路径
            print(path[state])
            print([y])
            newpath[y] = path[state] + [y]
            print(newpath[y])

        # 不需要保留旧路径
        path = newpath

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


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


print(example())

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值