Chinaunix首页 | 论坛 | 博客
  • 博客访问: 826389
  • 博文数量: 330
  • 博客积分: 9641
  • 博客等级: 中将
  • 技术积分: 3181
  • 用 户 组: 普通用户
  • 注册时间: 2007-01-19 14:41
文章分类

全部博文(330)

文章存档

2012年(17)

2011年(135)

2010年(85)

2009年(57)

2008年(36)

我的朋友

分类:

2012-01-04 11:06:03

文章来源:

  这篇文章简单描述一下Viterbi算法——一年之前我听过它的名字,直到两周之前才花了一点时间研究了个皮毛,在这里做个简单检讨。先用一句话来简单描述 一下:给出一个观测序列o1,o2,o3 …,我们希望找到观测序列背后的隐藏状态序列s1, s2, s3, …;Viterbi以它的发明者名字命名,正是这样一种由动态规划的方法来寻找出现概率最大的隐藏状态序列(被称为Viterbi路径)的算法。

这里需要抄一点有关隐马可夫序列(HMM,Hidden Markov Model)的书页来解释一下观测序列和隐藏状态序列。

首先从最简单的离散Markov过程入手,我们知道,Markov随机过程具有如下的性质:在任意时刻,从当前状态转移到下一个状态的概率与当前状 态之前的那些状态没有关系。所以,我们可以用一个状态转移概率矩阵来描述它。假设我们有n个离散状态S1, S2,…Sn,我们可以构造一个矩阵A,矩阵中的元素aij表示从当前状态Si下一时刻迁移到Sj状态的概率。

但是在很多情况下,Markov模型中的状态是我们观察不到的。例如,容器与彩球的模型:有若干个容器,每个容器中按已知比例放入各色的彩球(这 样,选择了容器后,我们可以用概率来预测取出各种彩球的可能性);我们做这样的实验,实验者从容器中取彩球——先选择一个容器,再从中抓出某一个球,只给 观察者看球的颜色;这样,每次取取出的球的颜色是可以观测到的,即o1, o2,…,但是每次选择哪个容器是不暴露给观察者的,容器的序列就组成了隐藏状态序列S1, S2,…Sn。这是一个典型的可以用HMM描述的实验。

HMM有几个重要的任务,其中之一就是期望通过观察序列来猜测背后最有可能的隐藏序列。在上面的例子中,就是找到我们在实验中最有可能选择到的容器 序列。Viterbi正是用来解决这个问题的算法。HMM另外两个任务是:a) 给定一个HMM,计算一个观测序列出现的可能性;b)已知一个观测序列,HMM参数不定,如何优化这些参数使得观测序列的出现概率最大。解决前一个问题可 以用与Viberbi结构非常类似的Forward算法来解决(实际上在下面合二为一),而后者可以用Baum-Welch/EM算法来迭代逼近。

从Wiki上抄一个例子来说明Viterbi算法。

假设你有一个朋友在外地,每天你可以通过电话来了解他每天的活动。他每天只会做三种活动之一——Walk, Shop, Clean。你的朋友从事哪一种活动的概率与当地的气候有关,这里,我们只考虑两种天气——Rainy, Sunny。我们知道,天气与运动的关系如下:


Rainy Sunny
Walk 0.1 0.6
Shop 0.4 0.3
Clean 0.5 0.1

例如,在下雨天出去散步的可能性是0.1。

而天气之前互相转换的关系如下,(从行到列)


Rainy Sunny
Rainy 0.7 0.3
Sunny 0.4 0.6

例如,从今天是晴天而明天就开始下雨的可能性是0.4 。

同时为了求解问题我们假设初始情况:通话开始的第一天的天气有0.6的概率是Rainy,有0.4概率是Sunny。OK,现在的问题是,如果连续 三天,你发现你的朋友的活动是:Walk->Shop->Clean;那么,如何判断你朋友那里这几天的天气是怎样的?

解决这个问题的python伪代码如下:

  1. def forward_viterbi(y, X, sp, tp, ep):
  2. T = {}
  3. for state in X:
  4. ## prob. V. path V. prob.
  5. T[state] = (sp[state], [state], sp[state])
  6. for output in y:
  7. U = {}
  8. for next_state in X:
  9. total = 0
  10. argmax = None
  11. valmax = 0
  12. for source_state in X:
  13. (prob, v_path, v_prob) = T[source_state]
  14. p = ep[source_state][output] * tp[source_state][next_state]
  15. prob *= p
  16. v_prob *= p
  17. total += prob
  18. if v_prob > valmax:
  19. argmax = v_path + [next_state]
  20. valmax = v_prob
  21. U[next_state] = (total, argmax, valmax)
  22. T = U
  23. ## apply sum/max to the final states:
  24. total = 0
  25. argmax = None
  26. valmax = 0
  27. for state in X:
  28. (prob, v_path, v_prob) = T[state]
  29. total += prob
  30. if v_prob > valmax:
  31. argmax = v_path
  32. valmax = v_prob
  33. return (total, argmax, valmax)

几点说明:

  1. 算法对于每一个状态要记录一个三元组:(prob, v_path, v_prob),其中,prob是从开始状态到当前状态所有路径(不仅仅 是最有可能的viterbi路径)的概率加在一起的结果(作为算法附产品,它可以输出一个观察序列在给定HMM下总的出现概率,即forward算法的输 出),v_path是从开始状态一直到当前状态的viterbi路径,v_prob则是该路径的概率。
  2. 算法开始,初始化T (T是一个Map,将每一种可能状态映射到上面所说的三元组上)
  3. 三重循环,对每个一活动y,考虑下一步每一个可能的状态next_state,并重新计算若从T中的当前状态state跃迁到 next_state概率会有怎样的变化。跃迁主要考虑天气转移(tp[source_state][next_state])与该天气下从事某种活动 (ep[source_state][output])的联合概率。所有下一步状态考虑完后,要从T中找出最优的选择viterbi路径——即概率最大的 viterbi路径,即上面更新Map U的代码U[next_state] = (total, argmax, valmax)。
  4. 算法最后还要对T中的各种情况总结,对total求和,选择其中一条作为最优的viterbi路径。
  5. 算法输出四个天气状态,这是因为,计算第三天的概率时,要考虑天气转变到下一天的情况。
  6. 通过程序的输出可以帮助理解这一过程:
  1. observation=Walk
  2. next_state=Sunny
  3. state=Sunny
  4. p=0.36
  5. triple=(0.144,Sunny->,0.144)
  6. state=Rainy
  7. p=0.03
  8. triple=(0.018,Rainy->,0.018)
  9. Update U[Sunny]=(0.162,Sunny->Sunny->,0.144)
  10. next_state=Rainy
  11. state=Sunny
  12. p=0.24
  13. triple=(0.096,Sunny->,0.096)
  14. state=Rainy
  15. p=0.07
  16. triple=(0.042,Rainy->,0.042)
  17. Update U[Rainy]=(0.138,Sunny->Rainy->,0.096)
  18. observation=Shop
  19. next_state=Sunny
  20. state=Sunny
  21. p=0.18
  22. triple=(0.02916,Sunny->Sunny->,0.02592)
  23. state=Rainy
  24. p=0.12
  25. triple=(0.01656,Sunny->Rainy->,0.01152)
  26. Update U[Sunny]=(0.04572,Sunny->Sunny->Sunny->,0.02592)
  27. next_state=Rainy
  28. state=Sunny
  29. p=0.12
  30. triple=(0.01944,Sunny->Sunny->,0.01728)
  31. state=Rainy
  32. p=0.28
  33. triple=(0.03864,Sunny->Rainy->,0.02688)
  34. Update U[Rainy]=(0.05808,Sunny->Rainy->Rainy->,0.02688)
  35. observation=Clean
  36. next_state=Sunny
  37. state=Sunny
  38. p=0.06
  39. triple=(0.0027432,Sunny->Sunny->Sunny->,0.0015552)
  40. state=Rainy
  41. p=0.15
  42. triple=(0.008712,Sunny->Rainy->Rainy->,0.004032)
  43. Update U[Sunny]=(0.0114552,Sunny->Rainy->Rainy->Sunny->,0.004032)
  44. next_state=Rainy
  45. state=Sunny
  46. p=0.04
  47. triple=(0.0018288,Sunny->Sunny->Sunny->,0.0010368)
  48. state=Rainy
  49. p=0.35
  50. triple=(0.020328,Sunny->Rainy->Rainy->,0.009408)
  51. Update U[Rainy]=(0.0221568,Sunny->Rainy->Rainy->Rainy->,0.009408)
  52. final triple=(0.033612,Sunny->Rainy->Rainy->Rainy->,0.009408)

所以,最终的结果是,朋友那边这几天最可能的天气情况是Sunny->Rainy->Rainy->Rainy,它有 0.009408的概率出现。而我们算法的另一个附带的结论是,我们所观察到的朋友这几天的活动序列:Walk->Shop->Clean在 我们的隐马可夫模型之下出现的总概率是0.033612。

参考文献

  1. http://www.ece.ucsb.edu/Faculty/Rabiner/ece259/Reprints/tutorial%20on%20hmm%20and%20applications.pdf
  2. http://googlechinablog.com/2006/04/blog-post_17.html

附:c++主要代码片断

  1. void forward_viterbi(const vector<string> & ob, viterbi_triple_t & vtriple)

  2.     {

  3.     //alias

  4.     map<string, double>& sp = start_prob;

  5.     map<string, map<string, double> > & tp = transition_prob;

  6.     map<string, map<string, double> > & ep = emission_prob;

  7.     // initialization

  8.     InitParameters();

  9.     map<string, viterbi_triple_t> T;

  10.     for (vector<string>::iterator it = states.begin();

  11.     it != states.end();

  12.     ++it)

  13.     {

  14.     viterbi_triple_t foo;

  15.     foo.prob = sp[*it];

  16.     foo.vpath.push_back(*it);

  17.     foo.vprob = sp[*it];

  18.     T[*it] = foo;

  19.     }

  20.     map<string, viterbi_triple_t> U;

  21.     double total = 0;

  22.     vector<string> argmax;

  23.     double valmax = 0;

  24.     double p = 0;

  25.     for (vector<string>::const_iterator itob = ob.begin(); itob != ob.end(); ++itob)

  26.     {

  27.     cout << “observation=<< *itob << endl;

  28.     U.clear();

  29.     for (vector<string>::iterator itNextState = states.begin();

  30.     itNextState != states.end();

  31.     ++itNextState)

  32.     {

  33.     cout <<\tnext_state=<< *itNextState << endl;

  34.     total = 0;

  35.     argmax.clear();

  36.     valmax = 0;

  37.     for (vector<string>::iterator itSrcState = states.begin();

  38.     itSrcState != states.end();

  39.     ++itSrcState)

  40.     {

  41.     cout <<\t\tstate=<< *itSrcState << endl;

  42.     viterbi_triple_t foo = T[*itSrcState];

  43.     p = ep[*itSrcState][*itob] * tp[*itSrcState][*itNextState];

  44.     cout <<\t\t\tp=<< p << endl;

  45.     foo.prob *= p;

  46.     foo.vprob *= p;

  47.     cout <<\t\t\ttriple=<< foo << endl;

  48.     total += foo.prob;

  49.     if (foo.vprob > valmax)

  50.     {

  51.     foo.vpath.push_back(*itNextState);

  52.     argmax = foo.vpath;

  53.     valmax = foo.vprob;

  54.     }

  55.     }

  56.     U[*itNextState] = viterbi_triple_t(total, argmax, valmax);

  57.     cout <<\tUpdate U[" << *itNextState << "]=<< U[*itNextState] << “” << endl;

  58.     }

  59.     T.swap(U);

  60.     }

  61.     total = 0;

  62.     argmax.clear();

  63.     valmax = 0;

  64.     for (vector<string>::iterator itState = states.begin();

  65.     itState != states.end();

  66.     ++itState)

  67.     {

  68.     viterbi_triple_t foo = T[*itState];

  69.     total += foo.prob;

  70.     if (foo.vprob > valmax)

  71.     {

  72.     argmax.swap(foo.vpath);

  73.     valmax = foo.vprob;

  74.     }

  75.     }

  76.     vtriple.prob = total;

  77.     vtriple.vpath = argmax;

  78.     vtriple.vprob = valmax;

  79.     cout << “final triple=<< vtriple << endl;

  80.     }

阅读(610) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~