python实现隐马尔科夫模型HMM


Posted in Python onMarch 25, 2018

一份完全按照李航<<统计学习方法>>介绍的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 相关文章推荐
Python实现远程调用MetaSploit的方法
Aug 22 Python
RC4文件加密的python实现方法
Jun 30 Python
Python简明入门教程
Aug 04 Python
Python中MySQLdb和torndb模块对MySQL的断连问题处理
Nov 09 Python
疯狂上涨的Python 开发者应从2.x还是3.x着手?
Nov 16 Python
TensorFlow实现RNN循环神经网络
Feb 28 Python
python实现自动发送报警监控邮件
Jun 21 Python
python异步实现定时任务和周期任务的方法
Jun 29 Python
Django model update的多种用法介绍
Mar 28 Python
python代数式括号有效性检验示例代码
Oct 04 Python
详解Python如何批量采集京东商品数据流程
Jan 22 Python
如何通过一篇文章了解Python中的生成器
Apr 02 Python
Python实现的寻找前5个默尼森数算法示例
Mar 25 #Python
Python实现修改文件内容的方法分析
Mar 25 #Python
利用python为运维人员写一个监控脚本
Mar 25 #Python
python实现数据写入excel表格
Mar 25 #Python
使用requests库制作Python爬虫
Mar 25 #Python
利用Python代码实现数据可视化的5种方法详解
Mar 25 #Python
Python cookbook(数据结构与算法)同时对数据做转换和换算处理操作示例
Mar 23 #Python
You might like
微信支付的开发流程详解
2016/09/13 PHP
PHP设计模式之建造者模式定义与用法简单示例
2018/08/13 PHP
JQuery live函数
2010/12/24 Javascript
js使用函数绑定技术改变事件处理程序的作用域
2011/12/26 Javascript
JQuery-tableDnD 拖拽的基本使用介绍
2013/07/04 Javascript
jQuery中delegate和on的用法与区别详细解析
2014/01/26 Javascript
jquery实现鼠标滑过显示提示框的方法
2015/02/05 Javascript
jQuery控制网页打印指定区域的方法
2015/04/07 Javascript
jQuery实现带玻璃流光质感的手风琴特效
2015/11/20 Javascript
原生javascript实现分享到朋友圈功能 支持ios和android
2016/05/11 Javascript
js基于setTimeout与setInterval实现多线程
2016/06/17 Javascript
JS遍历页面所有对象属性及实现方法
2016/08/01 Javascript
js select实现省市区联动选择
2020/04/17 Javascript
KnockoutJS 3.X API 第四章之click绑定
2016/10/10 Javascript
使用jQuery实现鼠标点击左右按钮滑动切换
2017/08/04 jQuery
4个顶级JavaScript高级文本编辑器
2018/10/10 Javascript
详解keep-alive + vuex 让缓存的页面灵活起来
2019/04/19 Javascript
解决ele ui 表格表头太长问题的实现
2019/11/13 Javascript
微信小程序indexOf的替换方法(推荐)
2020/01/14 Javascript
python实现超简单端口转发的方法
2015/03/13 Python
python按修改时间顺序排列文件的实例代码
2019/07/25 Python
python 对任意数据和曲线进行拟合并求出函数表达式的三种解决方案
2020/02/18 Python
真正了解CSS3背景下的@font face规则
2017/05/04 HTML / CSS
Html5内唤醒百度、高德APP的实现示例
2019/05/20 HTML / CSS
网易微博Web App用HTML5开发的过程介绍
2012/06/13 HTML / CSS
Booking.com西班牙:全球酒店预订
2018/03/30 全球购物
Mankind美国/加拿大:英国领先的男士美容护发用品公司
2018/12/05 全球购物
最新奶茶店创业计划书
2014/01/25 职场文书
第一批党的群众路线教育实践活动工作总结
2014/03/03 职场文书
管理部副部长岗位职责范文
2014/03/09 职场文书
党员四风问题对照检查材料思想汇报
2014/09/16 职场文书
新闻人物通讯稿
2014/10/09 职场文书
2014年店长工作总结
2014/11/17 职场文书
机关单位保密工作责任书
2015/05/11 职场文书
2015年乡镇工会工作总结
2015/05/19 职场文书
JS轻量级函数式编程实现XDM二
2022/06/16 Javascript