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. 关于液晶显示器的6bit面板、8bit面板及E-IPS(转)

    原文:http://bbs.3dmgame.com/thread-2232447-1-1.html              1.什么是6bit面板.8bit面板         众所周知,液晶显示器 ...

  2. 一定要使用-server参数来调试并发程序

    在阅读JCIP的时候想手工测试一下,结果发现总是没有出现书中描述的并发问题 后来我琢磨,以前记得书上说过,在debugging的环境下,JVM是低并发的,一定要在server的环境下测试,让JVM在高 ...

  3. ubuntu下刷新dns

    也是一条命令就可以:sudo /etc/init.d/dns-clean start

  4. oracle 远程登录

    打开命令终端,输入命令:sqlplus /nolog 输入命令:conn sys/sys@dba as sysdba

  5. [C++]红色波浪线是什么意思

    相关资料:https://zhidao.baidu.com/question/242005953.html 问题现象:在写C++代码时,写的注释都是红色波浪线. 问题原因:波浪线表示 词语拼写错误 字 ...

  6. asp.net treeview 总结

    网上关于Treeview的代码虽然多 但是都是很乱 实用性和正确性也不是很好 只好自己写一套了,时间比较紧张 性能可能还需调整 以用户组织的一个实际例子来讲诉Treeview的用法吧 组织表结构: 用 ...

  7. Toad 所有 菜单说明(太多)

    菜单说明 新版本 toad 软件中, 比较有用的菜单 session 菜单    Session Information: 显示当前session的用户的情况, 比如权限, 授权等 Database ...

  8. 关于Unity的C#基础学习(五)

    一.get/set访问器 class Person{ int my_age; //默认私有权限 int sex; //属性,类似于函数,但是又不是函数的东西 public int age{ get{ ...

  9. 【转】【Linux】grep命令详解

    简介 grep (global search regular expression(RE) and print out the line,全面搜索正则表达式并把行打印出来)是一种强大的文本搜索工具,它 ...

  10. Merging an upstream repository into your fork

    1. Check out the branch you wish to merge to. Usually, you will merge into master. $ git checkout ma ...