【火炉炼AI】机器学习044-创建隐马尔科夫模型

【火炉炼AI】机器学习044-创建隐马尔科夫模型

(本文所使用的Python库和版本号: Python 3.6, Numpy 1.14, scikit-learn 0.19, matplotlib 2.2 )

隐马尔科夫模型(Hidden Markov Model, HMM)是非常经典的机器学习模型,在语音识别,自然语言处理,模式识别等领域中有着非常广泛的应用。故而理解和熟练掌握HMM是机器学习领域中一项重要的技能。


1. 什么是隐马尔科夫模型

隐马尔科夫模型是统计模型,用来描述一个含有隐含未知参数的马尔科夫过程,其难点是从可观察参数确定过程中的隐含参数,然后利用这些参数做进一步的分析。

隐马尔科夫模型是关于时序的概率模型,描述一个由隐藏的马尔科夫链随机生成不可观测的状态随机序列,再由各个状态生成一个可观测的观测随机序列的过程。

1.1 马尔科夫过程

什么是马尔科夫过程?一种无记忆的随机过程,注意三个时间点:过去,现在,将来,(或者说:昨天,今天,明天)。在已知现在的状态的情况下,将来会处于哪种状态只取决于已知的现在状态,而与过去的状态无关。即,已知“现在”的状态情况下,“将来”的状态和“过去”的状态是相互独立的,这种特性成为马尔科夫性,具有这种性质的随机过程叫做马尔科夫过程,也称为马尔科夫链。

举个例子:荷花池中一只青蛙在跳动,从一片荷叶跳到下一片上。假设青蛙跳过的荷叶编号为X0,X1,X2,....Xn,且现在青蛙处于Xn这片荷叶上,那么青蛙下一刻要跳到哪片荷叶完全是由现在的荷叶Xn来决定的,而与X0,X1....Xn-1这些荷叶无关。这一过程可以认为是马尔科夫过程。

所以马尔科夫链有很强的时间观念,在t0时刻,青蛙位于X0荷叶上,t1时刻,青蛙位于X1荷叶上。

1.2 一个链条,两个序列,三个概率

在马尔科夫模型中,有六个参数非常重要,一个链条是指一个马尔科夫过程,由时间来连接整个链条。两个序列是每个时间点(链条上的每个节点)都具有两个值,由这两个值组成的链条(或序列):

1)状态序列(State Sequence): 由隐藏的马尔科夫链生成的状态的序列,当前处于某种状态,由这些状态组成的序列。

2)观测序列(Observation Sequence):很多情况下,状态并不能完全被我们所看到,我们只能看到某一个状态所表现出来的结果,这些结果可以被我们观测到,故而这些观测结果所组成的序列就是观测序列,它能部分地代表状态序列,但是不能完整的展现状态序列。

三个概率是指马尔科夫链往下推动的动力,代表这链条走势的方向。

1)初始状态概率:表示最开始时刻处于各个状态的概率。

2)状态转移概率:tn时刻的状态转移到下一个tn+1时刻状态的概率。

3)观测概率:如果将马尔科夫链看成一个矩阵,每一行代表每一个时刻,从上往下都是按照时间来排列的,每一行就代表某一个时刻,也对应于某一个状态值,每一列就是这个状态下的观测值,观测概率就是在某状态下,出现某个观测值的概率。

还有两个基本假设:

1)齐次马尔科夫假设:隐马尔科夫链在任意时刻T的状态值,只依赖于他前一个时刻的状态值,而与其他的状态值和观测值无关,这也是隐马尔科夫的特点。

2)观测独立性假设:任意时刻的观测值只依赖于该时刻的状态,而与其他的观测值和其他状态无关。

有一篇博文讲的非常好,有助于对隐马尔科夫模型的理解,该博文是:一文搞懂HMM(隐马尔可夫模型)

下面我借用这篇博文中的图片和例子来说明一下上述几个关键参数。

1.3 举例说明

假如有三个不同骰子,如下图所示,第一个为平时我们所见的正方体型,有六个面,标号为D6,每掷一次可以得到1-6中的任意一个数字,每个数字的概率为1/6.第二个为四面体,标号为D4,会得到1-4中的任意一个数字,概率为1/4,第三个为八面体,标号D8,会得到1-8中的任意一个数字,概率为1/8.

现在我们从这三个骰子中随机选择一个,然后每个骰子掷一次,得到一个数字,一共选10词,掷10次。那么我们可能选择的骰子序列是:D6 D8 D8 D6 D4 D8 D6 D6 D4 D8,得到的数字可能是:1 6 3 5 2 7 3 5 2 4。在每一次时,我们选择的骰子组成的序列就是状态序列,如上的(D6 D8 D8 D6 D4 D8 D6 D6 D4 D8),而由该骰子得到的数字就是我们的观测值,组成的就是观测序列(如数字1 6 3 5 2 7 3 5 2 4)。

很明显,观测序列是我们可以轻而易举得到的,也是很容易观察到的,故而有时也称为可见状态链,而很多时候,我们不知道选用的是哪个骰子,所以骰子这个状态序列就是一个隐藏的序列,也被称为隐含状态链。

隐马尔科夫模型中所说的马尔科夫链其实就是隐含状态链。

上面讲到的初始状态概率就是最开始我们选择某个骰子的概率,比如此处我们随机选择,那么初始状态概率就是1/3。

状态转移概率就是从一个骰子到下一个骰子的转换概率,也就是,这一次我们用的是D4骰子,那么下一次我们应该选择哪个骰子?此处我们是随机选择,故而下一个骰子是D4,D6,D8的概率都是1/3,假如我们修改一下规则,比如D6后面不能选择D4,且D6后面再选择D6的概率是0.9,D8的概率是0.1,那么此时的状态转移概率就是(0,0.9,0.1)。由于规则改变,故而状态转移概率也发生改变,得到的就是另外一个全新的HMM。需要注意的是,状态转移概率只存在于状态之间的转移过程中,而不存在观测值的改变过程中。

观测概率是指,某一个状态下得到某个观测值的概率,这个例子中,加入当前骰子是D6,那么正常情况下,此处我们得到的观测值是1-6中的任意一个数字,每个数字的概率为1/6,那么此时状态的观测概率就是每个都是1/6。假如有人出老千,对D6骰子动过手脚,比如使得得到1的概率是1/2,其他数字(2-6)的概率都是1/10,那么此时的观测概率也发生改变,也是一个全新的HMM模型。

用图表示为:注意图中的隐含状态链就是状态序列,可见状态组成的链条就是观测序列,黑色箭头表示的从一个隐含状态到下一个银行状态的转换的概率就是状态转移概率,红色箭头表示的从一个隐含状态到一个观测值的输出概率就是观测概率。

对于完全随机的选择骰子的过程,那么下一次选择骰子的概率都是1/3,这种状态转移概率可以表示为:

1.4 算法种类

在真实世界中,很多时候我们并不能知道所有的上述五个参数值,只知道其中的某几个,那么这些问题的解决方式也各种各样,下面主要讲解和HMM模型有关的三类算法,分别解决三种问题。

1)已知状态数量,状态转移概率,观测序列,求解状态序列。--解码问题,用维特比算法解决。

这类问题就是:假如我知道有哪几种骰子(状态数量),每种骰子后面应该怎么选择下一个骰子(状态转移概率),也知道骰子掷出来的结果(观测序列),比如掷出来的一系列结果是(数字1 6 3 5 2 7 3 5 2 4),那么我们怎么知道每一个步骤都是哪个骰子掷出来的(求解状态序列),比如数字1是D4,D6,D8哪个骰子的的来的?

这个问题在语音识别领域叫做解码问题,有两种解法,第一种解法是求最大似然状态路径,也就是,求解一串骰子序列,这串骰子序列产生的观测结果(即得到1 6 3 5 2 7 3 5 2 4这一串数字)的概率最大。第二种解法是,求每次掷出的骰子分别是某种骰子的概率,比如可以求得第一次掷D4的概率是0.5,D6的概率是0.3,D8的概率是0.2.具体的解法过程请参考其他博文。

2)已知状态数量,状态转移概率,观测序列,求解得到某个观测值的概率。 --概率问题,向前向后算法。

这类问题和上面类似,但是这次我们想知道在某一个时刻得到某个数字的概率。这个问题的求解貌似没有太多意义,因为得到的只是某个数字的概率,而不是真正的这个数字,但是在赌彩,股价预测等方面,却又很大的实用价值,如果在赌彩中知道下一刻某个数字出现的概率,或者在股价运行上,知道下一个交易日出现某个价格的概率,那么我们就能在这个上面赚大把银子。

3)已知状态数量,观测序列,求解状态转移概率。--学习问题,B-W算法。

这是最常见的情况,很多时候我们只知道有几个骰子,并且掷这些骰子得到了很多数字,那么怎么计算每种骰子后面该怎么选择下一个骰子了?

关于这些问题的解法,请参考博文: 一文搞懂HMM(隐马尔可夫模型)


2. 创建HMM模型

隐马尔科夫模型是一个生成模型,也就意味着一旦掌握了其底层结构,就可以产生数据。

2.1. 查看数据集序列

本次构建HMM模型的数据都存放在data_hmm.txt中,首先我们加载这个数据集到内存中来。这个数据集前两列是日期,第三列是我们所要分析的序列数据。对这列数据绘图可以看出其内在逻辑和结构,如下:

本数据集的时序数据绘图结果

2.2. 构建HMM模型

构建HMM模型的代码为:

dataset_X=df.iloc[:,2].values.reshape(1,-1).T # 前面两列是日期,用第2列作数据集
# 需要构建成二维数组形式,故而需要加上一个轴
print(dataset_X.shape) # 有3312个训练样本组成一列

# 建立HMM模型,并训练
from hmmlearn.hmm import GaussianHMM
model = GaussianHMM(n_components=4, covariance_type="diag", n_iter=1000)
model.fit(dataset_X)

由于此处我们只有一列数据,故而需要对这列数据准备一下,将其转变为二维数组,此处得到(3312,1)的二维数组,每一行就是一个观测值,构成了一个观测序列。

这个代码中用到了hmmlearn模块,这个模块需要自己通过pip install hmmlearn来安装。这个模块实现了三种HMM模型类,按照观测状态是连续状态还是离散状态,可以分为两类,GaussianHMM和GMMHMM是连续观测状态的HMM模型,而MultinomialHMM是离散观测状态的模型

本项目第一,二列代表时间,且可以看出时间上是连续的,故而使用GaussianHMM模型,这个模型假设观测状态符合高斯分布,而GMMHMM类则假设观测状态符合混合高斯分布。一般我们使用GaussianHMM即可。

GuassianHMM的几个参数:

1,n_components: 状态数量,默认为1。

2,covariance_type:有四种: spherical: 在每个状态下,观测值的所有特性分量都使用相同的方差值,对应协方差矩阵的非对角为0,对角值相等,即球面特性,这是最简单的高斯分布PDF。diag: 在每个状态下,观测值使用对角协方差矩阵,该矩阵非对角为0,对角值不相等,是covariance_type的默认参数。full:在每个状态下,观测值使用完全协方差矩阵,里面的元素都为非零。tied: 指所有的状态使用相同的完全协方差矩阵。这四种PDF类型里面,spherical, diag和full代表三种不同的高斯分布概率密度函数,而tied则可以看作是GaussianHMM和GMMHMM的特有实现。其中,full是最强大的,但是需要足够多的数据来做合理的参数估计;spherical是最简单的,通常用在数据不足或者硬件平台性能有限的情况之下;而diag则是这两者一个折中。在使用的时候,需要根据可观察态向量不同特性的相关性来选择合适的类型。

3,n_iter: 最大循环次数。

还有一些其他参数,但是只有上面三个参数最重要,其他可以用默认值即可。

对上面的HMM模型进行训练之后,可以查看该模型内部的一些结构和内容,比如:计算每一个隐含状态的均值和方差:

for i in range(model.n_components): # 打印出每个隐含状态
    mean=model.means_[i][0]
    variance=np.diag(model.covars_[i])[0]
    print('Hidden state: {}, Mean={:.3f}, Variance={:.3f}'
          .format((i+1),mean,variance))

-------------------------------------输---------出--------------------------------

Hidden state: 1, Mean=5.092, Variance=0.677
Hidden state: 2, Mean=2.601, Variance=0.257
Hidden state: 3, Mean=8.099, Variance=0.678
Hidden state: 4, Mean=0.600, Variance=0.254

--------------------------------------------完-------------------------------------

2.3. 查看HMM模型的预测效果

HMM模型是生成模型,我们训练这个模型的最终目的是希望它能够预测未来某个时点的值,对于这个模型,我们预测出1000个数据点,看看模型的效果如何。

# 使用HMM模型生成数据
N=1000
samples,_=model.sample(N)
plt.plot(samples[:,0])

HMM模型生成数据的序列图

2.4. 提升HMM模型的性能

上面可以看出,这个模型生成的数据的序列图和原始的数据序列图相差甚远,虽然有点起伏波动貌似一致,但是还难以满足我们需要,我们需要的是两者的相似度越大越好,越大表示HMM模型已经找到了数据的变动规律。

# 模型的提升,修改n_components

for i in [8,12,16,18,20]:
    model = GaussianHMM(n_components=i, covariance_type="diag", n_iter=1000)
    model.fit(dataset_X)
    samples,_=model.sample(1000)
    plt.plot(samples[:,0])
    plt.title('hidden state N={}'.format(i))
    plt.show()

运行后会得到五副图,可以看我的源代码,此处只贴出最后一幅的图片:

隐含状态为20时HMM模型的预测结果

可以看出,随着隐含状态越大,得到的预测结果图和原始序列图的相似度越大,表明HMM模型越准确。

########################小**********结###############################

1,HMM模型的构建和训练很简单,直接使用hmmlearn模块中的GuassianHMM函数即可,其训练时只需要时序数据即可。

2,比较难的是理解HMM模型,主要理解隐马尔科夫链,两种序列,三种概率,就能有个大概了解。

3,HMM模型中GuassianHMM函数的参数优化空间不大,只是优化隐含状态数即可,从图中看出,即使隐含状态数比较大得到的结果也不是很理想,此时需要用深度学习里面的RNN或LSTM来得到准确率更高的模型。

#################################################################


注:本部分代码已经全部上传到(我的github)上,欢迎下载。

参考资料:

1, Python机器学习经典实例,Prateek Joshi著,陶俊杰,陈小莉译

posted on 2018-10-19 14:33  科技老丁哥  阅读(737)  评论(0编辑  收藏  举报

导航