如果有看到这篇博客的同学是像我一样什么都不会就直接学自然语言处理,建议先看看《概率论》,把概率的基础看看,理解会更容易。如果是高手,请直接飘到末尾,看参考的博客,那里面写HMM模型很经典。我这里贴出来的唯一价值就是有Java实现的Forward算法。
自我感觉比较笨,一篇关于HMM模型的博客看了不下10次才慢慢能看明白。文中举的例子很形象,天气和水藻的关系。
天气状态与水藻是有关系的。今天的水藻是我们能观察到的状态,而明天的天气则是我们不能预测的。HMM模型就是用来描述昨天、今天、明天的关系的。HMM最不合理的一个假设就是所有时刻天气转变的概率矩阵及水藻与天气关系的概率矩阵是恒定的。
好了,一句话总结我所领悟到的HMM:两个二维矩阵,一个一维矩阵;两种状态。
HMM的学习,有几个算法必须得会的:第一个就是Forward算法。Forward算法有点动态规划的感觉。
Forward算法的Java代码实现如下:(程序功能:观察状态对应于当前HMM的概率)
- package com.xh.hmm;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- /*
- * 定义一个HMM
- * */
- public class HMM {
- private Map<String,Integer> observedState=new HashMap<String, Integer>();
- private Map<String,Integer> hidenState=new HashMap<String, Integer>();
- private double[] pi;
- private double[][] transationMatrix;
- private double[][] confusedMatrix;
- private int osCount;
- private int hsCount;
- public HMM(String[] observedState,
- String[] hideState, double[] pi,
- double[][] transationMatrix, double[][] confusedMatrix) {
- initState( observedState,this.observedState);
- initState( hideState,this.hidenState);
- this.pi = pi;
- this.transationMatrix = transationMatrix;
- this.confusedMatrix = confusedMatrix;
- osCount=confusedMatrix[0].length;
- hsCount=confusedMatrix.length;
- }
- private void initState(String[] states,Map<String, Integer> state){
- int i=0;
- for (String string : states) {
- state.put(string,i++);
- }
- }
- /*
- * 用forward algorithm来计算 一个观察状态被当前HMM生成的概率
- * */
- public double forward_algorithm(String[] observed){
- double[][] midValue=new double[observed.length][hsCount];
- /*
- * 最初始的状态,计算方法为:
- * Initial State Probabilitie*(在Initial State Probabilitie下观察到observed[0]的概率)
- * 这里就用到了pi和confusedMatrix两个矩阵
- * */
- int cur=observedState.get(observed[0]);
- for(int i=0;i<hsCount;i++){
- midValue[0][i]=pi[i]*confusedMatrix[i][cur];
- }
- /*
- * 计算中间状态,计算方法为
- * Pr( observation | hidden state is j ) * Pr(all paths to state j at time t)
- * */
- for(int t=1;t<observed.length;t++){
- cur=observedState.get(observed[t]);
- for(int j=0;j<hsCount;j++){
- double sum=0.0;
- for(int i=0;i<hsCount;i++){
- sum+=midValue[t-1][i]*transationMatrix[i][j];
- }
- midValue[t][j]=sum*confusedMatrix[j][cur];
- }
- }
- /*
- * 计算最后的结果
- * */
- double pro=0.0;
- for(int i=0;i<midValue[observed.length-1].length;i++){
- pro+=midValue[observed.length-1][i];
- }
- return pro;
- }
- }
测试程序如下:
- package com.xh.hmm;
- public class TestHMM {
- public static void main(String[] args) {
- String[] ostate=new String[]{"Dry","Dryish","Damp","Soggy"};
- String[] hstate=new String[]{"Sunny","Cloudy","Rainy"};
- double[] pi=new double[]{0.63,0.17,0.20};
- double[][] A=new double[][]{
- {0.500 , 0.375 , 0.125},
- {0.250 , 0.125 , 0.625},
- {0.250 , 0.375 , 0.375}};
- double[][] B=new double[][]{
- {0.60 , 0.20, 0.15, 0.05},
- {0.25 , 0.25, 0.25, 0.25},
- {0.05 , 0.10, 0.35, 0.50}};
- HMM_2 hmm=new HMM_2(ostate,hstate,pi,A,B);
- String[] observed=new String[]{"Dry","Damp","Soggy"};
- double rs=hmm.forward_algorithm(observed);
- System.out.println(rs);
- }
- }
最终的结果是:0.026901406250000003
实现了HMM算法后,我觉得那个midValue数组太耗费空间了,就修改成了滚动数组的形式。
|
参考博客:
( 隐马尔科夫模型HMM学习(一))http://blog.csdn.net/jackfirst86/article/details/6430275
(Forward算法的动画)(动画有问题,但是很形象,方便理解)
http://www.comp.leeds.ac.uk/roger/HiddenMarkovModels/html_dev/forward_algorithm/s3_pg3.html
(我爱自然语言处理)http://www.52nlp.cn/hmm-learn-best-practices-five-forward-algorithm-5