python实现隐马尔科夫模型HMM

 更新时间:2018年03月25日 11:27:53   作者:adzhua  
这篇文章主要为大家详细介绍了python实现隐马尔科夫模型HMM,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

一份完全按照李航<<统计学习方法>>介绍的HMM代码,供大家参考,具体内容如下

#coding=utf8 
''''' 
Created on 2017-8-5 
里面的代码许多地方可以精简,但为了百分百还原公式,就没有精简了。 
@author: adzhua 
''' 
 
import numpy as np 
 
class HMM(object): 
  def __init__(self, A, B, pi): 
    ''''' 
    A: 状态转移概率矩阵 
    B: 输出观察概率矩阵 
    pi: 初始化状态向量 
    ''' 
    self.A = np.array(A) 
    self.B = np.array(B) 
    self.pi = np.array(pi) 
    self.N = self.A.shape[0]  # 总共状态个数 
    self.M = self.B.shape[1]  # 总共观察值个数   
    
   
  # 输出HMM的参数信息 
  def printHMM(self): 
    print ("==================================================") 
    print ("HMM content: N =",self.N,",M =",self.M) 
    for i in range(self.N): 
      if i==0: 
        print ("hmm.A ",self.A[i,:]," hmm.B ",self.B[i,:]) 
      else: 
        print ("   ",self.A[i,:],"    ",self.B[i,:]) 
    print ("hmm.pi",self.pi) 
    print ("==================================================") 
           
   
  # 前向算法  
  def forwar(self, T, O, alpha, prob): 
    ''''' 
    T: 观察序列的长度 
    O: 观察序列 
    alpha: 运算中用到的临时数组 
    prob: 返回值所要求的概率 
    '''   
     
    # 初始化 
    for i in range(self.N): 
      alpha[0, i] = self.pi[i] * self.B[i, O[0]] 
 
    # 递归 
    for t in range(T-1): 
      for j in range(self.N): 
        sum = 0.0 
        for i in range(self.N): 
          sum += alpha[t, i] * self.A[i, j] 
        alpha[t+1, j] = sum * self.B[j, O[t+1]]     
     
    # 终止 
    sum = 0.0 
    for i in range(self.N): 
      sum += alpha[T-1, i] 
     
    prob[0] *= sum   
 
   
  # 带修正的前向算法 
  def forwardWithScale(self, T, O, alpha, scale, prob): 
    scale[0] = 0.0 
     
    # 初始化 
    for i in range(self.N): 
      alpha[0, i] = self.pi[i] * self.B[i, O[0]] 
      scale[0] += alpha[0, i] 
       
    for i in range(self.N): 
      alpha[0, i] /= scale[0] 
     
    # 递归 
    for t in range(T-1): 
      scale[t+1] = 0.0 
      for j in range(self.N): 
        sum = 0.0 
        for i in range(self.N): 
          sum += alpha[t, i] * self.A[i, j] 
         
        alpha[t+1, j] = sum * self.B[j, O[t+1]] 
        scale[t+1] += alpha[t+1, j] 
       
      for j in range(self.N): 
        alpha[t+1, j] /= scale[t+1] 
      
    # 终止 
    for t in range(T): 
      prob[0] += np.log(scale[t])     
       
       
  def back(self, T, O, beta, prob):  
    ''''' 
    T: 观察序列的长度  len(O) 
    O: 观察序列 
    beta: 计算时用到的临时数组 
    prob: 返回值;所要求的概率 
    '''  
     
    # 初始化         
    for i in range(self.N): 
      beta[T-1, i] = 1.0 
     
    # 递归 
    for t in range(T-2, -1, -1): # 从T-2开始递减;即T-2, T-3, T-4, ..., 0 
      for i in range(self.N): 
        sum = 0.0 
        for j in range(self.N): 
          sum += self.A[i, j] * self.B[j, O[t+1]] * beta[t+1, j] 
         
        beta[t, i] = sum 
     
    # 终止 
    sum = 0.0 
    for i in range(self.N): 
      sum += self.pi[i]*self.B[i,O[0]]*beta[0,i] 
     
    prob[0] = sum   
     
     
  # 带修正的后向算法 
  def backwardWithScale(self, T, O, beta, scale): 
    ''''' 
    T: 观察序列的长度 len(O) 
    O: 观察序列 
    beta: 计算时用到的临时数组 
    ''' 
    # 初始化 
    for i in range(self.N): 
      beta[T-1, i] = 1.0 
     
    # 递归         
    for t in range(T-2, -1, -1): 
      for i in range(self.N): 
        sum = 0.0 
        for j in range(self.N): 
          sum += self.A[i, j] * self.B[j, O[t+1]] * beta[t+1, j] 
         
        beta[t, i] = sum / scale[t+1]     
         
   
  # viterbi算法       
  def viterbi(self, O): 
    ''''' 
    O: 观察序列 
    ''' 
    T = len(O) 
    # 初始化 
    delta = np.zeros((T, self.N), np.float) 
    phi = np.zeros((T, self.N), np.float) 
    I = np.zeros(T) 
     
    for i in range(self.N): 
      delta[0, i] = self.pi[i] * self.B[i, O[0]] 
      phi[0, i] = 0.0 
     
    # 递归 
    for t in range(1, T): 
      for i in range(self.N): 
        delta[t, i] = self.B[i, O[t]] * np.array([delta[t-1, j] * self.A[j, i] for j in range(self.N)] ).max() 
        phi = np.array([delta[t-1, j] * self.A[j, i] for j in range(self.N)]).argmax() 
       
    # 终止 
    prob = delta[T-1, :].max() 
    I[T-1] = delta[T-1, :].argmax() 
     
    for t in range(T-2, -1, -1): 
      I[t] = phi[I[t+1]] 
       
     
    return prob, I 
   
   
  # 计算gamma(计算A所需的分母;详情见李航的统计学习) : 时刻t时马尔可夫链处于状态Si的概率 
  def computeGamma(self, T, alpha, beta, gamma): 
    '''''''' 
    for t in range(T): 
      for i in range(self.N): 
        sum = 0.0 
        for j in range(self.N): 
          sum += alpha[t, j] * beta[t, j] 
         
        gamma[t, i] = (alpha[t, i] * beta[t, i]) / sum   
   
  # 计算sai(i,j)(计算A所需的分子) 为给定训练序列O和模型lambda时 
  def computeXi(self, T, O, alpha, beta, Xi): 
     
    for t in range(T-1): 
      sum = 0.0 
      for i in range(self.N): 
        for j in range(self.N): 
          Xi[t, i, j] = alpha[t, i] * self.A[i, j] * self.B[j, O[t+1]] * beta[t+1, j] 
          sum += Xi[t, i, j] 
       
      for i in range(self.N): 
        for j in range(self.N): 
          Xi[t, i, j] /= sum 
   
   
  # 输入 L个观察序列O,初始模型:HMM={A,B,pi,N,M} 
  def BaumWelch(self, L, T, O, alpha, beta, gamma):                   
    DELTA = 0.01 ; round = 0 ; flag = 1 ; probf = [0.0] 
    delta = 0.0; probprev = 0.0 ; ratio = 0.0 ; deltaprev = 10e-70 
     
    xi = np.zeros((T, self.N, self.N)) # 计算A的分子 
    pi = np.zeros((T), np.float)  # 状态初始化概率 
     
    denominatorA = np.zeros((self.N), np.float) # 辅助计算A的分母的变量 
    denominatorB = np.zeros((self.N), np.float) 
    numeratorA = np.zeros((self.N, self.N), np.float)  # 辅助计算A的分子的变量 
    numeratorB = np.zeros((self.N, self.M), np.float)  # 针对输出观察概率矩阵 
    scale = np.zeros((T), np.float) 
     
    while True: 
      probf[0] =0 
       
      # E_step 
      for l in range(L): 
        self.forwardWithScale(T, O[l], alpha, scale, probf) 
        self.backwardWithScale(T, O[l], beta, scale) 
        self.computeGamma(T, alpha, beta, gamma)  # (t, i) 
        self.computeXi(T, O[l], alpha, beta, xi)  #(t, i, j) 
         
        for i in range(self.N): 
          pi[i] += gamma[0, i] 
          for t in range(T-1): 
            denominatorA[i] += gamma[t, i] 
            denominatorB[i] += gamma[t, i] 
          denominatorB[i] += gamma[T-1, i] 
         
          for j in range(self.N): 
            for t in range(T-1): 
              numeratorA[i, j] += xi[t, i, j] 
             
          for k in range(self.M): # M为观察状态取值个数 
            for t in range(T): 
              if O[l][t] == k: 
                numeratorB[i, k] += gamma[t, i]   
                 
       
      # M_step。 计算pi, A, B 
      for i in range(self.N): # 这个for循环也可以放到for l in range(L)里面 
        self.pi[i] = 0.001 / self.N + 0.999 * pi[i] / L 
         
        for j in range(self.N): 
          self.A[i, j] = 0.001 / self.N + 0.999 * numeratorA[i, j] / denominatorA[i]           
          numeratorA[i, j] = 0.0 
         
        for k in range(self.M): 
          self.B[i, k] = 0.001 / self.N + 0.999 * numeratorB[i, k] / denominatorB[i] 
          numeratorB[i, k] = 0.0   
         
        #重置 
        pi[i] = denominatorA[i] = denominatorB[i] = 0.0 
         
      if flag == 1: 
        flag = 0 
        probprev = probf[0] 
        ratio = 1 
        continue 
       
      delta = probf[0] - probprev  
      ratio = delta / deltaprev   
      probprev = probf[0] 
      deltaprev = delta 
      round += 1 
       
      if ratio <= DELTA : 
        print('num iteration: ', round)   
        break 
     
 
if __name__ == '__main__': 
  print ("python my HMM") 
   
  # 初始的状态概率矩阵pi;状态转移矩阵A;输出观察概率矩阵B; 观察序列 
  pi = [0.5,0.5] 
  A = [[0.8125,0.1875],[0.2,0.8]] 
  B = [[0.875,0.125],[0.25,0.75]] 
  O = [ 
     [1,0,0,1,1,0,0,0,0], 
     [1,1,0,1,0,0,1,1,0], 
     [0,0,1,1,0,0,1,1,1] 
    ] 
  L = len(O) 
  T = len(O[0])  # T等于最长序列的长度就好了 
   
  hmm = HMM(A, B, pi) 
  alpha = np.zeros((T,hmm.N),np.float) 
  beta = np.zeros((T,hmm.N),np.float) 
  gamma = np.zeros((T,hmm.N),np.float) 
   
  # 训练 
  hmm.BaumWelch(L,T,O,alpha,beta,gamma) 
   
  # 输出HMM参数信息 
  hmm.printHMM()  

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • Python数据类型之List列表实例详解

    Python数据类型之List列表实例详解

    这篇文章主要介绍了Python数据类型之List列表,结合实例形式分析了PythonList列表的概念、功能、定义以及判断、截取、遍历、切片等常见操作技巧,需要的朋友可以参考下
    2019-05-05
  • Python利用pygame模块制作代码雨

    Python利用pygame模块制作代码雨

    对Python游戏有所了解的朋友都知道,在2D的游戏制作中,经常会用到一个模块pygame,他能帮助我们实现很多方便使用的功能,本文就来用pygame模块制作代码雨效果,需要的可以了解一下
    2023-05-05
  • Python数值方法及数据可视化

    Python数值方法及数据可视化

    这篇文章主要介绍了Python数值方法及数据可视化,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-09-09
  • 解锁Python中神器vars内置函数的使用

    解锁Python中神器vars内置函数的使用

    vars()函数是一个内置函数,用于返回对象的__字典__,其中包含对象的__属性__,本文主要为大家详细介绍了vars()函数的具体使用,需要的小伙伴可以了解下
    2023-11-11
  • Python 列表排序详解

    Python 列表排序详解

    这篇文章主要介绍了Python中对列表排序实例,本文给出了9个List的排序实例,需要的朋友可以参考下,希望能够给你带来帮助
    2021-10-10
  • Python 使用SMTP发送邮件的代码小结

    Python 使用SMTP发送邮件的代码小结

    python的smtplib提供了一种很方便的途径发送电子邮件。它对smtp协议进行了简单的封装,需要的朋友可以参考下
    2016-09-09
  • Python实现的远程登录windows系统功能示例

    Python实现的远程登录windows系统功能示例

    这篇文章主要介绍了Python实现的远程登录windows系统功能,结合实例形式分析了Python基于wmi模块的远程连接与进程操作相关实现技巧,需要的朋友可以参考下
    2018-06-06
  • 关于Pycharm配置翻译插件Translation报错更新TTK失败不能使用的问题

    关于Pycharm配置翻译插件Translation报错更新TTK失败不能使用的问题

    这篇文章主要介绍了关于Pycharm配置翻译插件Translation报错更新TTK失败不能使用的问题,本文通过图文并茂的形式给大家分享解决方案,需要的朋友可以参考下
    2022-04-04
  • Python3+selenium配置常见报错解决方案

    Python3+selenium配置常见报错解决方案

    这篇文章主要介绍了Python3+selenium配置常见报错解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-08-08
  • Python中lambda的用法及其与def的区别解析

    Python中lambda的用法及其与def的区别解析

    这篇文章主要介绍了Python中lambda的用法及其与def的区别解析,需要的朋友可以参考下
    2014-07-07

最新评论