1. 前言

隐马尔科夫HMM模型是一类重要的机器学习方法,其主要用于序列数据的分析,广泛应用于语音识别、文本翻译、序列预测、中文分词等多个领域。虽然近年来,由于RNN等深度学习方法的发展,HMM模型逐渐变得不怎么流行了,但并不意味着完全退出应用领域,甚至在一些轻量级的任务中仍有应用。本系列博客将详细剖析隐马尔科夫链HMM模型,同以往网络上绝大多数教程不同,本系列博客将更深入地分析HMM,不仅包括估计序列隐状态的维特比算法(HMM解码问题)、前向后向算法等,而且还着重的分析HMM的EM训练过程,并将所有的过程都通过数学公式进行推导。

由于隐马尔科夫HMM模型是一类非常复杂的模型,其中包含了大量概率统计的数学知识,因此网络上多数博客一般都采用举例等比较通俗的方式来介绍HMM,这么做会让初学者很快明白HMM的原理,但要丢失了大量细节,让初学者处于一种似懂非懂的状态。而本文并没有考虑用非常通俗的文字描述HMM,还是考虑通过详细的数学公式来一步步引导初学者掌握HMM的思想。另外,本文重点分析了HMM的EM训练过程,这是网络上其他教程所没有的,而个人认为相比于维特比算法、前向后向算法,HMM的EM训练过程虽然更为复杂,但是一旦掌握这个训练过程,那么对于通用的链状图结构的推导、EM算法和网络训练的理解都会非常大的帮助。另外通过总结HMM的数学原理,也能非常方便将数学公式改写成代码。

最后,本文提供了一个简单版本的隐马尔科夫链HMM的Python代码,包含了高斯模型的HMM和离散HMM两种情况,代码中包含了HMM的训练、预测、解码等全部过程,核心代码总共只有200~300行代码,非常简单!个人代码水平比较渣=_=||,大家按照我的教程,应该都可以写出更鲁棒性更有高效的代码,附上Github地址:https://github.com/tostq/Easy_HMM

觉得好,就点星哦!

觉得好,就点星哦!

觉得好,就点星哦!


重要的事要说三遍!!!!

为了方便大家学习,我将整个HMM代码完善成整个学习项目,其中包括

hmm.py:HMM核心代码,包含了一个HMM基类,一个高斯HMM模型及一个离散HMM模型

DiscreteHMM_test.py及GaussianHMM_test.py:利用unnitest来测试我们的HMM,同时引入了一个经典HMM库hmmlearn作为对照组

Dice_01.py:利用离散HMM模型来解决丢色子问题的例子

Wordseg_02.py:解决中文分词问题的例子

Stock_03.py:解决股票预测问题的例子

2. 隐马尔科夫链HMM的背景

首先,已知一组序列 :

我们从这组序列中推导出产生这组序列的函数,假设函数参数为 ,其表示为

使得序列X发生概率最大的函数参数,要解决上式,最简单的考虑是将序列 的每个数据都视为独立的,比如建立一个神经网络。然后这种考虑会随着序列增长,而导致参数爆炸式增长。因此可以假设当前序列数据只与其前一数据值相关,即所谓的一阶马尔科夫链

有一阶马尔科夫链,也会有二阶马尔科夫链(即当前数据值取决于其前两个数据值)

当前本文不对二阶马尔科夫链进行深入分析了,着重考虑一阶马尔科夫链,现在根据一阶马尔科夫链的假设,我们有:

因此要解一阶马尔科夫链,其关键在于求数据(以下称观测值)之间转换函数 ,如果假设转换函数同序列中位置 (时间)无关,我们就能根据转换函数而求出整个序列的概率:

然而,如果观测值x的状态非常多(特别极端的情况是连续数据),转换函数会变成一个非常大的矩阵,如果x的状态有K个,那么转换函数就会是一个K*(K-1)个参数,而且对于连续变量观测值更是困难。

为了降低马尔科夫链的转换函数的参数量,我们引入了一个包含较少状态的隐状态值,将观测值的马尔科夫链转换为隐状态的马尔科夫链(即为隐马尔科夫链HMM)

其包含了一个重要假设:当前观测值只由当前隐状态所决定。这么做的一个重要好处是,隐状态值的状态远小于观测值的状态,因此隐藏状态的转换函数 的参数更少。

此时我们要决定的问题是:

在所有可能隐藏状态序列情况下,求使得序列 发生概率最大的函数参数 。

这里我们再总结下:

隐马尔科夫链HMM三个重要假设:

1. 当前观测值只由当前隐藏状态确定,而与其他隐藏状态或观测值无关(隐藏状态假设)

2. 当前隐藏状态由其前一个隐藏状态决定(一阶马尔科夫假设)

3. 隐藏状态之间的转换函数概率不随时间变化(转换函数稳定性假设)

隐马尔科夫链HMM所要解决的问题:

在所有可能隐藏状态序列情况下,求使得当前序列X产生概率最大的函数参数θ。

代码

http://blog.csdn.net/sinat_36005594/article/details/69568538

前几天用MATLAB实现了HMM的代码,这次用python写了一遍,依据仍然是李航博士的《统计学习方法》

由于第一次用python,所以代码可能会有许多缺陷,但是所有代码都用书中的例题进行了测试,结果正确。

这里想说一下python,在编写HMM过程中参看了之前写的MATLAB程序,发现他们有太多相似的地方,用到了numpy库,在python代码过程中最让我头疼的是数组角标,和MATLAB矩阵角标从1开始不同,numpy库数组角标都是从0开始,而且数组的维数也需要谨慎,一不小心就会出现too many indices for array的错误。程序中最后是维特比算法,在运行过程中出现了__main__:190: VisibleDeprecationWarning: using a non-integer number instead of an integer will result in an error in the future的警告,还没有去掉这个警告,查了一下说不影响结果,后面会去解决这个问题,下面贴出我的代码

# -*- coding: utf-8 -*-
"""
Created on Thu Feb 16 19:28:39 2017
2017-4-2
    ForwardBackwardAlg函数功能:实现前向算法
    理论依据:李航《统计学习方法》
2017-4-5
    修改了ForwardBackwardAlg函数名称为ForwardAlgo以及输出的alpha数组形式
    完成了BackwardAlgo函数功能:后向算法
    以及函数FBAlgoAppli:计算在观测序列和模型参数确定的情况下,
    某一个隐含状态对应相应的观测状态的概率
2017-4-6
    完成BaumWelchAlgo函数一次迭代
2017-4-7
    实现维特比算法
@author: sgp
"""

import numpy as np

#输入格式如下:
#A = np.array([[.5,.2,.3],[.3,.5,.2],[.2,.3,.5]])
#B = np.array([[.5,.5],[.4,.6],[.7,.3]])
#Pi = np.array([[.2,.4,.4]])
#O = np.array([[1,2,1]])

#应用ndarray在数组之间进行相互运算时,一定要确保数组维数相同!
#比如:
#In[93]:m = np.array([1,2,3,4])
#In[94]:m
#Out[94]: array([1, 2, 3, 4])
#In[95]:m.shape
#Out[95]: (4,)
#这里表示的是一维数组
#In[96]:m = np.array([[1,2,3,4]])
#In[97]:m
#Out[97]: array([[1, 2, 3, 4]])
#In[98]:m.shape
#Out[98]: (1, 4)
#而这里表示的就是二维数组
#注意In[93]和In[96]的区别,多一对中括号!!

#N = A.shape[0]为数组A的行数, H = O.shape[1]为数组O的列数
#在下列各函数中,alpha数组和beta数组均为N*H二维数组,也就是横向坐标是时间,纵向是状态

def ForwardAlgo(A,B,Pi,O):
    N = A.shape[0]#数组A的行数
    M = A.shape[1]#数组A的列数
    H = O.shape[1]#数组O的列数

    sum_alpha_1 = np.zeros((M,N))
    alpha = np.zeros((N,H))
    r = np.zeros((1,N))
    alpha_1 = np.multiply(Pi[0,:], B[:,O[0,0]-1])

    alpha[:,0] = np.array(alpha_1).reshape(1,N)#alpha_1是一维数组,在使用np.multiply的时候需要升级到二维数组。#错误是IndexError: too many indices for array

    for h in range(1,H):
        for i in range(N):
            for j in range(M):
                sum_alpha_1[i,j] = alpha[j,h-1] * A[j,i]
            r = sum_alpha_1.sum(1).reshape(1,N)#同理,将数组升级为二维数组
            alpha[i,h] = r[0,i] * B[i,O[0,h]-1]
    #print("alpha矩阵: \n %r" % alpha)
    p = alpha.sum(0).reshape(1,H)
    P = p[0,H-1]
    #print("观测概率: \n %r" % P)
    #return alpha
    return alpha, P

def BackwardAlgo(A,B,Pi,O):
    N = A.shape[0]#数组A的行数
    M = A.shape[1]#数组A的列数
    H = O.shape[1]#数组O的列数

    #beta = np.zeros((N,H))
    sum_beta = np.zeros((1,N))
    beta = np.zeros((N,H))
    beta[:,H-1] = 1
    p_beta = np.zeros((1,N))

    for h in range(H-1,0,-1):
        for i in range(N):
            for j in range(M):
                sum_beta[0,j] = A[i,j] * B[j,O[0,h]-1] * beta[j,h]
            beta[i,h-1] = sum_beta.sum(1)
    #print("beta矩阵: \n %r" % beta)
    for i in range(N):
        p_beta[0,i] = Pi[0,i] * B[i,O[0,0]-1] * beta[i,0]
    p = p_beta.sum(1).reshape(1,1)
    #print("观测概率: \n %r" % p[0,0])
    return beta, p[0,0]

def FBAlgoAppli(A,B,Pi,O,I):
    #计算在观测序列和模型参数确定的情况下,某一个隐含状态对应相应的观测状态的概率
    #例题参考李航《统计学习方法》P189习题10.2
    #输入格式:
    #I为二维数组,存放所求概率P(it = qi,O|lambda)中it和qi的角标t和i,即P=[t,i]
    alpha,p1 = ForwardAlgo(A,B,Pi,O)
    beta,p2 = BackwardAlgo(A,B,Pi,O)
    p = alpha[I[0,1]-1,I[0,0]-1] * beta[I[0,1]-1,I[0,0]-1] / p1
    return p

def GetGamma(A,B,Pi,O):
    N = A.shape[0]#数组A的行数
    H = O.shape[1]#数组O的列数
    Gamma = np.zeros((N,H))
    alpha,p1 = ForwardAlgo(A,B,Pi,O)
    beta,p2 = BackwardAlgo(A,B,Pi,O)
    for h in range(H):
        for i in range(N):
            Gamma[i,h] = alpha[i,h] * beta[i,h] / p1
    return Gamma

def GetXi(A,B,Pi,O):
    N = A.shape[0]#数组A的行数
    M = A.shape[1]#数组A的列数
    H = O.shape[1]#数组O的列数
    Xi = np.zeros((H-1,N,M))
    alpha,p1 = ForwardAlgo(A,B,Pi,O)
    beta,p2 = BackwardAlgo(A,B,Pi,O)
    for h in range(H-1):
        for i in range(N):
            for j in range(M):
                Xi[h,i,j] = alpha[i,h] * A[i,j] * B[j,O[0,h+1]-1] * beta[j,h+1] / p1
    #print("Xi矩阵: \n %r" % Xi)
    return Xi

def BaumWelchAlgo(A,B,Pi,O):
    N = A.shape[0]#数组A的行数
    M = A.shape[1]#数组A的列数
    Y = B.shape[1]#数组B的列数
    H = O.shape[1]#数组O的列数
    c = 0
    Gamma = GetGamma(A,B,Pi,O)
    Xi = GetXi(A,B,Pi,O)
    Xi_1 = Xi.sum(0)
    a = np.zeros((N,M))
    b = np.zeros((M,Y))
    pi = np.zeros((1,N))
    a_1 = np.subtract(Gamma.sum(1),Gamma[:,H-1]).reshape(1,N)
    for i in range(N):
        for j in range(M):
            a[i,j] = Xi_1[i,j] / a_1[0,i]
    #print(a)
    for y in range(Y):
        for j in range(M):
            for h in range(H):
                if O[0,h]-1 == y:
                    c = c + Gamma[j,h]
            gamma = Gamma.sum(1).reshape(1,N)
            b[j,y] = c / gamma[0,j]
            c = 0
    #print(b)
    for i in range(N):
        pi[0,i] = Gamma[i,0]
    #print(pi)
    return a,b,pi

def BaumWelchAlgo_n(A,B,Pi,O,n):#计算迭代次数为n的BaumWelch算法
    for i in range(n):
        A,B,Pi = BaumWelchAlgo(A,B,Pi,O)
    return A,B,Pi

def viterbi(A,B,Pi,O):
    N = A.shape[0]#数组A的行数
    M = A.shape[1]#数组A的列数
    H = O.shape[1]#数组O的列数
    Delta = np.zeros((M,H))
    Psi = np.zeros((M,H))
    Delta_1 = np.zeros((N,1))
    I = np.zeros((1,H))

    for i in range(N):
        Delta[i,0] = Pi[0,i] * B[i,O[0,0]-1]

    for h in range(1,H):
        for j in range(M):
            for i in range(N):
                Delta_1[i,0] = Delta[i,h-1] * A[i,j] * B[j,O[0,h]-1]
            Delta[j,h] = np.amax(Delta_1)
            Psi[j,h] = np.argmax(Delta_1)+1
    print("Delta矩阵: \n %r" % Delta)
    print("Psi矩阵: \n %r" % Psi)
    P_best = np.amax(Delta[:,H-1])
    psi = np.argmax(Delta[:,H-1])
    I[0,H-1] = psi + 1
    for h in range(H-1,0,-1):
        I[0,h-1] = Psi[I[0,h]-1,h]
    print("最优路径概率: \n %r" % P_best)
    print("最优路径: \n %r" % I)

Python实现HMM(隐马尔可夫模型)的更多相关文章

  1. HMM隐马尔可夫模型(词语粘合)

    HMM用于自然语言处理(NLP)中文分词,是用来描述一个含有隐含未知参数的马尔可夫过程,其目的是希望通过求解这些隐含的参数来进行实体识别,说简单些也就是起到词语粘合的作用. HMM隐马尔可夫模型包括: ...

  2. HMM隐马尔可夫模型来龙去脉(一)

    目录 隐马尔可夫模型HMM学习导航 一.认识贝叶斯网络 1.概念原理介绍 2.举例解析 二.马尔可夫模型 1.概念原理介绍 2.举例解析 三.隐马尔可夫模型 1.概念原理介绍 2.举例解析 四.隐马尔 ...

  3. HMM隐马尔可夫模型来龙去脉(二)

    目录 前言 预备知识 一.估计问题 1.问题推导 2.前向算法/后向算法 二.序列问题 1.问题推导 2.维特比算法 三.参数估计问题 1.问题推导 2.期望最大化算法(前向后向算法) 总结 前言 H ...

  4. HMM隐马尔科夫模型

    这是一个非常重要的模型,凡是学统计学.机器学习.数据挖掘的人都应该彻底搞懂. python包: hmmlearn 0.2.0 https://github.com/hmmlearn/hmmlearn ...

  5. 机器学习-HMM隐马尔可夫模型-笔记

    HMM定义 1)隐马尔科夫模型 (HMM, Hidden Markov Model) 可用标注问题,在语音识别. NLP .生物信息.模式识别等领域被实践证明是有效的算法. 2)HMM 是关于时序的概 ...

  6. 自然语言处理(1)-HMM隐马尔科夫模型基础概念(一)

    隐马尔科夫模型HMM 序言 文本序列标注是自然语言处理中非常重要的一环,我先接触到的是CRF(条件随机场模型)用于解决相关问题,因此希望能够对CRF有一个全面的理解,但是由于在学习过程中发现一个算法像 ...

  7. HMM 隐马尔科夫模型

    参考如下博客: http://www.52nlp.cn/itenyh%E7%89%88-%E7%94%A8hmm%E5%81%9A%E4%B8%AD%E6%96%87%E5%88%86%E8%AF%8 ...

  8. HMM:隐马尔可夫模型HMM

    http://blog.csdn.net/pipisorry/article/details/50722178 隐马尔可夫模型 隐马尔可夫模型(Hidden Markov Model,HMM)是统计模 ...

  9. 隐马尔可夫模型(Hidden Markov Model)

    隐马尔可夫模型(Hidden Markov Model) 隐马尔可夫模型(Hidden Markov Model, HMM)是一个重要的机器学习模型.直观地说,它可以解决一类这样的问题:有某样事物存在 ...

  10. HMM基本原理及其实现(隐马尔科夫模型)

    HMM(隐马尔科夫模型)基本原理及其实现 HMM基本原理 Markov链:如果一个过程的“将来”仅依赖“现在”而不依赖“过去”,则此过程具有马尔可夫性,或称此过程为马尔可夫过程.马尔可夫链是时间和状态 ...

随机推荐

  1. 02、Quick Start for Windows phone

    在使用这个 SDK 提供的功能前,必须先添加类库的引用到你的工程里.参考: Download and add the libraries to the project. 定义你的 XAML 的 UI ...

  2. 解决在eclipse中配置Tomcat时,出现"Cannot create a server using the selected type"的错误

    比如说使用tomcat 这是因为你之前创建过一次,比如说tomcat6,你指定的目录是:D:/tomcat-6.0.3 后来因为某种原因你把tomcat删了,然后你又安装到了E:/tomcat-6.0 ...

  3. PHP flock() 函数 php中的文件锁定机制

    举一个例子: 假设一个文件读取的过程,有数万人在同时操作,那么极可能a用户刚刚写入,b用户也写入了,两者以至于混乱,或者在读取的时候,读取到别人写的数据.就好比上公共厕所,去厕所的时候要把门给打开上, ...

  4. Linux快速定位并且杀掉占用端口的进程

    1.定位 lsof -i:8811(端口号) 2.杀掉进程 kill -9 63924

  5. MySQL5.0、5.1、5.5、5.6功能进化

    目前线上使用的版本情况:新上线端口统一使用5.5,不说别的,一个快速恢复重启就值回票价. 但因为历史原因还有大量5.1的版本,甚至,I’am sorry,还有少数5.0的版本. 至于5.0以前的版本, ...

  6. 关于Unity中的transform组件(二)

    在Scene视图中的蓝色网格,每一格默认是1米 一.沿着Z轴每秒移动10米 Transform cube_trans; void start(){ this.cube_trans=this.trans ...

  7. 【BZOJ】1641: [Usaco2007 Nov]Cow Hurdles 奶牛跨栏(floyd)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1641 这种水题无意义... #include <cstdio> #include < ...

  8. storm的集群安装与配置

    storm集群安装 机器:(storm及zookeeper都是这3台机器) 192.168.80.20 192.168.80.21 192.168.80.22 须要准备的软件有: zookeeper( ...

  9. redhat7搭建本地yum仓库

    ftp服务器搭建 安装vsftpd yum install vsftpd 配置启动服务和防火墙 修改配置,由于默认不开启主动模式,需要配置开启,在/etc/vsftpd/vsftpd.conf添加如下 ...

  10. 数据库存入数据后id保持不变,或者直接报错

    数据库存入数据后id保持不变,且添加的数据一直在进行覆盖 或者直接报错 数据库存入数据后id保持不变,且添加的数据一直在进行覆盖 原因是: 之前注释掉了loadimage();在该函数中含有建立新的记 ...