HMM(Hidden Markov Model)

目录

HMM定义

HMM的确定

从⽣成式的观点考虑隐马尔科夫模型,我们可以更好地理解隐马尔科夫模型。

 HMM的参数

  统一定义:

HMM举例

HMM的3个基本问题

概率计算问题

定义:前向概率-后向概率

前向算法

后向算法

 前后向关系

单个状态的概率:

两个状态的联合概率

期望

学习问题

监督学习方法

Baum-Welch算法(非监督学习方法)

预测问题

近似算法

Viterbi算法

python实现中文分词



-------七月算法机器学习笔记

HMM定义

上图为HMM的贝叶斯网络,【\large z_1,z_2不可观察的前提下,\large x_1 \& z_2,x_1\&x_2都不独立,不满足条件独立判定条件(tail-to-tail)】

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

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

       HMM随机生成的状态的序列,称为状态序列;每个状态生成一个观测,由此产生的观测随机序列,称为观测序列

               --序列的每个位置可看做是一个时刻。

HMM的确定

根据马尔可夫随机过程,我们现在让潜在变量\large z_n的概率分布\large p(z_n|z_{n-1})对前一个潜在变量\large z_{n-1}产生依赖。由于潜在变量是K维二值变量,因此条件概率分布对应于数字组成的表格,记做A,它的元素被称为转移概率。元素\large A_{jk}=p(z_{nk}=1|z_{n-1,j}=1).由于它们是概率值,因此满足\large 0\leq A_{jk} \leq 1 \ \&\sum_kA_{jk}=1,即行和为1.

直观意思是第n-1个时间步处于状态j时,那么在第n个时间步处于状态k的概率,(即下一个时间步,状态从j转移到k的概率)

从而矩阵A由K(K-1)个独立的参数。可以显式地将条件概率分布写成

                 \large p(z_n|z_{n-1},A)=\prod _{k=1}^K\prod _{j=1}^KA_{jk}^{(z_{n-1,j^{z_{nk}}})}         (1)

初始潜在结点\large z_1很特别,因为它没有父结点,因此它的边缘概率分布\large p(z_1)由一个概率向量\large \pi表示,元素为        \large \pi_k\equiv p(z_{1k}=1),即          \large p(z_1|\pi)=\prod_{k=1}^K\pi_k^{(z_{1k})}  ,  \large s.t.,\sum _k\pi_k=1            (2)

可以通过定义观测变量的条件概率分布\large p(x_n|z_n,B)来确定一个概率模型,其中B是控制概率分布的参数集合。这些条件概率被称为发射概率。因此对于一个给定的B值,概率分布\large p(x_n|z_n,B)由一个K维的向量组成,对应于二值向量\large z_n的K个可能状态。我们可以将发射概率表示为,

             \large p(x_n|z_n,B)=\prod_{k=1}^Kp(x_n|b_k)^{(z_{nk})}                    ( 3 )

从而观测变量和潜在变量上的联合概率分布为,

            \large p(X,Z|\lambda)=p(z_1|\pi)\left [ \prod_{n=2}^Np(z_n|z_{n-1},A) \right ]\prod_{m=1}^Mp(x_m|z_m,B)         ( 4 )

其中\large {\color{Red} X=\left \{ x_1,...,x_N \right \},\ Z=\left \{ z_1,...,z_N \right \} ,\ \lambda=\left \{ \pi,A,B \right \}}表示控制模型参数的集合。

从⽣成式的观点考虑隐马尔科夫模型,我们可以更好地理解隐马尔科夫模型。

       【马尔科夫模型为了从⼀个混合⾼斯分布中⽣成样本,我们⾸先随机算侧⼀个分量,选择的概率为混合系数πk, 然后从对应的⾼斯分量中⽣成⼀个样本向量x。这个过程重复N次,产⽣N个独⽴样本组成的数据集。

       在【隐马尔科夫模型】的情形,这个步骤修改如下。⾸先我们选择初始的潜在变量z1,概率由参数πk控制,然后采样对应的观测x1。现在我们使⽤已经初始化的z1的值,根据转移概 率p(z2 | z1)来选择变量z2的状态。从⽽我们以概率Ajk选择z2的状态k,其中k = 1, . . . ,K。⼀ 旦我们知道了z2,我们就可以对x2采样,从⽽也可以对下⼀个潜在变量z3采样,以此类推。

       这是有向图模型的祖先采样的⼀个例⼦。例如,如果我们有⼀个模型,其中对⾓转移元素Akk⽐ ⾮对⾓的元素⼤得多,那么⼀个典型的数据序列中,会有连续很长的⼀系列点由同⼀个概率分布⽣成,⽽从⼀个分量转移到另⼀个分量不会经常发⽣。图13.8说明了从隐马尔科夫模型⽣成样本的过程。

 HMM的参数\large \mathbf{\lambda=\left \{ \pi,A,B \right \}}}

  统一定义:

          Q是所有可能的状态的集合----N是可能的状态数(等价于前面说的K维)

          V是所有可能的观测的集合----M是可能的观测数(等价于观测值x的维度)

                          \large Q=\left \{ q_1,q_2,...,q_N \right \}

                          \large V=\left \{ v_1,v_2,...,v_M \right \}

         \large I是长度为T的状态序列,O是对应的观测序列

                         \large I=\left \{ i_1,i_2,...,i_T \right \}

                        \large O=\left \{ o_1,o_2,...,o_T \right \}

        A:状态转移概率矩阵

                   \large A=\left [ a_{ij} \right ]_{N\times N}\large a_{ij}=p(i_{t+1}=q_j|i_t=q_i)在时刻t处于状态\large q_i的条件下时刻t+1转移到状态\large q_j的概率

       B:观测(发射)概率矩阵

                    \large B=\left [ b_{ik} \right ]_{N\times M} ,\large b_{ik}=p(o_t=v_k|i_t=q_i), 即在时刻t处于状态\large q_i的条件下生成观测\large v_k的概 率。

       \large \pi是初始状态概率向量

                     \large \pi=(\pi_i)\large \pi_i=p(i_1=q_i), 即 时刻t=1处于状态\large q_i的概率

HMM举例

该示例的各个参数

那么,在给定参数π、A、B的前提下,得到观测序列“红红白白红”的概率是多少?即\large P(O|\lambda)。 由此引出HMM的3个基本问题

HMM的3个基本问题

      1)概率计算问题:前向-后向算法——动态规划

            给定模型\large \mathbf{\lambda=\left \{ \pi,A,B \right \}}}和观测序列,计算模型λ下观测序列\large O=\left \{ o_1,o_2,...,o_T \right \}出现的概率\large P(O|\lambda)

     2)学习问题:Baum-Welch算法(状态未知)——EM

           已知观测序列\large O=\left \{ o_1,o_2,...,o_T \right \},估计模型\large \mathbf{\lambda=\left \{ \pi,A,B \right \}}}的参数,使得在该模型下观测序列\large P(O|\lambda)最大

     3)预测问题:Viterbi算法——动态规划

          解码问题:已知模型\large \mathbf{\lambda=\left \{ \pi,A,B \right \}}}和观测序列\large O=\left \{ o_1,o_2,...,o_T \right \}

                            求给定观测序列条件概率\large P(I|O,\lambda)最大的状态序列\large I

概率计算问题

直接计算法:暴力算法

由公式(1)(2)

                \large \Rightarrow P(I|\lambda) &=p(i_1|\pi)\prod_{t=2}^T p(i_t|i_{t-1},A) =\pi_{i_1}a_{i_1i_2}a_{i_2i_3}\cdot\cdot\cdot a_{i_{T-1}i_T}         (5)

              表示只与状态转移概率矩阵A和初始概率分布π有关

由公式(3)

                \large \Rightarrow P(O|I,\lambda)=\prod_{t=1}^Tp(o_t|B)=b_{i_1o_1}b_{i_2o_2}\cdot \cdot \cdot b_{i_To_T}                                  ( 6 )

                表示只与观测(发射)概率矩阵B有关

由公式(4)\large O\large I同时出现的联合概率:

                  \large \begin{align*} \Rightarrow P(O,I|\lambda)&=P(I|\lambda)P(O|I,\lambda) \\ &=p(i_1|\pi)\left \{ \prod_{t=2}^T p(i_t|i_{t-1},A)\right \} \prod_{t=1}^Tp(o_t|B)\\ &=\pi_{i_1}b_{i_1o_1}a_{i_1i_2}b_{i_2o_2}a_{i_2i_3}\cdot\cdot\cdot a_{i_{T-1}i_T}b_{i_To_T} \end{align*}              (7)

因此,对公式(7)中的所有可能的状态序列\large I=\left \{ i_1,i_2,...,i_T \right \}求和,即可得到\large P(O|\lambda),即

                    \large \begin{align*} P(O|\lambda)&=\sum_IP(O,I|\lambda) \\ &= \sum_{i_1,i_2,...,i_T}\pi_{i_1}b_{i_1o_1}a_{i_1i_2}b_{i_2o_2}a_{i_2i_3}\cdot\cdot\cdot a_{i_{T-1}i_T}b_{i_To_T} \end{align*}              ( 8 )

对(8)式进行分析:加和符号中共有2T个因子,\large I的遍历个数为\large N^T,因此,时间复杂度为\large O(TN^T),指数级增长,复杂度过高。

借鉴动态规划的方法,选择一个状态推算出它的状态转移方程。

chow-liu tree

【凡是树状或者链状(类树)的图,将任何一个节点作为某一时刻状态值,能够看到的输出就是它的前向;以它作为条件能够看到的输出就是它的后向】

思考:用贝叶斯网络的这套思维体系去影响深度学习的网络,从而将深度网络精简。

定义:前向概率-后向概率

这里y等价于(前面提到的)o;q即i

       前向概率:

              \large \alpha_i(t)=p(y_1,y_2,...,y_t,q_t=i|\lambda), 表示第t个时刻位于第i号状态并且观察到了前t个观测值的概率;

                                                                                  或者说,前t个观测和第t个时刻位于状态i的联合概率

       后向概率:

             \large \beta_i(t)=p(y_{t+1},...,y_T|q_t=i,\lambda),表示在第t个时刻位于第i号状态时,能够观察到t+1及其后面的观测的概率

前向算法

定义:给定\large \lambda,到t时刻部分观测序列为\large \left \{ o_1,o_2,...,o_t \right \}且状态为\large q_i的概率称为前向概率

           记做:\large \alpha_t(i)=p(o_1,o_2,...,o_t,i_t=q_i|\lambda)

                     可以递归计算前向概率\large \alpha_t(i)及观测序列概率\large P(O|\lambda)

           初值:\large \alpha_1(i)=\pi_ib_{io_1},                                         (9)

                      即在时刻t=1时位于状态i且观察到了\large o_1的概率

           递归:对于t=1,2…T-1

                      \large \alpha_{t+1}(i)=\left \{ \sum_{j=1}^N\alpha_t(j)a_{ji} \right \}b_{io_{t+1}},          (10)

                      t+1时刻位于状态i的概率:t时刻的状态\large j=\left \{ 1,2,...,N \right \}转移到 t+1 时刻状态 i 的概率的和

                             ----表示到达了t+1时刻的状态 i【我们只关注t+1时刻的状态,所以不关心t时刻的状态j,所以积分消去j】<类似全概率公式,加和规则,下面(11)同理>

                      那么,\large \alpha_{t+1}(i)即 (t+1时刻位于状态i的概率)且(观测到\large o_{t+1})的概率

            最终:我们得到\large \alpha_T(i)【T时刻位于状态i时,已经观测到整个序列O的概率】,将i积分掉,即得给定参数\large \lambda,观测到序列O的概率

                     \large P(O|\lambda)=\sum_{i=1}^N\alpha_T(i)                                (11)

 分析时间复杂度:

              (11)\large T*N^2(10)

后向算法

\large \beta_t(i)=p(y_{t+1},...,y_T|i_t=q_i,\lambda),表示在第t个时刻位于第i号状态时,能够观察到t+1及其后面的观测的概率

             初值:\large \beta_T(i)=1   ,               (12)

                        在最后时刻T,已经不需要(没有)后面的观测,存在即为1

             递推:对于t=T-1,T-2,...,1

                        \large \beta_t(i)=\left \{ \sum_{j=1}^Na_{ij}b_{jo_{t+1}}\beta_{t+1}(j) \right \},     (13)

                      保证t+1时刻已经观测到序列\large \left \{ o_{t+2},o_{t+3},...,o_T \right \}的前提下(即\large \beta_{t+1}),

                      还要求t时刻状态i能够转移到下一个时刻t+1的某一个状态且能够观测到\large o_{t+1}

                    【t时刻状态i转移到t+1时刻的任一状态时,只要能观测到\large o_{t+1}即可保证\large \beta_{t}(i)能够观测到序列\large \left \{ o_{t+1},...,o_T \right \},

                     因此与j无关,积分消掉】

            最终:当递归到最后一步t=1时,还有\large o_1未观测到,因此同理

                       \large P(O|\lambda)=\sum_{i=1}^N\pi_ib_{io_1}\beta_1(i)                     (14)  

 前后向关系

   根据定义,公式(7):

           \large \begin{align*} P(O,i_t=q_i|\lambda) &=P(O|i_t=q_i,\lambda)P(i_t=q_i|\lambda) \\ &=P(o_1,...,o_t,o_{t+1},...,o_T|i_t=q_i,\lambda)P(i_t=q_i|\lambda) \\ &=P(o_1,...,o_t|i_t=q_i,\lambda)P(o_{t+1},...o_T|i_t=q_i,\lambda)P(i_t=q_i|\lambda) \\ &=P(o_1,...,o_t,i_t=q_i|\lambda)P(o_{t+1},...,o_T|i_t=q_i,\lambda) \\ &=\alpha_t(i)\beta_t(i) \\ \end{align*}              ( 15 )

单个状态的概率:

    给定模型λ和观测O,在时刻t处于状态\large {\color{Red} q_i}的概率。 记,

           \large \gamma_t(i)=P(i_t=q_i|O,\lambda)                                      (16)

     结合公式(15)以及前后向概率的定义,化简得

            \large \begin{align*} \gamma_t(i)&=P(i_t=q_i|O,\lambda) \\ &=\frac{P(i_t=q_i,O|\lambda)}{P(O|\lambda)} \\ &=\frac{\alpha_t(i)\beta_t(i)}{\sum_{i=1}^N\alpha_t(i)\beta_t(i)} \end{align*}                                                                                    (17)

    \large \gamma的意义:

         在每个时刻t选择在该时刻最有可能出现的状态\large {\color{Red} i_t^*},从而得到一个状态序列\large {\color{Red} I^*=\left \{ i_1^*,i_2^*,...,i_T^* \right \}},将它作为预测的结果。

两个状态的联合概率

  求给定模型λ和观测O,在时刻t处于状态\large {\color{Red} q_i} 并且时刻t+1处于状态\large {\color{Red} q_j}的概率。

           \large \begin{align*} \xi_t(i,j)&=P(i_t=q_i,i_{t+1}=q_j|O,\lambda) \\ &=\frac{P(i_t=q_i,i_{t+1}=q_j,O|\lambda)}{P(O|\lambda)} \\ &= \frac{P(i_t=q_i,i_{t+1}=q_j,O|\lambda)}{\sum_{i=1}^N\sum_{j=1}^NP(i_t=a_i,i_{t+1}=q_j,O|\lambda)} \end{align*}                                  (18)

           而\large P(i_t=q_i,i_{t+1}=q_j,O|\lambda)=\alpha_t(i)a_{ij}b_{jo_{t+1}}\beta_{t+1}(j)                             (19)

期望

   在观测O下状态i出现的期望:   \large \sum_{t=1}^{T-1}\gamma_t(i)

   在观测O下状态i转移到状态j的期望:\large \sum_{t=1}^{T-1}\xi_t(i,j)

学习问题

      1)若训练数据包括观测序列和状态序列,则HMM的学习非常简单,是监督学习;

      2)若训练数据只有观测序列,则HMM的学习需要使用EM算法,是非监督学习。

监督学习方法

       假设已给定训练数据包含S个长度相同的观测序列和对应的状态序列\large \left \{ (O_1,I_1),(O_2,I_2),...,(O_s,I_s)\right \},那么,可以直接利用Bernoulli大数定理的结论“频率的极限是概率”,给出HMM的参数估计。

       转移概率\large a_{ij}的估计:

              设样本中时刻t处于状态i时刻t+1转移到状态j的频数为 \large A_{ij},则

                        \large \hat{a}_{ij}=\frac{A_{ij}}{\sum_{j=1}^NA_{ij}}

      观测概率\large b_{ik}的估计:

             设样本中状态i并观测为k的频数为\large B_{ik},则

                       \large \hat{b}_{ik}=\frac{B_{ik}}{\sum_{k=1}^MB_{ik}}

      初始状态概率\large \pi_i的估计为S个样本中初始状态为\large q_i的概率。

Baum-Welch算法(非监督学习方法)

EM算法整体框架

EM过程:对数似然

所有观测数据写成\large O=(o_1,...,o_T),所有隐数据写成\large I=(i_1,...,i_T),完全数据是\large (O,I)=(o_1,...,o_T,i_1,...,i_T), 完全数据的对数似然函数是\large ln\ P(O,I|\lambda)

假设\large \bar{\lambda}是HMM参数的当前估计值,\large \lambda为待求参数。

            \large \begin{align*} Q(\lambda,\bar{\lambda})&=\sum_I\left \{ ln\ P(O,I|\lambda) \right \}P(I|O,\bar{\lambda}) \\ &=\sum_Iln\ P(O,I|\lambda)\frac{P(O,I|\bar{\lambda})}{P(O,\bar{\lambda})} \\ &\propto \sum_Iln\ P(O,I|\lambda)P(O,I|\bar{\lambda}) \end{align*}      ( 20 )

根据公式(7)

     

函数可写成:

         \begin{align*} Q(\lambda,\bar{\lambda}) &\propto \sum_Iln\ P(O,I|\lambda)P(O,I|\bar{\lambda}) \\ &=\sum_Iln\ \pi_iP(O,I|\bar{\lambda})+\sum_I\left \{ \sum_{t=1}^{T-1}ln\ a_{i_ti_{t+1}} \right \}P(O,I|\bar{\lambda})+\sum_I\left \{ \sum_{t=1}^Tln\ b_{i_to_t}\right \}P(O,I|\bar{\lambda}) \end{align*}     (21)

极大化Q,求得参数A,B,π

由于该三个参数分别位于三个项中【公式(21)】,可分别极大化

    第一项:π   

                 \sum_Iln\ \pi_{i_1}P(O,I|\bar{\lambda})=\sum_{i=1}^Nln\ \pi_{i_1}P(O,i_1=i|\bar{\lambda})s.t.,\sum_{i=1}^N\pi_i=1               ( 22 )

         利用lagrange乘子法,得:

                 L_1=\sum_{i=1}^Nln\ \pi_{i_1}P(O,i_1=i|\bar{\lambda})+\gamma(\sum_{i=1}^N\pi_i-1)                                             ( 23 )

        对\pi_i求偏导,并等0,得

                \frac{\partial L_1}{\partial \pi_i}=P(O,i_1=i|\bar{\lambda})+\gamma\pi_i=0                                            (24)

                \Rightarrow \gamma\sum_{i=1}^N\pi_i=-\sum_{i=1}^NP(O,i_1=i|\bar{\lambda})

               \Rightarrow {\color{Red} \gamma=-P(O|\bar{\lambda})}                                             (25)

       将公式(25)回带到(24)得

              

   第二项:转移概率

             

         仍然使用Lagrange乘子法,得

             

   第三项:观测(发射)概率

       同理,得

             

预测问题

     近似算法

     Viterbi算法

近似算法

Viterbi算法

思考:算法(走棋盘/格子取数)

        给定m*n的矩阵,每个位置是一个非负整数,从左上角开始,每次只能朝右和下走, 走到右下角,求总和最小的路径。

     

动态规划状态转移方程:

     

      走的方向决定了同一个格 子不会经过两次。

      若当前位于(x,y)处,它来自于哪些格子呢?

      dp[0,0]=a[0,0] / 第一行(列)累积

     dp[x,y] = min(dp[x-1,y]+a[x,y],dp[x,y-1]+a[x,y])

     即:dp[x,y] = min(dp[x-1,y],dp[x,y-1]) +a[x,y]

思考:若将上述问题改成“求从左上到右下的最大路径”呢?

Viterbi算法实际是用动态规划解HMM预测问题,用DP求概率最大的路径(最优路径), 这条路径对应一个状态序列。

定义变量\mathbf{​{\color{Red} \delta_t(i)}}:在时刻t状态为i的所有路径中,概率的最大值。

与前向算法过程类似

  定义:\delta_t(i)=max_{i_1,i_2,...i_{t-1}}\ P(i_t=i,i_{t-1},...i_1,o_t,...o_1|\lambda)

  递推:\delta_1(i)=\pi_ib_{io_1}

             \begin{align*} \delta_{t+1}(i)&=max_{(i_1,i_2,...i_t)}\ P(i_{t+1}=i,i_t,...i_1,o_{t+1},...o_1|\lambda) \\ &=max_{(1\leq j \leq N)} (\delta_t(j)a_{ji})b_{io_{t+1}} \end{align*}

  终止:P^*=max_{(1\leq i \leq N)}\delta_T(i)

python实现中文分词

训练集和测试集均使用开源数据集:http://sighan.cs.uchicago.edu/bakeoff2005/

参考了博客:https://blog.csdn.net/aaalswaaa1/article/details/83745785

参数学习使用了监督学习

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import pylab
from pandas import DataFrame, Series
import os

plt.rcParams['font.sans-serif'] = ['SimHei']  #指定默认字体
plt.rcParams['axes.unicode_minus'] = False  #解决保存图像是负号'-'显示为方块的问题
import pickle

class HMM(object):
    def __init__(self):

        self.state_list=[0,1,2,3]#['B','M','E','S']
        self.words=set()
        self.count=np.zeros((4,1))

        self.model_file = 'datasets/hmm_model2.pkl'

    def load_model(self,trained=True):
        if trained:
            with open(self.model_file,'rb') as f:
                self.A=pickle.load(f)
                self.B=pickle.load(f)
                self.pi=pickle.load(f)
                self.load_parameter=True
                f.close()
        else:
            self.A = np.zeros((4, 4))
            self.B = np.ones((4, 65536))
            self.pi = np.zeros((4, 1))
            self.load_parameter=False

    def markLabel(self,text):
        out_text=[]
        if len(text)==1:
            out_text.append(3)
        else:
            out_text+=[0]+[1]*(len(text)-2)+[2]
        return out_text
    def train(self,path):
        self.load_model(False)
        line_num=-1
        #'datasets/pku_training.txt'
        with open(path) as f:
            for line in f:
                line_num+=1
                line=line.strip()
                # print(line)
                if not line:
                    continue
                # if line_num==10:
                #     break
                word_list=[i for i in line if i !=' ']
                # print(word_list)
                self.words |=set(word_list)
                line_list = line.split()
                # print(line_list)
                O_state=[]
                for w in line_list:
                    O_state.extend(self.markLabel(w))
                # print(line)
                print(O_state)
                # print(word_list)
                for k,v in enumerate(O_state):
                    self.count[v]+=1
                    if k==0:
                        self.pi[v,0]+=1
                    else:
                        self.A[O_state[k-1],v]+=1
                        self.B[v,ord(word_list[k])]+=1
                        # print(word_list[k])
            f.close()
        self.pi/=line_num
        self.A/=(np.sum(self.A,axis=1).reshape(4,1))
        print(self.A)
        self.B/=np.sum(self.B,axis=1).reshape(4,1)
        print(self.B)
        with open(self.model_file,'wb') as f:
            pickle.dump(self.A,f)
            pickle.dump(self.B,f)
            pickle.dump(self.pi,f)
            f.close()

    def viterbi(self, pi, A, B, o):
        T = len(o)  # 观测序列
        delta = [[0 for i in range(4)] for t in range(T)]
        pre = [[0 for i in range(4)] for t in range(T)]  # 记录前一个状态
        for i in range(4):
            delta[0][i] = pi[i,0]*B[i,ord(o[0])]
        for t in range(1, T):
            for i in range(4):
                delta[t][i] = delta[t - 1][0]*A[0,i]
                for j in range(1, 4):
                    vj = delta[t - 1][j]*A[j,i]
                    if delta[t][i] < vj:
                        delta[t][i] = vj
                        pre[t][i] = j
                delta[t][i] *= B[i,ord(o[t])]
        decode = [-1 for t in range(T)]  # 解码:回溯查找最大路径
        q = 0
        for i in range(1, 4):
            if delta[T - 1][i] > delta[T - 1][q]:
                q = i
        decode[T - 1] = q
        for t in range(T - 2, -1, -1):
            q = pre[t + 1][q]
            decode[t] = q
        return decode
    def segment(self,sentence,decode):
        '''
        B:一个词的开始
        E:一个词的结束
        M:一个词的中间
        S:单字成词
       举例:你S现B在E应B该E去S幼B儿M园E了S
        '''
        print(decode)
        N=len(sentence)
        i=0
        while i<N:#B/M/E/S
            if decode[i]==0 or decode[i]==1:  #begin
                j=i+1
                while j<N:
                    if decode[j]==2:
                        break
                    j+=1
                print(sentence[i:j+1],'/')
                i=j+1
            elif decode[i]==3 or decode[i]==2:  #single
                print(sentence[i:i+1],'/')
                i+=1
            else:
                print('error',i,decode[i])
                i+=1
    def test(self,o):
        self.load_model(os.path.exists(self.model_file))
        print(self.A)
        decode=self.viterbi(self.pi,self.A,self.B,o)
        self.segment(o,decode)
hmm=HMM()
# hmm.train('datasets/pku_training.txt')
hmm.test('2001年新年钟声即将敲响。人类社会前进的航船就要驶入21世纪的新航程。中国人民进入了向现代化建设第三步战略目标迈进的新征程')


非监督学习参数(未完)

    def log_sum(self,array):
        t=0
        for i in range(len(array)):
            t+=array[i]
        return t
    def cal_alpha(self,pi,A,B,o,alpha):
        for i in range(4):
            alpha[0][i]=pi[i]+B[i][ord(o[0])]
        T=len(o)
        temp=[0 for k in range(4)]

        for t in range(1,T):
            for i in range(4):
                for j in range(4):
                    temp[j]=(alpha[t-1][j]+A[j][i])
                alpha[t][i]=self.log_sum(temp)
                alpha[t][i]+=B[i][ord(o[t])]
        return alpha
    def cal_beta(self,pi,A,B,o,beta):
        T=len(o)
        for i in range(4):
            beta[T-1][i]=1
        temp=[0 for k in range(4)]
        for t in range(T-2,-1,-1):
            for i in range(4):
                beta[t][i]=0
                for j in range(4):
                    temp[j]=A[i][j]+B[j][ord(o[t+1])]+beta[t+1][j]
                beta[t][i]+=self.log_sum(temp)
        return beta
    def cal_gamma(self,alpha,beta,gamma):
        for t in range(len(alpha)):
            for i in range(4):
                gamma[t][i]=alpha[t][i]+beta[t][i]
            s=self.log_sum(gamma[t])
            for j in range(4):
                gamma[t][j]-=s
        return gamma
    def cal_xi(self,alpha,beta,A,B,o,xi):
        T=len(alpha)
        temp=[0 for x in range(16)]
        for t in range(T-1):
            k=0
            for i in range(4):
                for j in range(4):
                    xi[t][i][j]=alpha[t][i]+A[i][j]+B[j][ord(o[t+1])]+beta[t+1][j]
                    temp[k]=xi[t][i][j]
                    k+=1
            s=self.log_sum(temp)
            for i in range(4):
                for j in range(4):
                    xi[t][i][j]-=s
        return xi
    def bw(self,pi,A,B,alpha,beta,gamma,xi,o):
        T=len(alpha)
        for i in range(4):
            pi[i]=gamma[0][i]    #π
        s1=[0 for x in range(T-1)]
        s2=[0 for x in range(T-1)]
        for i in range(4):
            for j in range(4):
                for t in range(T-1):
                    s1[t]=xi[t][i][j]
                    s2[t]=gamma[t][i]
                A[i][j]=self.log_sum(s1)-self.log_sum(s2)#A
        s1=[0 for x in range(T)]
        s2=[0 for x in range(T)]
        for i in range(4):
            for k in range(65536):
                valid=0
                for t in range(T):
                    if ord(o[t])==k:
                        s1[valid]=gamma[t][i]
                        valid+=1
                    s2[t]=gamma[t][i]
                if valid==0:
                    B[i][k]=-3.14e+100
                else:
                    print('**********')
                    B[i][k]=self.log_sum(s1[:valid])-self.log_sum(s2)#B
        return A,B,pi
    def baum_welch(self,pi,A,B):
        df = pd.read_excel('datasets/meituan.xls', usecols=['comment'])
        df = df.dropna()
        preprocessed = set(df['comment'])
        df2 = DataFrame(preprocessed, columns=['comment'])
        # df=pd.read_excel('datasets/meituan1.xls')

        sentence=df2.ix[2,0]

        print(sentence)
        # sentence=list(df2['comment'])
        T=len(sentence)
        print(T)
        alpha=[[0 for i in range(4)] for t in range(T)]
        beta=[[0 for i in range(4)] for t in range(T)]
        gamma=[[0 for i in range(4)] for t in range(T)]
        xi=[[[0 for j in range(4)] for i in range(4)] for t in range(T-1)]
        for time in range(100):
            print(B)
            alpha=self.cal_alpha(pi,A,B,sentence,alpha)
            beta=self.cal_beta(pi,A,B,sentence,beta)
            gamma=self.cal_gamma(alpha,beta,gamma)
            xi=self.cal_xi(alpha,beta,A,B,sentence,xi)
            A,B,pi=self.bw(pi,A,B,alpha,beta,gamma,xi,sentence)
            # print(A,B,pi)
        decode=self.viterbi(pi, A, B, df2.ix[4,0])
        self.segment(df2.ix[4,0],decode)
        return pi,A,B

 

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值