python 隐马尔科夫_机器学习算法之——隐马尔可夫(Hidden Markov ModelsHMM)原理及Python实现...

前言

上星期写了Kaggle竞赛的详细介绍及入门指导,但对于真正想要玩这个竞赛的伙伴,机器学习中的相关算法是必不可少的,即使是你不想获得名次和奖牌。那么,从本周开始,我将介绍在Kaggle比赛中的最基本的也是运用最广的机器学习算法,很多项目用这些基本的模型就能解决基础问题了。

1 概述

隐马尔可夫模型(Hidden Markov Model,HMM)是结构最简单的动态贝叶斯网,这是一种著名的有向图模型,主要用于时序数据建模(语音识别、自然语言处理等)。

假设有三个不同的骰子(6面、4面、8面),每次先从三个骰子里选一个,每个骰子选中的概率为1/3,如下图所示,重复上述过程,得到一串数字[1 6 3 5 2 7]。这些可观测变量组成可观测状态链。

同时,在隐马尔可夫模型中还有一条由隐变量组成的隐含状态链,在本例中即骰子的序列。比如得到这串数字骰子的序列可能为[D6 D8 D8 D6 D4 D8]。

隐马尔可夫模型示意图如下所示:

图中,箭头表示变量之间的依赖关系。在任意时刻,观测变量(骰子点数)仅依赖于状态变量(哪类骰子),“观测独立性假设”。

同时,t时刻的状态qt仅依赖于t-1时刻的状态qt-1。这就是马尔可夫链,即系统的下一时刻的状态仅由当前状态决定不依赖以往的任何状态(无记忆性),“齐次马尔可夫性假设”。

2 隐马尔可夫模型三要素

对于一个隐马尔可夫模型,它的所有N个可能的状态的集合Q={q1,q2,…,qN},所有M个可能的观测集合V={v1,v2,…,vM}

隐马尔可夫模型三要素:状态转移概率矩阵A,A=[aij]NxN,aij表示任意时刻t状态qi条件下,下一时刻t+1状态为qj的概率;

观测概率矩阵B,B=[bij]NxM,bij表示任意时刻t状态qi条件下,生成观测值vj的概率;

初始状态概率向量Π,Π=(π1,π2,…,πN),πi表示初始时刻t=1,状态为qi的概率。

一个隐马尔可夫模型可由λ=(A, B, Π)来指代。

3 隐马尔可夫模型的三个基本问题(1) 给定模型λ=(A, B, Π),计算其产生观测序列O={o1,o2,…,oT}的概率P(O|λ);

计算掷出点数163527的概率

(2) 给定模型λ=(A, B, Π)和观测序列O={o1,o2,…,oT},推断能够最大概率产生此观测序列的状态序列I={i1,i2,…,iT},即使P(I|O)最大的I;

推断掷出点数163527的骰子种类

(3) 给定观测序列O={o1,o2,…,oT},估计模型λ=(A, B, Π)的参数,使P(O|λ)最大;

已知骰子有几种,不知道骰子的种类,根据多次掷出骰子的结果,反推出骰子的种类

这三个基本问题在现实应用中非常重要,例如根据观测序列O={o1,o2,…,oT-1,}推测当前时刻最有可能出现的观测值OT,这就转换成基本问题(1);

在语音识别中,观测值为语音信号,隐藏状态为文字,根据观测信号推断最有可能的状态序列,即基本问题(2);

在大多数应用中,人工指定参数模型已变得越来越不可行,如何根据训练样本学得最优参数模型,就是基本问题(3)。

4 三个基本问题的解法

基于两个条件独立假设,隐马尔可夫模型的这三个基本问题均能被高效求解。

4.1 基本问题(1)解法

4.1.1 直接计算法(概念上可行,计算上不可行)

通过列举所有可能的长度为T的状态序列I=(i1,i2,…,iT),求各个状态序列I与观测序列O同时出现的联合概率P(I,O|λ),然后对所有可能求和得到P(O|λ)。

状态序列I=(i1,i2,…,iT)的概率是P(I|λ)=π1a12a23…a(T-1)T

对于固定状态序列 I,观测序O=(o1,o2,…,oT)的概率P(O|I,λ)= b11b22…bTT

I 和 O同时出现的联合概率P(I,O|λ)= P(I|λ) P(O|I,λ)

然后对所有可能的 I 求和,得到P(O|λ)

这种方法计算量很大,算法不可行。

4.1.2 前向算法(forward algorithm)(t=1,一步一步向前计算)

前向概率αt(i)= P(o1,o2,…,ot,ii=qi|λ),表示模型λ,时刻 t,观测序列为o1,o2,…,ot且状态为qi的概率。

(1) 初始化前向概率

状态为qi和观测值为o1的联合概率 α1(i)=π1bi1

(2) 递推t=1,2,…,T-1

根据下图,得到αt+1(i)= [Σj=1,…,Nαt(j)αji]bi(t+1)

(3) 终止 P(O|λ) = Σi=1,…,NαT(i)

前向算法高效的关键是其局部计算前向概率,根据路径结构,如下图所示,每次计算直接利用前一时刻计算结果,避免重复计算,减少计算量。

4.1.3 后向算法(backward algorithm)

后向概率βt(i) = P(o1,o2,…,ot,ii=qi|λ),表示模型λ,时刻t,从t+1到时刻T观测序列o1,o2,…,ot且状态为qi的概率。

(1)初始化后向概率

对最终时刻的所有状态为qi规定βT(i) = 1。

(2)递推t=T-1,T-2,…,1

根据下图,计算t时刻状态为qi,且时刻t+1之后的观测序列为ot+1,ot+2,…,ot的后向概率。只需考虑t+1时刻所有可能的N个状态qi的转移概率(transition probability) αij,以及在此状态下的观测概率bi(t+1),然后考虑qj之后的后向概率βt+1(j),得到

βt(i) = Σj=1,…,Nβt+1(j)αijbi(t+1).

​(3) 终止 P(O|λ) = Σi=1,…,Nβ1(i)πibi1

前向算法高效的关键是其局部计算前向概率,根据路径结构,如下图所示,每次计算直接利用前一时刻计算结果,避免重复计算,减少计算量。

4.2 基本问题(2)解法

4.2.1 近似算法

选择每一时刻最有可能出现的状态,从而得到一个状态序列。这个方法计算简单,但是不能保证整个状态序列的出现概率最大。因为可能出现转移概率为0的相邻状态。

4.2.2 Viterbi算法

使用动态规划求解概率最大(最优)路径。t=1时刻开始,递推地计算在时刻t状态为i的各条部分路径的最大概率,直到计算到时刻T,状态为i的各条路径的最大概率,时刻T的最大概率即为最优路径的概率,最优路径的节点也同时得到。

如果还不明白,看一下李航《统计学习方法》的186-187页的例题就能明白算法的原理。

状态[3 3 3]极为概率最大路径。

4.3 基本问题(3)解法

4.3.1 监督学习方法

给定S个长度相同的(观测序列,状态序列)作为训练集(O1,I1),O2,I3),…, (Os,Is)使用极大似然估计法来估计模型参数。

转移概率αij 的估计:样本中t时刻处于状态i,t+1时刻转移到状态j的频数为αij,则

观测概率bij和初始状态概率πi的估计类似。

4.3.2 Baum-Welch算法

使用EM算法得到模型参数估计式

EM算法是常用的估计参数隐变量的利器,它是一种迭代方法,基本思想是:

(1) 选择模型参数初始值;

(2) (E步)根据给定的观测数据和模型参数,求隐变量的期望;

(3) (M步)根据已得隐变量期望和观测数据,对模型参数做极大似然估计,得到新的模型参数,重复第二步。

5 Python代码实现

5.1 Baum-Welch算法的程序实现

这里提供Baum-Welch算法的代码实现。

本来打算试一下用自己写的HMM跑一下中文分词,但很可惜,代码运行的比较慢。所以改成 模拟 三角波 以及 正弦波。

# encoding=utf8

import numpy as np

import csv

class HMM(object):

def __init__(self,N,M):

self.A = np.zeros((N,N)) # 状态转移概率矩阵

self.B = np.zeros((N,M)) # 观测概率矩阵

self.Pi = np.array([1.0/N]*N) # 初始状态概率矩阵

self.N = N # 可能的状态数

self.M = M # 可能的观测数

def cal_probality(self, O):

self.T = len(O)

self.O = O

self.forward()

return sum(self.alpha[self.T-1])

def forward(self):

"""前向算法"""

self.alpha = np.zeros((self.T,self.N))

# 公式 10.15

for i in range(self.N):

self.alpha[0][i] = self.Pi[i]*self.B[i][self.O[0]]

# 公式10.16

for t in range(1,self.T):

for i in range(self.N):

sum = 0

for j in range(self.N):

sum += self.alpha[t-1][j]*self.A[j][i]

self.alpha[t][i] = sum * self.B[i][self.O[t]]

def backward(self):

"""后向算法"""

self.beta = np.zeros((self.T,self.N))

# 公式10.19

for i in range(self.N):

self.beta[self.T-1][i] = 1

# 公式10.20

for t in range(self.T-2,-1,-1):

for i in range(self.N):

for j in range(self.N):

self.beta[t][i] += self.A[i][j]*self.B[j][self.O[t+1]]*self.beta[t+1][j]

def cal_gamma(self, i, t):

"""公式 10.24"""

numerator = self.alpha[t][i]*self.beta[t][i]

denominator = 0

for j in range(self.N):

denominator += self.alpha[t][j]*self.beta[t][j]

return numerator/denominator

def cal_ksi(self, i, j, t):

"""公式 10.26"""

numerator = self.alpha[t][i]*self.A[i][j]*self.B[j][self.O[t+1]]*self.beta[t+1][j]

denominator = 0

for i in range(self.N):

for j in range(self.N):

denominator += self.alpha[t][i]*self.A[i][j]*self.B[j][self.O[t+1]]*self.beta[t+1][j]

return numerator/denominator

def init(self):

"""随机生成 A,B,Pi并保证每行相加等于 1"""

import random

for i in range(self.N):

randomlist = [random.randint(0,100) for t in range(self.N)]

Sum = sum(randomlist)

for j in range(self.N):

self.A[i][j] = randomlist[j]/Sum

for i in range(self.N):

randomlist = [random.randint(0,100) for t in range(self.M)]

Sum = sum(randomlist)

for j in range(self.M):

self.B[i][j] = randomlist[j]/Sum

def train(self, O, MaxSteps = 100):

self.T = len(O)

self.O = O

# 初始化

self.init()

step = 0

# 递推

while step

step+=1

print(step)

tmp_A = np.zeros((self.N,self.N))

tmp_B = np.zeros((self.N,self.M))

tmp_pi = np.array([0.0]*self.N)

self.forward()

self.backward()

# a_{ij}

for i in range(self.N):

for j in range(self.N):

numerator=0.0

denominator=0.0

for t in range(self.T-1):

numerator += self.cal_ksi(i,j,t)

denominator += self.cal_gamma(i,t)

tmp_A[i][j] = numerator/denominator

# b_{jk}

for j in range(self.N):

for k in range(self.M):

numerator = 0.0

denominator = 0.0

for t in range(self.T):

if k == self.O[t]:

numerator += self.cal_gamma(j,t)

denominator += self.cal_gamma(j,t)

tmp_B[j][k] = numerator / denominator

# pi_i

for i in range(self.N):

tmp_pi[i] = self.cal_gamma(i,0)

self.A = tmp_A

self.B = tmp_B

self.Pi = tmp_pi

def generate(self, length):

import random

I = []

# start

ran = random.randint(0,1000)/1000.0

i = 0

while self.Pi[i]

ran -= self.Pi[i]

i += 1

I.append(i)

# 生成状态序列

for i in range(1,length):

last = I[-1]

ran = random.randint(0, 1000) / 1000.0

i = 0

while self.A[last][i] < ran or self.A[last][i]<0.0001:

ran -= self.A[last][i]

i += 1

I.append(i)

# 生成观测序列

Y = []

for i in range(length):

k = 0

ran = random.randint(0, 1000) / 1000.0

while self.B[I[i]][k] < ran or self.B[I[i]][k]<0.0001:

ran -= self.B[I[i]][k]

k += 1

Y.append(k)

return Y

def triangle(length):

'''三角波'''

X = [i for i in range(length)]

Y = []

for x in X:

x = x % 6

if x <= 3:

Y.append(x)

else:

Y.append(6-x)

return X,Y

def show_data(x,y):

import matplotlib.pyplot as plt

plt.plot(x, y, 'g')

plt.show()

return y

if __name__ == '__main__':

hmm = HMM(10,4)

tri_x, tri_y = triangle(20)

hmm.train(tri_y)

y = hmm.generate(100)

print(y)

x = [i for i in range(100)]

show_data(x,y)

5.2 运行结果

5.2.1 三角波

三角波比较简单,我设置N=10,扔进去长度为20的序列,训练100次,下图是其生成的长度为100的序列。

可以看出效果还是很不错的。

5.2.2 正弦波

正弦波有些麻烦,因为观测序列不能太大,所以我设置N=15,M=100,扔进去长度为40的序列,训练100次,训练的非常慢,下图是其生成的长度为100的序列。

可以看出效果一般,如果改成C的代码,并增加N应该是能模拟的。

6 HMM的实际应用举例

参考文献:

[2]《机器学习》周志华

[3]《统计学习方法》李航

延伸阅读:

△长按关注「迈微电子研发社」

△长按加入「迈微电子研发社」学习辅导群

给个关注么么哒!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/532022.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java编程50_java经典50编程题(1-10)

1.有一对兔子从出生后第三个月起&#xff0c;每个月都生一对小兔子&#xff0c;小兔子长到三个月后每个月又生一对兔子&#xff0c;假设兔子不死亡&#xff0c;问每个月兔子的总数为多少&#xff1f;分析过程图片发自简书App示例代码图片发自简书App运行结果图片发自简书App反思…

python替代hadoop_Python连接Hadoop数据中遇到的各种坑(汇总)

最近准备使用PythonHadoopPandas进行一些深度的分析与机器学习相关工作。(当然随着学习过程的进展&#xff0c;现在准备使用PythonSparkHadoop这样一套体系来搭建后续的工作环境)&#xff0c;当然这是后话。但是这项工作首要条件就是将Python与Hadoop进行打通&#xff0c;本来认…

java 自动化测试_java写一个自动化测试

你模仿购物车试一下&#xff0c;同样是买东西&#xff0c;加上胜负平的赔率&#xff0c;输出改下应该就可以了package com.homework.lhh;import java.util.ArrayList;import java.util.Comparator;import java.util.Scanner;public class Ex04 {public static void main(String…

超大规模集成电路_纳米级超大规模集成电路芯片低功耗物理设计分析(二)

文 | 大顺简要介绍了功耗的组成&#xff0c;在此基础上从工艺、电路、门、系统四个层面探讨了纳米级超大规模集成电路的低功耗物理设计方法。关键词&#xff1a;纳米级&#xff1b;超大规模集成电路&#xff1b;电路芯片&#xff1b;电路设计02纳米级超大规模集成电路芯片低功耗…

java中的printnb_javaI/O系统笔记

1、File类File类的名字有一定的误导性&#xff1b;我们可能认为它指代的是文件&#xff0c;实际上却并非如此。它既能代表一个特定文件的名称&#xff0c;又能代表一个目录下的一组文件的名称。1.1、目录列表器如果需要查看目录列表&#xff0c;可以通过file.list(FilenameFilt…

outlook反应慢的原因_保险管怎么区分慢熔和快熔?

保险丝快熔与慢熔的区别所有双帽;对于这样的产品特性和安全性熔丝; gG的”&#xff0c;即&#xff0c;与接触帽组合接触;即&#xff0c;所述双(内/外盖)的盖。和一般的小型或地下加工厂&#xff0c;以便执行切割角&#xff0c;降低生产成本&#xff0c;这将选择单个帽铆接“单&…

java成员内部类_Java中的内部类(二)成员内部类

Java中的成员内部类(实例内部类)&#xff1a;相当于类中的一个成员变量&#xff0c;下面通过一个例子来观察成员内部类的特点public classOuter {//定义一个实例变量和一个静态变量private inta;private static intb;//定义一个静态方法和一个非静态方法public static voidsay(…

word 通配符_学会Word通配符,可以帮助我们批量处理好多事情

长文档需要批量修改或删除某些内容的时候&#xff0c;我们可以利用Word中的通配符来搞定这一切&#xff0c;当然&#xff0c;前提是你必须会使用它。通配符的功能非常强大&#xff0c;能够随意组合替换或删除我们定义的规则内容&#xff0c;下面易老师就分享一些关于查找替换通…

java存储键值结构_java-键值存储为主数据库

我将要开始一个项目,该项目的读写操作非常频繁且频繁.因此,环顾四周,我发现内存数据库正是为此目的而创建的.经过更多调查后,我进入了redis.Redis看起来很酷(虽然刚开始阅读,但是对此有很多了解).但是我主要只看过关系数据库,并且以元组和关系的方式来考虑数据(我认为我可以随着…

python 输入文件名查找_python 查找文件名包含指定字符串的方法

编写一个程序&#xff0c;能在当前目录以及当前目录的所有子目录下查找文件名包含指定字符串的文件&#xff0c;并打印出绝对路径。import osclass searchfile(object):def __init__(self,path.):self._pathpathself.abspathos.path.abspath(self._path) # 默认当前目录def fin…

java 运行 出现选择_Eclipse 运行出现java.lang.NoClassDefFoundError的解决方法

上篇博文也提到了这个问题&#xff0c;但没有深入的讲解。这次特意做了整理&#xff0c;详细解释其原因。先看错误java.lang.NoClassDefFoundError&#xff0c;显然是java虚拟机找不到指定的类&#xff0c;多数情况下是外部jar中的类。Eclipse的自动化&#xff0c;集成化&#…

设置熄屏_刚买的手机微信收不到信息提醒耽误事情,手机到手一定要这样设置...

手机使用过程中经常会遇到第三方软件接收不到信息提醒的状况&#xff0c;常常因此耽误了很多重要的事情&#xff0c;造成损失。特别是刚换新手机或者手机刚升级系统时发生的最多。一般都觉得是手机问题&#xff0c;其实只是手机的系统设置出现了问题&#xff0c;只要跟我按照以…

java判断对称素数_SM2非对称算法的原理及实现 Java SM2的代码案例 | 一生孤注掷温柔 | 小奋斗...

SM2椭圆曲线公钥密码算法&#xff1a;我国自主知识产权的商用密码算法&#xff0c;是ECC(Elliptic Curve Cryptosystem)算法的一种&#xff0c;基于椭圆曲线离散对数问题&#xff0c;计算复杂度是指数级&#xff0c;求解难度较大&#xff0c;同等安全程度要求下&#xff0c;椭圆…

multipartfile 获取音频时长_抖音音频下载捷径:一键提取音频,安卓+ios全通用,完全免费...

本文相关&#xff1a;抖音音频提取、抖音音频快捷指令、捷径怎么获取抖音音乐…昨天有抖友分享了一个抖音短视频链接&#xff0c;告诉我&#xff0c;她很喜欢这个视频里的歌曲&#xff0c;但是在很多歌曲app上面却找不到相同的版本&#xff0c;然后就问我&#xff0c;有没有什么…

python可以做特效吗_学习mel语言,Python,JavaScript到什么程度才能做一下大型特效,要自已开发插件脚本呢?...

感谢邀请。首先自己要在某一方面要擅长&#xff0c;认准一个定位。比如android是钥匙做前端应用软件的&#xff0c;python可以做爬虫及其人工智能&#xff0c;js做全段网页&#xff0c;java主要是做后端的1、我们程序员对于开发软件来说&#xff0c;无论你选择的是那种语言&…

POJ2513-Colored Sticks

/*思路&#xff1a;类似图论中“一笔画”问题&#xff0c;两根木棒的相连接的端点是一样的颜色&#xff0c;&#xff08;a,b&#xff09;--(b,c)--(c, d)....方法&#xff1a;trie树并查集&#xff0c; 利用trie树建立字符串和某一个节点的映射&#xff0c;并将这些和字符串构成…

php windows共享内存,给PHP开启shmop扩展实现共享内存

这篇文章主要介绍了关于给PHP开启shmop扩展实现共享内存&#xff0c;有着一定的参考价值&#xff0c;现在分享给大家&#xff0c;有需要的朋友可以参考一下在项目开发中&#xff0c;想要实现PHP多个进程之间共享数据的功能&#xff0c;让客户端连接能够共享一个状态&#xff0c…

导入ansys的实体怎么进行parameter_ANSYS在线缆线束设计中的仿真应用

ANSYS采用ANSYS Maxwell、Q3D、Twin Builder等电磁仿真软件&#xff0c;从线缆线束设计、寄生参数RLCG提取、到系统电磁兼容提供了全面仿真分析。创建模型ANSYS在Maxwell软件基础上提出针对用户定制化的“线缆线束设计工具包”&#xff0c;帮助客户参数化建立特定几何模型&…

怎么做95置信区间图_这种动态的OD图怎么做?简单3步快速搞定

之前在视频号中发过一个单车的出行数据可视化效果。动态展示了某天单车不同时段的运行情况&#xff0c;这种动态的OD可视化效果是如何制作的呢&#xff1f;使用的是kepler.gl进行制作的&#xff0c;其实非常简单&#xff0c;3步即可快速搞定。一、数据软件准备1、软件制作这种动…

php抖音跳转地址,PHP如何实现解析抖音无水印视频

问题来源很多时候你在douyin里看到了一个短视频&#xff0c;想复制下来自己编辑文字来发布&#xff0c;可是视频里的水印却是原者的。这个时候你想把水印去掉&#xff0c;你要如何做呢&#xff1f;这里提供PHP实现去除水印的主要方法&#xff0c;其实很简单。使用方法&#xff…