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查询Mysql时返回字典结构的代码
Jun 18 Python
Python2.x版本中cmp()方法的使用教程
May 14 Python
在Python的Django框架中为代码添加注释的方法
Jul 16 Python
python编程开发之日期操作实例分析
Nov 13 Python
详解Python的Django框架中manage命令的使用与扩展
Apr 11 Python
python编程实现随机生成多个椭圆实例代码
Jan 03 Python
centos6.5安装python3.7.1之后无法使用pip的解决方案
Feb 14 Python
python3利用Socket实现通信的方法示例
May 06 Python
详解Python下载图片并保存本地的两种方式
May 15 Python
Django前后端分离csrf token获取方式
Dec 25 Python
python利用appium实现手机APP自动化的示例
Jan 26 Python
pycharm进入时每次都是insert模式的解决方式
Feb 05 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基础教程
2015/08/26 PHP
WordPress中限制非管理员用户在文章后只能评论一次
2015/12/31 PHP
PHP使用Memcache时模拟命名空间及缓存失效问题的解决
2016/02/27 PHP
laradock环境docker-compose操作详解
2019/07/29 PHP
JS JavaScript获取Url参数,src属性参数
2021/03/09 Javascript
什么是json和jsonp,jQuery json实例详详细说明
2012/12/11 Javascript
jquery select多选框的左右移动 具体实现代码
2013/07/03 Javascript
js 获取浏览器版本以此来调整CSS的样式
2014/06/03 Javascript
JS闭包、作用域链、垃圾回收、内存泄露相关知识小结
2016/05/16 Javascript
实现隔行换色效果的两种方式【实用】
2016/11/27 Javascript
angular4模块中给标签添加背景图的实现方法
2017/09/15 Javascript
Vue中自定义全局组件的实现方法
2017/12/08 Javascript
如何获取TypeScript的声明文件.d.ts
2018/05/01 Javascript
在vue项目中引用Iview的方法
2018/09/14 Javascript
webpack+vue-cli项目中引入外部非模块格式js的方法
2018/09/28 Javascript
js canvas实现橡皮擦效果
2018/12/20 Javascript
JavaScript forEach中return失效问题解决方案
2020/06/01 Javascript
微信小程序实现左滑删除效果
2020/11/18 Javascript
在Python中通过threading模块定义和调用线程的方法
2016/07/12 Python
机器学习经典算法-logistic回归代码详解
2017/12/22 Python
Python线程下使用锁的技巧分享
2018/09/13 Python
python针对不定分隔符切割提取字符串的方法
2018/10/26 Python
python 求一个列表中所有元素的乘积实例
2019/06/11 Python
Python爬虫+tkinter界面实现历史天气查询的思路详解
2021/02/22 Python
CSS3 三维变形实现立体方块特效源码
2016/12/15 HTML / CSS
将HTML5 Canvas的内容保存为图片借助toDataURL实现
2013/05/20 HTML / CSS
吉列剃须刀美国官网:Gillette美国
2018/07/13 全球购物
Linden Leaves官网:新西兰纯净护肤品
2020/12/20 全球购物
会计实习生工作总结的自我评价
2013/10/07 职场文书
优秀毕业自我鉴定
2014/02/15 职场文书
《赵州桥》教学反思
2014/02/17 职场文书
反腐倡廉演讲稿
2014/05/22 职场文书
硕士毕业答辩开场白
2015/05/27 职场文书
酒店开业主持词
2015/07/02 职场文书
Nginx开启Brotli压缩算法实现过程详解
2021/03/31 Servers
Golang 编译成DLL文件的操作
2021/05/06 Golang