维特比算法在隐马尔可夫模型中的应用

前言

文章标题的两个概念也许对于许多同学们来说都相对比较陌生,都比较偏向于于理论方面的知识,但是这个算法非常的强大,在很多方面都会存在他的影子。2个概念,1个维特比算法,1个隐马尔可夫模型。你很难想象,输入法的设计也会用到其中的一些知识。

HMM-隐马尔可夫模型

隐马尔可夫模型如果真的要展开来讲,那短短的一篇文章当然无法阐述的清,所以我会以最简单的方式解释。隐马尔可夫模型简称HMM,根据百度百科中的描述,隐马尔可夫模型描述的是一个含有隐含未知参数的马尔可夫模型。模型的本质是从观察的参数中获取隐含的参数信息。一般的在马尔可夫模型中,前后之间的特征会存在部分的依赖影响。示例图如下:

隐马尔可夫模型在语音识别中有广泛的应用。其实在输入法中也有用到。举个例子,假设我输入wszs,分别代表4个字,所以观察特征就是w, s, z, s,那么我想挖掘出他所想表达的信息,也就是我想打出的字是什么,可能是"我是张三",又可能是“晚上再说”,这个就是可能的信息,最可能的信息,就会被放在输入选择越靠前的位置。在这里我们就会用到一个叫维特比的算法了。

Viterbi-维特比算法

维特比算法这个名字的产生是以著名科学家维特比命名的,维特比算法是数字通信中非常常用的一种算法。那么维特比算法到底是做什么的呢。简单的说,他是一种特殊的动态规划算法,也就是DP问题。但是这里可不是单纯的寻找最短路径这些问题,可能他是需要寻找各个条件因素下最大概率的一条路径,假设针对观察特征,会有多个隐含特征值的情况。比如下面这个是多种隐含变量的组合情况,形成了一个密集的篱笆网络。

于是问题就转变成了,如何在这么多的路径中找到最佳路径。如果这是输入法的例子,上面的每一列的值就是某个拼音下对应的可能的字。于是我们就很容易联想到可以用dp的思想去做,每次求得相邻变量之间求得后最佳的值,存在下一列的节点上,而不是组合这么多种情况去算。时间复杂度能降低不少。但是在马尔可夫模型中,你还要考虑一些别的因素。所以总的来说,维特比算法就是一种利用动态规划算法寻找最有可能产生观察序列的隐含信息序列,尤其是在类似于隐马尔可夫模型的应用中。

算法实例

下面给出一个实际例子,来说明一下维特比算法到底怎么用,如果你用过动态规划算法,相信一定能很迅速的理解我想表达的意思。下面这个例子讲的是海藻的观察特征与天气的关系,通过观测海藻的特征状态,退出当天天气的状况。当然当天天气的预测还可能受昨天的天气影响,所以这是一个很棒的隐马尔可夫模型问题。问题的描述是下面这段话:

假设连续观察3天的海藻湿度为(Dry,Damp,Soggy),求这三天最可能的天气情况。天气只有三类(Sunny,Cloudy,Rainy),而且海藻湿度和天气有一定的关系。问题具体描述,链接在此

ok,状态转移概率矩阵和混淆矩阵都已给出,详细代码以及相关数据,请点击链接:https://github.com/linyiqun/DataMiningAlgorithm/tree/master/Others/DataMining_Viterbi

直接给出代码解答,主算法类ViterbiTool.java:

package DataMining_Viterbi;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * 维特比算法工具类
 *
 * @author lyq
 *
 */
public class ViterbiTool {
	// 状态转移概率矩阵文件地址
	private String stmFilePath;
	// 混淆矩阵文件地址
	private String confusionFilePath;
	// 初始状态概率
	private double[] initStatePro;
	// 观察到的状态序列
	public String[] observeStates;
	// 状态转移矩阵值
	private double[][] stMatrix;
	// 混淆矩阵值
	private double[][] confusionMatrix;
	// 各个条件下的潜在特征概率值
	private double[][] potentialValues;
	// 潜在特征
	private ArrayList<String> potentialAttrs;
	// 属性值列坐标映射图
	private HashMap<String, Integer> name2Index;
	// 列坐标属性值映射图
	private HashMap<Integer, String> index2name;

	public ViterbiTool(String stmFilePath, String confusionFilePath,
			double[] initStatePro, String[] observeStates) {
		this.stmFilePath = stmFilePath;
		this.confusionFilePath = confusionFilePath;
		this.initStatePro = initStatePro;
		this.observeStates = observeStates;

		initOperation();
	}

	/**
	 * 初始化数据操作
	 */
	private void initOperation() {
		double[] temp;
		int index;
		ArrayList<String[]> smtDatas;
		ArrayList<String[]> cfDatas;

		smtDatas = readDataFile(stmFilePath);
		cfDatas = readDataFile(confusionFilePath);

		index = 0;
		this.stMatrix = new double[smtDatas.size()][];
		for (String[] array : smtDatas) {
			temp = new double[array.length];
			for (int i = 0; i < array.length; i++) {
				try {
					temp[i] = Double.parseDouble(array[i]);
				} catch (NumberFormatException e) {
					temp[i] = -1;
				}
			}

			// 将转换后的值赋给数组中
			this.stMatrix[index] = temp;
			index++;
		}

		index = 0;
		this.confusionMatrix = new double[cfDatas.size()][];
		for (String[] array : cfDatas) {
			temp = new double[array.length];
			for (int i = 0; i < array.length; i++) {
				try {
					temp[i] = Double.parseDouble(array[i]);
				} catch (NumberFormatException e) {
					temp[i] = -1;
				}
			}

			// 将转换后的值赋给数组中
			this.confusionMatrix[index] = temp;
			index++;
		}

		this.potentialAttrs = new ArrayList<>();
		// 添加潜在特征属性
		for (String s : smtDatas.get(0)) {
			this.potentialAttrs.add(s);
		}
		// 去除首列无效列
		potentialAttrs.remove(0);

		this.name2Index = new HashMap<>();
		this.index2name = new HashMap<>();

		// 添加名称下标映射关系
		for (int i = 1; i < smtDatas.get(0).length; i++) {
			this.name2Index.put(smtDatas.get(0)[i], i);
			// 添加下标到名称的映射
			this.index2name.put(i, smtDatas.get(0)[i]);
		}

		for (int i = 1; i < cfDatas.get(0).length; i++) {
			this.name2Index.put(cfDatas.get(0)[i], i);
		}
	}

	/**
	 * 从文件中读取数据
	 */
	private ArrayList<String[]> readDataFile(String filePath) {
		File file = new File(filePath);
		ArrayList<String[]> dataArray = new ArrayList<String[]>();

		try {
			BufferedReader in = new BufferedReader(new FileReader(file));
			String str;
			String[] tempArray;
			while ((str = in.readLine()) != null) {
				tempArray = str.split(" ");
				dataArray.add(tempArray);
			}
			in.close();
		} catch (IOException e) {
			e.getStackTrace();
		}

		return dataArray;
	}

	/**
	 * 根据观察特征计算隐藏的特征概率矩阵
	 */
	private void calPotencialProMatrix() {
		String curObserveState;
		// 观察特征和潜在特征的下标
		int osIndex;
		int psIndex;
		double temp;
		double maxPro;
		// 混淆矩阵概率值,就是相关影响的因素概率
		double confusionPro;

		this.potentialValues = new double[observeStates.length][potentialAttrs
				.size() + 1];
		for (int i = 0; i < this.observeStates.length; i++) {
			curObserveState = this.observeStates[i];
			osIndex = this.name2Index.get(curObserveState);
			maxPro = -1;

			// 因为是第一个观察特征,没有前面的影响,根据初始状态计算
			if (i == 0) {
				for (String attr : this.potentialAttrs) {
					psIndex = this.name2Index.get(attr);
					confusionPro = this.confusionMatrix[psIndex][osIndex];

					temp = this.initStatePro[psIndex - 1] * confusionPro;
					this.potentialValues[BaseNames.DAY1][psIndex] = temp;
				}
			} else {
				// 后面的潜在特征受前一个特征的影响,以及当前的混淆因素影响
				for (String toDayAttr : this.potentialAttrs) {
					psIndex = this.name2Index.get(toDayAttr);
					confusionPro = this.confusionMatrix[psIndex][osIndex];

					int index;
					maxPro = -1;
					// 通过昨天的概率计算今天此特征的最大概率
					for (String yAttr : this.potentialAttrs) {
						index = this.name2Index.get(yAttr);
						temp = this.potentialValues[i - 1][index]
								* this.stMatrix[index][psIndex];

						// 计算得到今天此潜在特征的最大概率
						if (temp > maxPro) {
							maxPro = temp;
						}
					}

					this.potentialValues[i][psIndex] = maxPro * confusionPro;
				}
			}
		}
	}

	/**
	 * 根据同时期最大概率值输出潜在特征值
	 */
	private void outputResultAttr() {
		double maxPro;
		int maxIndex;
		ArrayList<String> psValues;

		psValues = new ArrayList<>();
		for (int i = 0; i < this.potentialValues.length; i++) {
			maxPro = -1;
			maxIndex = 0;

			for (int j = 0; j < potentialValues[i].length; j++) {
				if (this.potentialValues[i][j] > maxPro) {
					maxPro = potentialValues[i][j];
					maxIndex = j;
				}
			}

			// 取出最大概率下标对应的潜在特征
			psValues.add(this.index2name.get(maxIndex));
		}

		System.out.println("观察特征为:");
		for (String s : this.observeStates) {
			System.out.print(s + ", ");
		}
		System.out.println();

		System.out.println("潜在特征为:");
		for (String s : psValues) {
			System.out.print(s + ", ");
		}
		System.out.println();
	}

	/**
	 * 根据观察属性,得到潜在属性信息
	 */
	public void calHMMObserve() {
		calPotencialProMatrix();
		outputResultAttr();
	}
}

测试结果输出:

观察特征为:
Dry, Damp, Soggy,
潜在特征为:
Sunny, Cloudy, Rainy, 

参考文献

百度百科-隐马尔可夫模型

百度百科-维特比

<<数学之美>>第二版-吴军

http://blog.csdn.net/jeiwt/article/details/8076739

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-25 07:03:04

维特比算法在隐马尔可夫模型中的应用的相关文章

隐马尔可夫模型中基于比例因子的前向算法(java实现)

直接上干货哈,其他子算法,后续补上. 1 package jxutcm.edu.cn.hmm.model;  2   3 import jxutcm.edu.cn.hmm.bean.HMMHelper;  4 import jxutcm.edu.cn.util.TCMMath;  5   6 /**  7  * [改进后的前向算法]  8  * [带比例因子修正的前向算法 :计算观察序列的概率 ]  9  * [注意] 改进后,就没必要使用后向算法来求观测序列概率了,直接利用中间比例因子scal

隐马尔可夫模型HMM与维特比Veterbi算法(二)

隐马尔可夫模型HMM与维特比Veterbi算法(二) 主要内容: 前向算法(Forward Algorithm) 穷举搜索( Exhaustive search for solution) 使用递归降低问题复杂度 前向算法的定义 程序实现前向算法 举例说明前向算法 一.前向算法(Forward Algorithm) 目标:计算观察序列的概率(Finding the probability of an observed sequence) 1. 穷举搜索( Exhaustive search fo

机器学习算法总结(七)——隐马尔科夫模型(前向后向算法、鲍姆-韦尔奇算法、维特比算法)

概率图模型是一类用图来表达变量相关关系的概率模型.它以图为表示工具,最常见的是用一个结点表示一个或一组随机变量,结点之间的变表是变量间的概率相关关系.根据边的性质不同,可以将概率图模型分为两类:一类是使用有向无环图表示变量间的依赖关系,称为有向图模型或贝叶斯网:另一类是使用无向图表示变量间的相关关系,称为无向图模型或马尔科夫网. 隐马尔科夫模型(简称HMM)是结构最简单的动态贝叶斯网,是一种著名的有向图模型,主要用于时间序数据建模,在语音识别,自然语言处理,生物信息,模式识别中有着广泛的应用,虽

隐马尔科夫模型(前向后向算法、鲍姆-韦尔奇算法、维特比算法)

概率图模型是一类用图来表达变量相关关系的概率模型.它以图为表示工具,最常见的是用一个结点表示一个或一组随机变量,结点之间的变表是变量间的概率相关关系.根据边的性质不同,可以将概率图模型分为两类:一类是使用有向无环图表示变量间的依赖关系,称为有向图模型或贝叶斯网:另一类是使用无向图表示变量间的相关关系,称为无向图模型或马尔科夫网. 隐马尔科夫模型(简称HMM)是结构最简单的动态贝叶斯网,是一种著名的有向图模型,主要用于时间序数据建模,在语音识别,自然语言处理,生物信息,模式识别中有着广泛的应用,虽

一文搞懂HMM(隐马尔可夫模型)

本文转自于:http://www.cnblogs.com/skyme/p/4651331.html 隐马尔可夫模型(Hidden Markov Model,HMM)是统计模型,它用来描述一个含有隐含未知参数的马尔可夫过程.其难点是从可观察的参数中确定该过程的隐含参数.然后利用这些参数来作进一步的分析,例如模式识别. 是在被建模的系统被认为是一个马尔可夫过程与未观测到的(隐藏的)的状态的统计马尔可夫模型. 下面用一个简单的例子来阐述: 假设我手里有三个不同的骰子.第一个骰子是我们平常见的骰子(称这

隐马尔可夫模型(HMM:Hidden Markov Models)

理论部分转载自:http://blog.csdn.net/likelet/article/details/7056068 手动计算例子转载自:http://blog.sina.com.cn/s/blog_953f8a550100zh35.html 隐马尔可夫模型 (Hidden Markov Model,HMM) 最初由 L. E. Baum 和其它一些学者发表在一系列的统计学论文中,随后在语言识别,自然语言处理以及生物信息等领域体现了很大的价值.平时,经常能接触到涉及 HMM 的相关文章,一直

炎热天气看书还是钓鱼?隐马尔科夫模型教你预测!

高温天气与行为概率 夏季是一年最热的时候,气温普遍偏高,一般把日最高气温达到35℃以上的天气叫作高温天气,但是一般情况下高温天气分为两类.  (1)干热型高温.一般是指气温较高.太阳辐射强而且空气的湿度较小的高温天气.  (2)闷热型高温.一般是指水汽丰富,但是气温相对而言并不算太高,给人感受闷热.  小张在不同类型下的高温天气下会有不同的行为,但是归纳起来为主要为散步.垂钓.看书三类,分别在干热型高温和闷热型高温下对应行为的概率见下表.  假设干热型高温和闷热型高温之间会进行相互转变,每天可能

隐马尔科夫模型python实现简单拼音输入法

在网上看到一篇关于隐马尔科夫模型的介绍,觉得简直不能再神奇,又在网上找到大神的一篇关于如何用隐马尔可夫模型实现中文拼音输入的博客,无奈大神没给可以运行的代码,只能纯手动网上找到了结巴分词的词库,根据此训练得出隐马尔科夫模型,用维特比算法实现了一个简单的拼音输入法.githuh地址:https://github.com/LiuRoy/Pinyin_Demo 原理简介 隐马尔科夫模型 抄一段网上的定义: 隐马尔可夫模型 (Hidden Markov Model) 是一种统计模型,用来描述一个含有隐含

隐马尔可夫模型(HMM)中文分词

1. 马尔可夫模型 如果一个系统有n个有限状态$S=\{s_{1} , s_{2} ,\dots s_{n}\}$,随着时间推移,该系统将从某一状态转移到另一状态,$Q=\{q_{1},q_{2},\dots q_{n}\}$位一个随机变量序列,该序列中的变量取值为状态集S中的某个状态,其中$q_{t}$表示系统在时间t的状态.那么:系统在时间t处于状态$s_{j}$的概率取决于其在时间1,2, $\dots$  t-1的状态,该概率为: $$P(q_{t} = s_{j} | q_{t-1}