机器学习之隐马尔科夫模型(HMM)

机器学习之隐马尔科夫模型(HMM)

  • 1、隐马尔科夫模型介绍
  • 2、隐马尔科夫数学原理
  • 3、Python代码实现隐马尔科夫模型
  • 4、总结

隐马尔可夫模型介绍

马尔科夫模型(hidden Markov model,HMM)是关于时序的概率模型,描述由一个隐藏的马尔科夫随机生成不可观测的状态随机序列,再由各个状态生成一个观测从而产生观测随机序列的过程,属于一个生成模型。

下面我们来从概率学角度定义马尔科夫模型,从一个典型例子开始:

假设有4个盒子,每个盒子里面有不同数量的红、白两种颜色的球,具体如下表:

盒子编号 1 2 3 4
红球数 5 3 6 8
白球数 5 7 4 2

现在从这些盒子中取出T个球,取样规则为每次选择一个盒子取出一个球,记录其颜色,放回。在这个过程中,我们只能观测到球的颜色的序列,观测不到球是从哪个盒子中取出来的,即观测不到盒子的序列,这里有两个随机序列,一个是盒子的序列(状态序列),一个是球的颜色的观测序列(观测序列),前者是隐藏的,只有后者是可观测的。这里就构成了一个马尔科夫的例子。
定义是所有的可能的状态集合,V是所有的可能的观测的集合:

其中,N是可能的状态数,M是可能的观测数,例如上例中N=4,M=2。

是长度为T的状态序列,是对应的观测序列:

A是状态转移概率矩阵:

其中, 是指在时刻处于状态的条件下在时刻转移到状态的概率。

B是观测概率矩阵:

其中, 是指在时刻处于状态的条件下生成观测的概率。

是初始状态概率向量:

其中, 是指在时刻=1处于状态的概率。

由此可得到,隐马尔可夫模型的三元符号表示,即

称为隐马尔可夫模型的三要素。

由定义可知隐马尔可夫模型做了两个基本假设:

(1)齐次马尔科夫性假设,即假设隐藏的马尔科夫链在任意时刻的状态只和-1状态有关;

(2)观测独立性假设,观测只和当前时刻状态有关;

仍以上面的盒子取球为例,假设我们定义盒子和球模型:

  • 状态集合: = {盒子1,盒子2,盒子3,盒子4}, N=4

  • 观测集合: = {红球,白球} M=2

  • 初始化概率分布:

  • 状态转移矩阵:

  • 观测矩阵:

隐马尔可夫模型的三个基本问题

  • 1、概率计算问题

    给定:

    计算:

  • 2、学习问题

    已知:

    估计:,使最大

  • 3、预测问题(解码)

    已知:

    求:使最大的状态序列

下面我们使用python代码写一个HMM模型生成序列的示例代码

import numpy as np

class HMM(object):
    def __init__(self, N, M, pi=None, A=None, B=None):
        self.N = N
        self.M = M
        self.pi = pi
        self.A = A
        self.B = B

    def get_data_with_distribute(self, dist): # 根据给定的概率分布随机返回数据(索引)
        r = np.random.rand()
        for i, p in enumerate(dist):
            if r < p: return i
            r -= p

    def generate(self, T: int):
        '''
        根据给定的参数生成观测序列
        T: 指定要生成观测序列的长度
        '''
        result = []
        for ind in range(T):        # 依次生成状态和观测数据
            if ind==0:
                i = self.get_data_with_distribute(self.pi)
            else:
                i = self.get_data_with_distribute(self.A[i])
            o = self.get_data_with_distribute(self.B[i])
            result.append(o)
        return result

if __name__ == "__main__":
    pi = np.array([0.25, 0.25, 0.25, 0.25])
    A = np.array([
        [0,  1,  0, 0],
        [0.4, 0, 0.6, 0],
        [0, 0.4, 0, 0.6],
        [0, 0, 0.5, 0.5]])
    B = np.array([
        [0.5, 0.5],
        [0.3, 0.7],
        [0.6, 0.4],
        [0.8, 0.2]])
    hmm = HMM(4, 2, pi, A, B)
    print(hmm.generate(10))  # 生成10个数据
 
# 生成结果如下
[0, 0, 0, 0, 1, 1, 1, 1, 0, 0]   # 0代表红球,1代表白球

隐马尔可夫模型数学原理

下面我们从隐马尔可夫模型的三个基本问题出发,逐个解释问题解法:

  • 1、概率计算问题

    • 直接计算法
    • 前向算法
    • 后向算法

    直接计算法:

    • 状态序列概率:

    • 对固定的状态序列,观测序列的概率:

    • 和同时出现的联合概率为:

    • 对所有的可能的状态序列求和,得到观测序列的概率:

      此算法的复杂度为,计算量太大,不可行。

    前向算法:

    • 前向概率定义:给定隐马尔科夫模型,定义到时刻t部分观测序列为:,且状态为的概率为前向概率,记作:

      输入:隐马尔可夫模型,观测序列;

      输出:观测序列概率

      初值:

      递推:

      终止:

      算法复杂度,算法复杂度大大降低

      因为:

      所以:

    • 下面以一个具体的例子来演示前向算法计算过程:
      A = \left[ \begin{matrix} 0.5 & 0.2 & 0.3 \\ 0.3 & 0.5 & 0.2 \\ 0.2 & 0.3 & 0.5 \end{matrix} \right], B = \left[ \begin{matrix} 0.5 & 0.5 \\ 0.4 & 0.6 \\ 0.7 & 0.3 \end{matrix} \right], \pi = (0.2,0.4,0.4)^T
      假设T = 3,O = (红,白,红),使用前向算法计算,模型,状态集合,观测集合

      (1)计算初值

      (2)递推计算
      \begin{align} \alpha_2(1) & = \left[ \begin{matrix} \sum_{i=1}^3\alpha_1(i)a_{i1} \end{matrix} \right]b_1(o_2) = (0.1\times0.5+0.16\times0.3+0.28\times0.2)\times0.5 = 0.154\times0.5 = 0.077 \\ \alpha_1(2) & = \left[ \begin{matrix} \sum_{i=1}^3\alpha_1(i)a_{i2} \end{matrix} \right]b_2(o_2) = 0.184\times0.6 = 0.1104 \\ \alpha_1(3) & = \left[ \begin{matrix} \sum_{i=1}^3\alpha_1(i)a_{i3} \end{matrix} \right]b_3(o_2) = 0.202\times0.3 = 0.0606 \\ \alpha_3(1) & = \left[ \begin{matrix} \sum_{i=1}^3\alpha_2(i)a_{i1} \end{matrix} \right]b_1(o_3) = 0.04187 \\ \alpha_3(2) & = \left[ \begin{matrix} \sum_{i=1}^3\alpha_2(i)a_{i2} \end{matrix} \right]b_2(o_3) = 0.03551 \\ \alpha_3(3) & = \left[ \begin{matrix} \sum_{i=1}^3\alpha_2(i)a_{i3} \end{matrix} \right]b_3(o_3) = 0.05284 \end{align}
      (3)终止

    后向算法:

    • 后向概率定义:给定隐马尔可夫模型,定义在时刻t状态为的条件下,从t+1到T的部分观测序列为:的概率为后向概率,记作:

      仍然可以使用递推的方法求得后向概率及观测序列概率

      输入:隐马尔科夫模型,观测序列O;

      输出:观测序列概率.

      (1)            

      (2)对

      (3

      前后向统一写为:(t=1和t=T-1分别对应)

      一些概率和期望值计算方式:

      1、给定模型和观测,在时刻处于状态的概率,记

      2、给定模型和观测,在时刻处于状态且在时刻处于概率,记
      \xi_t(i,j)=P(i_t=q_i,i_{t+1}=q_j,O|\lambda)=\frac{P(i_t=q_t,i_{t+1}=q_j,O|\lambda)}{P(O|\lambda)}=\frac{P(i_t=q_t,i_{t+1}=q_j,O|\lambda)}{\sum_{i=1}^N \sum_{j=1}^N P(i_t=q_i,i_{t+1}=q_j,O|\lambda)} = \frac{\alpha_t(i)a_{ij}b_j(o_{t+1}) \beta_{t+1}(j)}{\sum_{i=1}^N \sum_{j=1}^N \alpha_t(i)a_{ij}b_j(o_{t+1}) \beta_{t+1}(j)}
      3、由1和2可得,(1)在观测O下状态i出现的期望值:,(2)在观测O下由状态i转移的期望值:,(3)在观测O下由状态i转移到状态j的期望值:。

  • 2、学习问题

    监督学习方法:

    • 假设训练数据是包括观测序列O和对应的状态序列I,
    • 可以利用极大似然估计发来估计隐马尔可夫模型参数。

    已知:

    (1)转移概率的估计:假设样本中时刻t处于状态i,时刻t+1转移到状态j的频数为那么转台转移概率的估计是:

    (2)观测概率的估计:设样本中状态为j并观测为k的频数是那么状态j观测为k的概率

    (3)初始状态概率的估计为S个样本中初始状态为的频率。

    非监督学习方法:

    Baum-Welch算法

    假定训练数据只包括{O1,O2,...Os},求模型参数,实质上是有隐变量的概率模型:EM算法。

    (1)确定完全数据的对数似然函数

    完全数据

    完全数据的对数似然函数

    (2)EM的E步,求Q函数,则:

    对序列总长度T进行

    (3)EM算法的M步,极大化求模型参数

    第一项,由约束条件:利用拉格朗日乘子求偏导数,并令结果等于0

    得:

    第二项可写成由约束条件,拉格朗日乘子法得:

    第三项由约束条件,注意,只有在时对的偏导数才不为0,以表示,求得

    将以上得到的概率分别用表示

    Baum-Welch算法流程:

    输入:观测数据;

    输出:隐马尔可夫模型参数。

    (1)初始化

    对n=0,选取得到模型

    (2)递推,对n=1,2,...,

    右侧各值按照观测和模型计算

    (3)终止,得到模型参数

  • 3、预测问题

    预测算法

    (1)近似算法

    想法:在每个时刻t选择在该时刻最后可能的状态,从而得到一个序列状态序列,将它作为预测的结果,在时刻t处于状态的概率:

    在每一时刻t最有可能的状态是:

    从而得到状态序列:

    得到的状态有可能实际不发生

    (2)维比特算法

    用动态规划解概率最大路径,一个路径对应一个状态序列。
    最优路径具有这样的特性:如果最优路径在时刻t通过结点,那么这一路径从结点到终点的部分路径,对于从到的所有可能的部分路径来说,必须是最优的。
    只需从时刻t=1开始,递推地计算在时刻t状态为i的各条部分路径的最大概率,直至得到时刻t=T状态为i的各条路径的最大概率,时刻t=T的最大概率即为最优路径的概率最有路径的终结点也同时得到。
    之后,为了找出最优路径的各个结点,从终结点开始,由后向前逐步求得结点,得到最优路径

    导入两个变量和,定义在时刻t状态为i的所有单个路径中概率最大值为:

    由定义可得变量的递推公式:
    \delta_{t+1}(i)=\underset{i_1,i_2,\cdots,i_t}{\operatorname{max}}P(i_{t+1}=i,i_t,\cdots,i_1,o_{t+1},\cdots,o_1|\lambda)=\underset{1\leq j \leq N}{\operatorname{max}}[\delta_t(j)a_{ji}]b_i(o_{t+1}),  i=1,2,\cdots,N;t=1,2,\cdots,T-1
    定义在时刻t状态为i的所有单个路径中概率最大的路径的第t-1个结点为

    输入:模型和观测;

    输出:最优路径.

    ①初始化

    ②递推,对

    ③终止

    ④最优路径回溯,对

    求得最优路径
    示例:
    A= \left[\begin{matrix} 0.5 & 0.2 & 0.3 \\ 0.3 & 0.5 & 0.2 \\ 0.2 & 0.3 & 0.5 \end{matrix}\right], B = \left[\begin{matrix} 0.5 & 0.5 \\ 0.4 & 0.6 \\ 0.7 & 0.3 \end{matrix}\right], \pi = (0.2,0.4,0.4)^T
    O=(红,白,红),试求最优状态序列,即最优路径

    ①初始化:在t=1时,对每一个状态i,i=1,2,3,求状态i观测O1为红的概率,记为:

    代入实际数据:,记

    ②在t=2时,对每一个状态i,i=1,2,3,求在t=1时状态为j观测O1为红并在t=2时状态为i观测O2为白的路径的最大概率,记为

    同时,对每个状态i,记录概率最大路径的前一个状态j,

    计算:
    \delta_2(1)=\underset{1 \leq j \leq 3}{\operatorname{max}}[\delta_1(j)a_{j1}]b_1(o_2)=\underset{j}{\operatorname{max}}\{0.10\times0.5,0.16\times0.3,0.28\times0.2\}\times0.5=0.028 \\ \psi_2(1)=3 \\ \delta_2(2)=0.0504,\psi_2(2)=3 \\ \delta_2(3)=0.042,\psi_2(3)=3
    同样,在t=3时
    \delta_3(i)=\underset{1 \leq j \leq 3}{\operatorname{max}}[\delta_2(j)a_{ji}]b_i(o_3) \\ \psi_3(i)=\underset{1\leq j \leq 3}{\operatorname{argmax}}[\delta_2(j)a_{ji}] \\ \delta_3(1) = 0.00756,\psi_3(1)=2 \\ \delta_3(2) = 0.01008,\psi_3(2)=2 \\ \delta_3(3) = 0.0147,\psi_3(3)=3
    ③以P*表示最优路径的概率:

    最优路径的终点是:

    ④由最优路径的终点,逆向找到

    于是求得最优路径,即最优状态序列:

隐马尔科夫模型Python实现

import numpy as np

class HMM(object):
    def __init__(self, N, M, pi=None, A=None, B=None):
        self.N = N
        self.M = M
        self.pi = pi
        self.A = A
        self.B = B

    def get_data_with_distribute(self, dist): # 根据给定的概率分布随机返回数据(索引)
        r = np.random.random()
        for i, p in enumerate(dist):
            if r < p: return i
            r -= p

    def generate(self, T: int):
        '''
        根据给定的参数生成观测序列
        T: 指定要生成观测序列的长度
        '''
        result = []
        i = 0
        for ind in range(T):        # 依次生成状态和观测数据
            if ind==0:
                i = self.get_data_with_distribute(self.pi)
            else:
                i = self.get_data_with_distribute(self.A[i,:])
            o = self.get_data_with_distribute(self.B[i,:])
            result.append(o)
        return result
    
    def forward(self,X):
        '''
        根据给定的参数计算条件概率(前向算法)
        X:观测数据
        '''
        alpha = self.pi * self.B[:,X[0]]
        for x in X[1:]:
            #将alpha构造成(N,1)维度,使用广播机制
            alpha = np.sum(self.A * alpha[...,np.newaxis],axis=0) * self.B[:,x]
        
        return alpha.sum()
    
    def backward(self,X):
        '''
        根据给定的参数计算条件概率(后向算法)
        X:观测数据
        '''
        beta = np.ones(self.N)
        for x in X[:0:-1]:
            #beta默认为(1,N)维度,使用广播机制
            beta = np.sum(self.A * self.B[:,x] * beta,axis=1)

        return np.sum(beta * self.pi * self.B[:,X[0]],axis=0)
    
    def get_alpha_beta(self,X):
        '''
        根据给定数据与参数,计算所有时刻的前向概率和后向概率
        '''
        T = len(X)
        alpha = np.zeros((T,self.N))
        alpha[0,:] = self.pi * self.B[:,X[0]]
        for t in range(T-1):
            alpha[t+1,:] = np.sum(self.A * alpha[t,:][...,np.newaxis],axis=0) * self.B[:,X[t+1]]
            
        beta = np.ones((T,self.N))
        for t in range(T-1,0,-1):
            beta[t-1,:] = np.sum(self.A * beta[t,:] * self.B[:,X[t]],axis=1)

        # print(alpha[T-1,:].sum(),np.sum(beta[0,:] * self.pi * self.B[:,X[0]],axis=0))        
        return alpha,beta
    
    def fit(self,X):
        '''
        根据观测序列估计参数
        '''
        #初始化参数pi,A,B
        self.pi = np.random.random(self.N)
        self.pi /= np.sum(self.pi)
        self.A = np.random.random((self.N,self.N))
        self.A /= np.sum(self.A,axis=1)[:,np.newaxis]
        self.B = np.random.random((self.N,self.M))
        self.B /= np.sum(self.B,axis=1)[:,np.newaxis]
        
        T = len(X)
        for epoch in range(10):
            #根据公式计算下一时刻参数值
            alpha,beta = self.get_alpha_beta(X)
            gamma = alpha * beta
            for i in range(self.N):
                for j in range(self.N):
                    self.A[i,j] = np.sum(alpha[:-1,i] * beta[1:,j] * self.A[i,j] * self.B[j,X[1:]],axis=0) / np.sum(gamma[:-1,i],axis=0)
            for i in range(self.N):
                for j in range(self.M):
                    self.B[i,j] = np.sum(gamma[:,i] * (X == j),axis=0) / np.sum(gamma[:,i],axis=0)
                    
            self.pi = gamma[0] / gamma[0].sum()
            
    def decode(self,X):
        '''
        解码问题,参数已知,根据给定的观测序列,返回最大概率的隐藏序列
        '''
        T = len(X)
        x = X[0]
        delta = self.pi[:,np.newaxis] * self.B[:,x]
        varphi = np.zeros((T,self.N),dtype=np.int)
        path = [0] * T
        for t in range(1,T):
            # delta = delta.reshape(-1,1)
            tmp = delta * self.A
            varphi[t,:] = np.argmax(tmp,axis=0)
            delta = np.max(tmp,axis=0) * self.B[:,X[t]]

        path[-1] = np.argmax(delta)
        #回溯最优路径
        for t in range(T-1,0,-1):
            path[t-1] = varphi[t,path[t]]
            
        return path
 
if __name__ == "__main__":
    pi = np.array([0.25, 0.25, 0.25, 0.25])
    A = np.array([
        [0,  1,  0, 0],
        [0.4, 0, 0.6, 0],
        [0, 0.4, 0, 0.6],
        [0, 0, 0.5, 0.5]])
    B = np.array([
        [0.5, 0.5],
        [0.3, 0.7],
        [0.6, 0.4],
        [0.8, 0.2]])
    hmm = HMM(4, 2, pi, A, B)
    print(hmm.generate(10))  # 生成10个数据
    X = [0,0,1,1,0] #观测序列为红,红,白,白,红
    print(hmm.forward(X))
    print(hmm.backward(X))

    import matplotlib.pyplot as plt
    def triangle_data(T):   # 生成三角波形状的序列
        data = []
        for x in range(T):
            x = x % 2
            data.append(x if x <= 1 else 2-x)
        return data
    
    data = np.array(triangle_data(15))
    hmm.fit(data)
    gen_obs = hmm.generate(15)  # 再根据学习的参数生成数据
    x = np.arange(15)
    plt.scatter(x, gen_obs, marker='*', color='r')
    plt.plot(x, data, color='g')
    plt.show()

    pi = np.array([0.2, 0.4, 0.4])
    A = np.array([
        [0.5,  0.2,  0.3],
        [0.3, 0.5, 0.2],
        [0.2, 0.3, 0.5]])
    B = np.array([
        [0.5, 0.5],
        [0.4, 0.6],
        [0.7, 0.3]])
    O = [0,1,0]
    hmm = HMM(3, 2, pi, A, B)
    path = hmm.decode(O)
    print(path) #输出(2,2,2)为索引

总结

隐马尔可夫模型是关于时序的概率模型,由初始状态概率向量、状态转移概率矩阵A和观测概率矩阵B决定,可以写成的形式,隐马尔可夫模型是一个生成模型,它的应用很多,在分词、词性标注、NLP、语音识别等领域都有着广泛的应用,是一个非常经典的机器学习模型。

你可能感兴趣的:(机器学习之隐马尔科夫模型(HMM))