HMM和Viterbi
1. 隐马尔可夫模型的定义和构成
2. 隐马尔可夫模型的三个问题
3. 基于隐马尔可夫模型的分词算法实现
1.1. 介绍隐马尔科夫模型之前首先说一下几个重要的概念,
(1)马尔可夫假设:模型的当前状态仅仅依赖于前面n个状态,可以看到这种假设有可能会丢失信息。
(2)马尔可夫过程:状态间的转移仅仅依赖于前n个状态的过程,也就是当前的状态跟前n个状态之前的状态无关。
(3)马尔可夫链:时间和状态都是离散的马尔可夫过程叫做马尔可夫链,这里的重点是离散
(4)马尔科夫模型:独立于时间t的随机过程,也就是跟时间t没关系,状态跟具体哪个时刻没关系,t只用来说明状态的顺序
1.2 隐马尔可夫模型(hidden Markov model, HMM)
马尔可夫模型有时又叫做可视马尔可夫模型,因为状态是可以看到的,隐马尔可夫模型的状态是看不的,可以观察到的是一些观察值,这些观察值是由状态按照一定的概率产生的,所以可以看到这里有两个随机过程,一个是一个状态到另一个状态的随机,一个是状态到观察值的随机。
一个HMM由如下几个部分组成:
(1)状态的数目N
(2)观察值得数目M
(3)状态的概率转移矩阵A={aij},即从第i个状态到第j个状态的概率,且概率和为1。
(4)状态到观察值的概率B={bij},即从第i个状态到第j个观察值得概率,且概率和为1。
(5)第一次选择哪个状态的概率qi,即一个序列中第i个状态为第一个状态的概率,概率和为1。
例:一个暗室里有N个口袋,每个口袋中有M种不同颜色的球,一个人按照一定的概率分布(p1)随机的选取一个初始口袋,从中根据不同颜色的概率分布p2选取一个球,并报告该球的初始颜色,然后再根据口袋的概率分布p3选取另外一个口袋,根据不同颜色球的概率分布选取一个球并报告该球的颜色,重复这个过程。这里的N个口袋就是HMM的N个状态,M个颜色就是HMM的M个观察值,p1就是HMM的初始状态概率,p3就是HMM的状态转移概率A,p2HMM的状态到观察值的概率B
2. 隐马尔科夫模型的三个问题
2.1 估值问题,如何根据已知的参数,估计一个观察值的概率。就是在1.2中5个部分已知的情况下,估计一个观察值的概率。
例如:给定一个观察序列 和模型,计算在给定u的情况下,观察序列O的概率,即P(O|u)
解决方法:前向算法
具体的程序实现为:
- /**
- * 前向算法
- * @param phmm
- * @param O
- * @return:输出所有的前向概率,p(O|u)只要将T时刻的前向概率求和即可
- */
- public static double [][] forward(HMMEntity phmm, int []O){
- int i,j;
- int t;
- double sum;
- int T = O.length;
- double [][] alpha = new double[T][phmm.getN()];
- //初始化,计算t1时刻所有状态的局部概率
- for(i=0;i<phmm.getN();i++){
- alpha[0][i] = phmm.getPi()[i] * phmm.getB()[i][O[0]];
- System.out.println("alpha[0]["+i+"] = "+alpha[0][i]);
- }
- //递归计算每个时间点的局部概率
- for(t=0;t<T-1;t++){
- for(j=0;j<phmm.getN();j++){
- sum = 0.0;
- for(i=0;i<phmm.getN();i++){
- sum += alpha[t][i] * phmm.getA()[i][j];
- }
- alpha[t+1][j] = sum * phmm.getB()[j][O[t+1]];
- System.out.println("alpha["+(t+1)+"]["+j+"] = "+alpha[t+1][j]);
- }
- }
- return alpha;
- }
2.2 解码问题:给定一个观察序列和模型,如何快速有效的选择最优的状态序列,是的该状态序列最好的解释观察序列
解决方法:维特比算法
实现:
- /**
- * 维特比算法
- * @param phmm
- * @param O
- */
- public static int[] viterbi(HMMEntity phmm,int []O){
- int i,j;
- int t;
- int maxvalind;
- double maxval,val;
- int T = O.length;
- double[][] delta = new double[T][phmm.getN()];
- int[][]psi = new int[T][phmm.getN()];
- for(i=0;i<phmm.getN();i++){
- delta[0][i] = phmm.getPi()[i]* phmm.getB()[i][O[0]];
- psi[0][i] = 0;
- }
- for(t=1;t<T;t++){
- for(j=0;j<phmm.getN();j++){
- maxval = 0.0;
- maxvalind = 1;
- for(i=0;i<phmm.getN();i++){
- val = delta[t-1][i]*phmm.getA()[i][j];
- if(val > maxval){
- maxval = val;
- maxvalind = i;
- }
- }
- delta[t][j] = maxval*phmm.getB()[j][O[t]];
- psi[t][j] = maxvalind;
- }
- }
- double pprob = 0;
- int q[] = new int[T];
- for(i=0;i<phmm.getN();i++){
- if(delta[T-1][i]>pprob){
- pprob = delta[T-1][i];
- q[T-1] = i;
- }
- }
- for(t=T-2;t>=0;t--){
- q[t] = psi[t+1][q[t+1]];
- }
- return q;
- }