viterbi算法主要解决已知HMM和观察序列,找到最可能的隐藏状态,也就是所说的解码问题。解码在语音识别中的应用就是,根据一段声音序列,找到最有可能对应的文字序列。


   还是天气和水藻的这个例子,我们运用viterbi算法找出dry,damp,soggy对应的天气情况。所有的已知条件和之前那个前向算法都是一直的,不同之处就是求解的过程。


1.  根据初始概率和观察概率,计算t=0时刻的概率值,保存在delta数组的第一列


2.  计算t=i 时刻(i < T)状态j (j < M)下概率的值,选出最大的概率值maxVal,乘以观察概率保存到delta数组 中,然后将相应的索引值保存到path数组中。


3.  在t = T -1时刻,选出最大的那个概率值,最大概率值保存在prob中,同时用k记下对应的行号,并将k存入list的头部


4.  t从 T - 1开始循环到t=1,因为当前t时刻path数组中的值保存的是t-1时刻的行号,因此循环的取出该值,最后就是所要求的隐藏状态的路径了。



java版viterbi算法:

package cn.yunzhisheng.hmm;


import java.util.LinkedList;

import java.util.List;


publicclass HMM {


publicint   M = 3;

// 转移矩阵

publicdouble[][] transferMatix = {

{0.500, 0.375, 0.125},

{0.250, 0.125, 0.625},

{0.250, 0.375, 0.375}

};


// 观察矩阵

publicdouble[][] observationMatix = {

{0.60, 0.20, 0.15, 0.05},

{0.25, 0.25, 0.25, 0.25},

{0.05, 0.10, 0.35, 0.50}

};

// 初始概率

publicdouble[] pi = {0.63, 0.17, 0.20};



public List<Integer> viterbi(int[] seq){

int T = seq.length;

// 保存计算的概率

double [][] delta = newdouble[M][T];

// 保存最佳路径

int [][] path = newint[M][T];

// 当前概率的值

double val = 0.0;

// 保存当前最大概率的值

double maxVal = 0.0;

// t-1时刻最大概率的索引

int maxIndex = 0;

// t = 0 用初始概率乘以相应观察矩阵的概率,保存到delta的第一列中

for(int j = 0; j < M; j++){

delta[j][0] = pi[j] * observationMatix[j][seq[0]];

}

for(int t = 1; t < T; t++){

for(int j = 0; j < M; j++){

maxVal = 0.0;

maxIndex= 0;

for(int i = 0; i < M; i++){

val = delta[i][t-1] * transferMatix[i][j];

if(val > maxVal){

// 保存最大概率

maxVal= val;

// 保存t-1时刻的索引

maxIndex = i;

}

}

// 将概率保存在delta数组中

delta[j][t] = maxVal * observationMatix[j][seq[t]];;

path[j][t] = maxIndex;

}

}

// 对最后一列,比较概率,找到最大概率的索引

double prob = 0.0;

int k = 0;

for(int j = 0; j < M; j++){

if(delta[j][T-1] > prob){

prob = delta[j][T-1];

k = j;

}

}

System.out.println("prob=" + prob);

LinkedList<Integer> list = new LinkedList<Integer>();

list.addFirst(k);

int row = k;

for(int t = T-1; t > 0; t--){

int v = path[row][t];

list.addFirst(v);

row = v;

}


return list;

}



publicstaticvoid main(String[] args) {

HMM hmm = new HMM();

// 观察序列

int [] seq = {0, 2, 3};

// 前向算法

//double p = hmm.forward(seq);

//System.out.println(p);

// 维特比算法

List<Integer> weather = hmm.viterbi(seq);

System.out.println(weather);

}

}


运行结果:

prob=0.01107421875

[0, 1, 2]


即最终的隐藏状态是:sunny,cloudy,rainy


注:http://www.comp.leeds.ac.uk/roger/HiddenMarkovModels/html_dev/viterbi_algorithm/s3_pg3.html 这个网站计算有问题,它把状态矩阵给弄反了


我的计算结果和 umdhmm是一样的,同时手工计算的也是这个结果。