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脚本实现分析dns日志并对受访域名排行
Sep 18 Python
详细介绍Python的鸭子类型
Sep 12 Python
python自动化脚本安装指定版本python环境详解
Sep 14 Python
Python使用正则表达式过滤或替换HTML标签的方法详解
Sep 25 Python
几种实用的pythonic语法实例代码
Feb 24 Python
Python matplotlib 画图窗口显示到gui或者控制台的实例
May 24 Python
python监测当前联网状态并连接的实例
Dec 18 Python
Python中pymysql 模块的使用详解
Aug 12 Python
python实现多线程端口扫描
Aug 31 Python
python修改linux中文件(文件夹)的权限属性操作
Mar 05 Python
Python切片列表字符串如何实现切换
Aug 06 Python
Python爬虫破解登陆哔哩哔哩的方法
Nov 17 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
php,ajax实现分页
2008/03/27 PHP
php从csv文件读取数据并输出到网页的方法
2015/03/14 PHP
php中文繁体和简体相互转换的方法
2015/03/21 PHP
PHP 双链表(SplDoublyLinkedList)简介和使用实例
2015/05/12 PHP
phpStudy配置多站点多域名和多端口的方法
2017/09/01 PHP
PHP 进度条函数的简单实例
2017/09/19 PHP
jQuery 常见操作实现方式和常用函数方法总结
2011/05/06 Javascript
Js从头学起(基本数据类型和引用类型的参数传递详细分析)
2012/02/16 Javascript
javascript实现可改变滚动方向的无缝滚动实例
2013/06/17 Javascript
javascript实现俄罗斯方块游戏的思路和方法
2015/04/27 Javascript
jQuery实现页面内锚点平滑跳转特效的方法总结
2015/05/11 Javascript
js实现select跳转菜单新窗口效果代码分享(超简单)
2015/08/21 Javascript
全面详细的jQuery常见开发技巧手册
2016/02/21 Javascript
jQuery简单实现提交数据出现loading进度条的方法
2016/03/29 Javascript
AngularJS在IE8的不支持的解决方法
2016/05/13 Javascript
Angular在一个页面中使用两个ng-app的方法
2017/02/20 Javascript
javascript实现日期三级联动下拉框选择菜单
2020/12/03 Javascript
简单谈谈React中的路由系统
2017/07/25 Javascript
Vue.directive()的用法和实例详解
2018/03/04 Javascript
angular2模块和共享模块详解
2018/04/08 Javascript
jQuery实现鼠标滑过商品小图片上显示对应大图片功能【测试可用】
2018/04/27 jQuery
vue elementUI table表格数据 滚动懒加载的实现方法
2019/04/04 Javascript
微信小程序实现的图片保存功能示例
2019/04/24 Javascript
微信小程序上线发布流程图文详解
2019/05/06 Javascript
vue项目中使用scss的方法步骤
2019/05/16 Javascript
浅探express路由和中间件的实现
2019/09/30 Javascript
基于javascript实现碰撞检测
2020/03/12 Javascript
ssm+vue前后端分离框架整合实现(附源码)
2020/07/08 Javascript
vue项目打包后请求地址错误/打包后跨域操作
2020/11/04 Javascript
Django框架自定义模型管理器与元选项用法分析
2019/07/22 Python
关于numpy中eye和identity的区别详解
2019/11/29 Python
购买英国原创艺术:Art Gallery
2018/08/25 全球购物
物业管理专业自荐信
2014/07/01 职场文书
2014年汽车销售工作总结
2014/12/01 职场文书
试用期自我评价范文
2015/03/10 职场文书
党风廉政建设心得体会(2016最新版)
2016/01/22 职场文书