【NLP CODE】维特比算法 Viterbi

https://blog.csdn.net/Luzichang/article/details/91365752 

假设有这样一个问题,远在另一个城市上大学的儿子每天通过邮件向你汇报他今天做的最多事情是什么,这些事情可能是这三项之一:打球、读书、访友。

那么在这种场景下,如何推测儿子所在城市的天气情况。

假设儿子活动有如下规律:在晴天更可能打球,在阴天更可能访友,在雨天更可能的读书,在散发概率矩阵中显示为概率值大小。

在这个问题中,儿子的活动类型构成了模型的可见层,天气情况成了隐藏层

1.状态的样本空间 states = ('晴', '阴','雨');

2.起始的状态概率  start_probability = {'晴':0.5, '阴':0.3,'雨':0.2}
3.状态转移概率     transition_probability = {
                                                                                    '晴': {'晴':0.7, '阴':0.2,'雨':0.1},
                                                                                     '阴': {'晴':0.3, '阴':0.5,'雨':0.2},
                                                                                     '雨': {'晴':0.3, '阴':0.4,'雨':0.3},
                                                                   }
4.状态->观测的发散概率  emission_probability = {
                                                                                     '晴': { '读书': 0.3,'打球': 0.4, '访友': 0.3},
                                                                                     '阴': { '读书': 0.3,'打球': 0.2, '访友': 0.5},
                                                                                     '雨': { '读书': 0.8,'打球': 0.1, '访友': 0.1},
                                                                                   }

5.观测的样本空间(观测序列) observations = ( '读书', '打球','访友','读书')

代码:

# https://blog.csdn.net/Luzichang/article/details/91365752

import math
# 状态的样本空间
states = ('', '','')
# 观测的样本空间
observations = ( '读书', '打球','访友','读书')
#observations = ( '读书', '读书','读书','读书')
#observations = ( '打球', '打球','打球','打球')
#observations = ( '访友', '访友','访友','访友')
#observations = ( '读书', '访友','访友','读书')
# 起始个状态概率
start_probability = {'':0.5, '':0.3,'':0.2}
# 状态转移概率
transition_probability = {
  '': {'':0.7, '':0.2,'':0.1},
  '': {'':0.3, '':0.5,'':0.2},
  '': {'':0.3, '':0.4,'':0.3},
}
# 状态->观测的发散概率
emission_probability = {
  '': { '读书': 0.3,'打球': 0.4, '访友': 0.3},
  '': { '读书': 0.3,'打球': 0.2, '访友': 0.5},
  '': { '读书': 0.8,'打球': 0.1, '访友': 0.1},
}
# 计算以E为底的幂
def E(x):
  #return math.pow(math.e,x)
  return x
 
 
def display_result(observations,result_m):#较为友好清晰的显示结果
  # 从结果中找出最佳路径
  #print(result_m)
  infered_states = []
  final = len(result_m) - 1
  (p, pre_state), final_state = max(zip(result_m[final].values(), result_m[final].keys()))
  infered_states.insert(0, final_state)
  infered_states.insert(0, pre_state)
  for t in range(final - 1, 0, -1):
    _, pre_state = result_m[t][pre_state]
    infered_states.insert(0, pre_state)
  print(format("Viterbi Result", "=^80s"))
  head = format("观测序列", " ^7s")
  head += format("推断状态", " ^16s")
  for s in states:
    head += format(s, " ^15s")
  print(head)
  print(format("", "-^80s"))
 
  for obs,result,infered_state in zip(observations,result_m,infered_states):
    item = format(obs," ^10s")
    item += format(infered_state," ^18s")
    for s in states:
      item += format(result[s][0]," >12.8f")
      if infered_state == s:
        item += "(*)"
      else:
        item +="   "
 
    print(item)
  print(format("", "=^80s"))
 
 
 
def viterbi(obs, states, start_p, trans_p, emit_p):
 
  result_m = [{}] # 存放结果,每一个元素是一个字典,每一个字典的形式是 state:(p,pre_state)
                  # 其中state,p分别是当前状态下的概率值,pre_state表示该值由上一次的那个状态计算得到
  for s in states:  # 对于每一个状态
    result_m[0][s] = (E(start_p[s]*emit_p[s][obs[0]]),None) # 把第一个观测节点对应的各状态值计算出来
    #print('11',result_m[0][s])
  for t in range(1,len(obs)):
    result_m.append({})  # 准备t时刻的结果存放字典,形式同上
 
    for s in states: # 对于每一个t时刻状态s,获取t-1时刻每个状态s0的p,结合由s0转化为s的转移概率和s状态至obs的发散概率
                     # 计算t时刻s状态的最大概率,并记录该概率的来源状态s0
                     # max()内部比较的是一个tuple:(p,s0),max比较tuple内的第一个元素值
      result_m[t][s] = max([(E(result_m[t-1][s0][0]*trans_p[s0][s]*emit_p[s][obs[t]]),s0) for s0 in states])
      #print(result_m[t][s])
  return result_m    # 所有结果(包括最佳路径)都在这里,但直观的最佳路径还需要依此结果单独生成,在显示的时候生成
 
 
if __name__ == "__main__":
    result_m = viterbi( observations,
                        states,
                        start_probability,
                        transition_probability,
                        emission_probability)
    display_result(observations,result_m)
View Code

输出结果:

=================================Viterbi Result=================================
 观测序列        推断状态             晴              阴              雨       
--------------------------------------------------------------------------------
    读书            晴           0.15000000(*)  0.09000000     0.16000000
    打球            晴           0.04200000(*)  0.01280000     0.00480000
    访友            晴           0.00882000(*)  0.00420000     0.00042000
    读书            晴           0.00185220(*)  0.00063000     0.00070560
================================================================================

测试其他观测序列

=================================Viterbi Result=================================
 观测序列        推断状态             晴              阴              雨
--------------------------------------------------------------------------------
    读书            雨           0.15000000     0.09000000     0.16000000(*)
    读书            雨           0.03150000     0.01920000     0.03840000(*)
    读书            雨           0.00661500     0.00460800     0.00921600(*)
    读书            雨           0.00138915     0.00110592     0.00221184(*)
================================================================================
=================================Viterbi Result=================================
 观测序列        推断状态             晴              阴              雨       
--------------------------------------------------------------------------------
    打球            晴           0.20000000(*)  0.06000000     0.02000000
    打球            晴           0.05600000(*)  0.00800000     0.00200000
    打球            晴           0.01568000(*)  0.00224000     0.00056000
    打球            晴           0.00439040(*)  0.00062720     0.00015680
================================================================================
=================================Viterbi Result=================================
 观测序列        推断状态             晴              阴              雨
--------------------------------------------------------------------------------
    访友            阴           0.15000000     0.15000000(*)  0.02000000
    访友            阴           0.03150000     0.03750000(*)  0.00300000
    访友            阴           0.00661500     0.00937500(*)  0.00075000
    访友            阴           0.00138915     0.00234375(*)  0.00018750
================================================================================
=================================Viterbi Result=================================
 观测序列        推断状态             晴              阴              雨
--------------------------------------------------------------------------------
    访友            阴           0.15000000     0.15000000(*)  0.02000000
    访友            阴           0.03150000     0.03750000(*)  0.00300000
    读书            阴           0.00661500     0.00562500(*)  0.00600000
    访友            阴           0.00138915     0.00140625(*)  0.00018000
    访友            阴           0.00029172     0.00035156(*)  0.00002812
    访友            阴           0.00006126     0.00008789(*)  0.00000703
    读书            雨           0.00001286     0.00001318     0.00001406(*)
================================================================================
原文地址:https://www.cnblogs.com/hbuwyg/p/13252386.html