www.pudn.com > hmmPlatform.rar > HMM.cpp


#include "StdAfx.h" 
#include ".\hmm.h" 
#include  
 
////////////////////////////////////////////////////////////////////////// 
// 构造函数 
//  
// 创建人:		陈文凯 
// 创建日期:	2005-06-05 
// 修改人: 
// 修改日期: 
CHMM::CHMM() 
{ 
	// 设定数据 
	this->m_pPi = NULL; 
	this->m_pA = NULL;  
	this->m_pB = NULL; 
	this->m_pCodeBook = NULL; 
} 
 
////////////////////////////////////////////////////////////////////////// 
// 析构函数 
//  
// 创建人:		陈文凯 
// 创建日期:	2005-06-05 
// 修改人: 
// 修改日期: 
CHMM::~CHMM(void) 
{ 
	this->Dispose(); 
} 
 
////////////////////////////////////////////////////////////////////////// 
// 释放模型占用资源 
//  
// 创建人:		陈文凯 
// 创建日期:	2005-06-09 
// 修改人: 
// 修改日期: 
void CHMM::Dispose() 
{ 
	if (this->m_pA != NULL) 
	{ 
		delete[] this->m_pA; 
		this->m_pA = NULL; 
	} 
 
	if (this->m_pB != NULL) 
	{ 
		delete[] this->m_pB; 
		this->m_pB = NULL; 
	} 
 
	if (this->m_pPi != NULL) 
	{ 
		delete[] this->m_pPi; 
		this->m_pPi = NULL; 
	} 
 
	if (this->m_pCodeBook != NULL) 
	{ 
		delete[] this->m_pCodeBook; 
 
		this->m_pCodeBook = NULL; 
	} 
} 
 
////////////////////////////////////////////////////////////////////////// 
// 导出HMM模型信息 
//  
// 创建人:		陈文凯 
// 创建日期:	2005-06-09 
// 修改人: 
// 修改日期: 
BOOL CHMM::SaveModel(CString strFileName) 
{ 
	CFile outFile(strFileName, CFile::modeCreate | CFile::modeWrite);	 
	// 操作是否成功 
	BOOL bRet = FALSE; 
 
	if (outFile.m_hFile != CFile::hFileNull) 
	{ 
		// 写入数字 
		outFile.Write((LPCTSTR)this->m_strWord, CHMM_WORD_LEN); 
		// 写入HMM状态数 
		outFile.Write(&this->m_nStatusNums, sizeof(unsigned int)); 
		// 写入短时点数 
		outFile.Write(&this->m_nFrameSize, sizeof(unsigned int)); 
		// 写入训练码本长度 
		outFile.Write(&this->m_nCodeNums, sizeof(unsigned int)); 
 
		// 写入Pi序列 
		outFile.Write(this->m_pPi, sizeof(double) * this->m_nStatusNums); 
		// 写入A序列 
		outFile.Write(this->m_pA,  
			sizeof(double) * this->m_nStatusNums * this->m_nStatusNums); 
		// 写入B序列 
		outFile.Write(this->m_pB,  
			sizeof(double) * this->m_nStatusNums * this->m_nCodeNums); 
		// 写入码本 
		outFile.Write(this->m_pCodeBook, sizeof(double) * this->m_nCodeNums); 
 
		// 关闭导出文件 
		outFile.Close(); 
		bRet = TRUE; 
	} 
 
	return bRet; 
} 
 
////////////////////////////////////////////////////////////////////////// 
// 导入HMM模型信息 
//  
// 创建人:		陈文凯 
// 创建日期:	2005-06-09 
// 修改人: 
// 修改日期: 
BOOL CHMM::LoadModel(CString strFileName) 
{ 
	CFile inFile(strFileName, CFile::modeRead | CFile::shareDenyNone); 
	// 操作是否成功 
	BOOL bRet = FALSE; 
	// 保存读入数字 
	char strWord[CHMM_WORD_LEN]; 
 
	if (inFile.m_hFile != CFile::hFileNull) 
	{ 
		// 释放模型占用资源 
		this->Dispose(); 
 
		// 读入数字 
		inFile.Read(&strWord, CHMM_WORD_LEN); 
		this->m_strWord.Empty(); 
		this->m_strWord.Append(strWord, CHMM_WORD_LEN); 
 
		// 读入HMM状态数 
		inFile.Read(&this->m_nStatusNums, sizeof(unsigned int)); 
		// 读入短时点数 
		inFile.Read(&this->m_nFrameSize, sizeof(unsigned int)); 
		// 读入训练码本长度 
		inFile.Read(&this->m_nCodeNums, sizeof(unsigned int)); 
 
		// 分配参数空间	 
		this->m_pPi = new double[this->m_nStatusNums]; 
		this->m_pA = new double[this->m_nStatusNums * this->m_nStatusNums]; 
		this->m_pB = new double[this->m_nStatusNums * this->m_nCodeNums]; 
		this->m_pCodeBook = new double[this->m_nCodeNums]; 
 
		// 读入Pi序列 
		inFile.Read(this->m_pPi, sizeof(double) * this->m_nStatusNums); 
		// 读入A序列 
		inFile.Read(this->m_pA,  
			sizeof(double) * this->m_nStatusNums * this->m_nStatusNums); 
		// 读入B序列 
		inFile.Read(this->m_pB,  
			sizeof(double) * this->m_nStatusNums * this->m_nCodeNums); 
		// 读入码本 
		inFile.Read(this->m_pCodeBook, sizeof(double) * this->m_nCodeNums); 
 
		// 关闭导入文件 
		inFile.Close(); 
		bRet = TRUE; 
	} 
 
	return bRet; 
} 
 
////////////////////////////////////////////////////////////////////////// 
// 实现对HMM模型的迭代训练 
//  
// 创建人:		陈文凯 
// 创建日期:	2005-06-09 
// 修改人: 
// 修改日期: 
void CHMM::Train( 
				 const double* pDataIn,		// 输入采样序列 
				 unsigned int nInLen		// 输入采样序列长度 
				 ) 
{ 
	// 前后向算法返回参数 
	double fRate = 0; 
	double* pa = NULL; 
	double* pb = NULL; 
 
	// 对输入采样序列进行矢量量化形成的码本 
	double* pCodeBook = NULL; 
	// 输入码本对应的HMM模型下标 
	unsigned int* pCodeIndex = NULL; 
 
	// 判断输入数据是否合法 
	if (pDataIn != NULL && this->m_pA != NULL &&  
		this->m_pB != NULL && this->m_pPi != NULL) 
	{ 
		pa = new double[this->m_nCodeNums * this->m_nStatusNums]; 
		pb = new double[this->m_nCodeNums * this->m_nStatusNums]; 
 
		// 对输入采样序列进行矢量量化 
		pCodeBook = new double[this->m_nCodeNums]; 
		CVQ::KMeansCluster( 
			pDataIn, nInLen, pCodeBook, this->m_nCodeNums); 
 
		// 获得输入码本对应的HMM模型码本下标 
		pCodeIndex = new unsigned int[this->m_nCodeNums]; 
		CVQ::Classify( 
			pCodeBook, this->m_nCodeNums, this->m_pCodeBook, this->m_nCodeNums, pCodeIndex); 
 
		// 调用前后向算法, 计算概率, pa, pb 
		fRate = this->ForwardBackward( 
			pCodeIndex, this->m_nCodeNums, this->m_pPi, this->m_pA,  
			this->m_pB, this->m_nStatusNums, pa, pb); 
 
		// 调用BW算法进行参数优化 
		this->BaumWelch( 
			pCodeIndex, this->m_nCodeNums, this->m_pPi, this->m_pA, 
			this->m_pB, this->m_nStatusNums, pa, pb, fRate); 
 
		// 更新码本 
		for (unsigned int i = 0; i < this->m_nCodeNums; i++) 
		{ 
			// this->m_pCodeBook[i] = (this->m_pCodeBook[i] + pCodeBook[i]) / 2; 
			this->m_pCodeBook[i] = this->m_pCodeBook[i] * 0.7 + pCodeBook[i] * 0.3; 
		} 
 
		delete[] pa; 
		delete[] pb; 
		delete[] pCodeBook; 
		delete[] pCodeIndex; 
	} 
} 
 
////////////////////////////////////////////////////////////////////////// 
// 实现对HMM模型得初始化训练 
//  
// 创建人:		陈文凯 
// 创建日期:	2005-06-09 
// 修改人: 
// 修改日期: 
void CHMM::PrepareTrain(  
						CString strWord,  
						const double* pDataIn,	// 第一个输入采样序列 
						unsigned int nInLen,	// 输入采样序列长度 
						unsigned int nFrameSize,// 分帧宽度 
						unsigned int nStatusNums /* = 6 */,   
						unsigned int nCodeNums	 /* = 4   */ 
						) 
{ 
	// 计算概率 
	double fRate = 0; 
	// 循环变量 
	unsigned int i = 0; 
	unsigned int j = 0; 
 
	// 设置模型信息 
	this->m_strWord = strWord; 
	this->m_nStatusNums = nStatusNums; 
	this->m_nCodeNums = nCodeNums; 
	this->m_nFrameSize = nFrameSize; 
 
	// 初始化pI/pA/pB/pCodeBook 
	this->m_pPi = new double[this->m_nStatusNums]; 
	this->m_pA = new double[this->m_nStatusNums * this->m_nStatusNums]; 
	this->m_pB = new double[this->m_nStatusNums * this->m_nCodeNums]; 
	this->m_pCodeBook = new double[this->m_nCodeNums]; 
 
	memset(this->m_pPi, 0, sizeof(double) * this->m_nStatusNums); 
	memset(this->m_pA, 0, sizeof(double) * this->m_nStatusNums * this->m_nStatusNums); 
	memset(this->m_pB, 0, sizeof(double) * this->m_nStatusNums * this->m_nCodeNums); 
	memset(this->m_pCodeBook, 0, sizeof(double) * this->m_nCodeNums); 
 
	// 对pI进行均分 
	fRate = (double) 1 / nStatusNums; 
	for (i = 0; i < nStatusNums; i++) 
	{ 
		this->m_pPi[i] = fRate; 
	} 
 
	// 对PA[i][j]进行均分 
	for (i = 0; i < nStatusNums; i++) 
	{ 
		for (j = 0; j < nStatusNums; j++) 
		{ 
			this->m_pA[i * nStatusNums + j] = fRate; 
		} 
	} 
 
	// 对pB[i][j]进行均分 
	fRate = (double) 1 / nCodeNums; 
	for (i = 0; i < nStatusNums; i++) 
	{ 
		for (j = 0; j < nCodeNums; j++) 
		{ 
			this->m_pB[i * nCodeNums + j] = fRate; 
		} 
	} 
 
	// 形成初始码本 
	CVQ::KMeansCluster(pDataIn, nInLen, this->m_pCodeBook, this->m_nCodeNums); 
 
	// 进行参数优化 
	this->Train(pDataIn, nInLen); 
} 
 
////////////////////////////////////////////////////////////////////////// 
// 实现前后向算法,对于给定的HMM参数和观察序列O, 
// 求能够产生观察序列O的概率 
//  
// 创建人:		陈文凯 
// 创建日期:	2005-06-08 
// 修改人: 
// 修改日期: 
double CHMM::ForwardBackward( 
							 const unsigned int* pCodeBook,	// 输入观察码本序列对应的HMM码本的下标 
							 unsigned int nCodeNums,		// 输入观察序列长度 
							 const double* pPi,				// HMM的pi矢量 
							 const double* pA,				// HMM的A矩阵 
							 const double* pB,				// HMM的B矩阵 
							 unsigned int nStatusNums,		// HMM状态数 
							 double* pa,					// 前后向算法中的a 
							 double* pb						// 前后向算法中的b 
							 ) 
{ 
	// 能够产生观察序列O的概率 
	double fRate = 0; 
	// 递推时做累积 
	double fSum = 0; 
 
	// 循环变量,damn vc 
	// 输入序列循环 
	int t = 0; 
	// 状态循环 
	int i = 0; 
	int j = 0; 
 
	// 初始化pa, pb 
	memset(pa, 0, sizeof(double) * (nCodeNums * nStatusNums)); 
	memset(pb, 0, sizeof(double) * (nCodeNums * nStatusNums)); 
 
	// 实现前后向算法 
	// 初始化 
	for (i = 0; i < nStatusNums; i++) 
	{ 
		// 计算公式:pa[1][i] = pI[i] * pB[i][o1]	1 <= i <= N 
		pa[0 * nStatusNums + i] = pPi[i] * pB[i * nCodeNums + pCodeBook[0]]; 
 
		// 计算公式:pb[nCodeNums - 1][i] = 1 
		pb[(nCodeNums - 1) * nStatusNums + i] = 1; 
	} 
 
	// 前向递推 
	for (t = 1; t < nCodeNums; t++) 
	{ 
		for (j = 0; j < nStatusNums; j++) 
		{ 
			fSum = 0; 
 
			for (i = 0; i < nStatusNums; i++) 
			{ 
				// 累积 fSum += pa[t-1][i] * pA[i][j] 
				fSum += pa[(t - 1) * nStatusNums + i] * pA[i * nStatusNums + j]; 
			} 
 
			// 计算公式:pa[t][j] = fSum * pB[j][ot] 
			pa[t * nStatusNums + j] = fSum * pB[j * nCodeNums + pCodeBook[t]]; 
			TRACE("pa(%d, %d) : %f\n", t, j, pa[t * nStatusNums + j]); 
		} 
	} 
 
	// 后向递推 
	for (t = nCodeNums - 2; t >= 0; t--) 
	{		 
		for (i = 0; i < nStatusNums; i++) 
		{ 
			fSum = 0; 
 
			for (j = 0; j < nStatusNums; j++) 
			{ 
				// 累积 fSum += pA[i][j] * pB[j][t + 1] * pb[t + 1][j] 
				fSum += pA[i * nStatusNums + j] *  
					pB[j * nCodeNums + pCodeBook[t + 1]] *  
					pb[(t + 1) * nStatusNums + j]; 
			} 
 
			pb[t * nStatusNums + i] = fSum; 
			TRACE("pb(%d, %d): %lf\n", t, i, pb[t * nStatusNums + i]); 
		} 
	} 
 
	// 结束 
	// 计算概率,令t=nCodeNums-1 
	t = nCodeNums - 1; 
	for (i = 0; i < nStatusNums; i++) 
	{ 
		// 计算公式:fRate += pa[t][i] * pb[t][i]; 
		fRate += pa[t * nStatusNums + i] * pb[t * nStatusNums + i]; 
	} 
 
	return fRate; 
} 
 
////////////////////////////////////////////////////////////////////////// 
// 实现Viterbi算法,对于给定的HMM参数和观察序列O, 
// 返回最大概率 
//  
// 创建人:		陈文凯 
// 创建日期:	2005-06-08 
// 修改人: 
// 修改日期: 
double CHMM::Viterbi( 
					 const unsigned int* pCodeBook,	// 输入观察码本序列对应的HMM码本的下标 
					 unsigned int nCodeNums,		// 输入观察序列长度 
					 const double* pPi,				// HMM的pi矢量 
					 const double* pA,				// HMM的A矩阵 
					 const double* pB,				// HMM的B矩阵 
					 unsigned int nStatusNums		// HMM状态数 
					 ) 
{ 
	// pa[t][i]为t时刻沿着一条路径q1, q2, ..., qt,且qt = st 
	// 产生出输入观察序列o1, o2, o3, ..., ot的最大概率 
	// 1 <= t <= nInLen, 1 <= i <= nStatusNums 
	double* pa = NULL; 
 
	// 临时计算 
	double fTemp = 0; 
	double fMax = 0; 
 
	// 最大概率 
	double fMaxRate = 0; 
 
	// 循环变量 
	// 采样序列循环 
	unsigned int t = 0; 
	// 状态循环 
	unsigned int j = 0; 
	unsigned int i = 0; 
 
	// 分配和初始化pa 
	pa = new double[nCodeNums * nStatusNums]; 
	memset(pa, 0, sizeof(double) * (nCodeNums * nStatusNums)); 
 
	// 初始化 
	for (i = 0; i < nStatusNums; i++) 
	{ 
		// 计算公式:pa[1][i] = pi[i] * pB[i][1] 
		pa[0 * nStatusNums + i] = pPi[i] * pB[i * nCodeNums + pCodeBook[0]]; 
	} 
 
	// 递推 
	for (t = 1; t < nCodeNums; t++) 
	{ 
		for (j = 0; j < nStatusNums; j++) 
		{ 
			// 求pa[t - 1][i] * pA[i][j]的最大值, 1 <= i <= N 
			fMax = pa[(t - 1) * nStatusNums + 0] * pA[0 * nStatusNums + j]; 
 
			for (i = 0; i < nStatusNums; i++) 
			{ 
				fTemp = pa[(t - 1) * nStatusNums + i] * pA[i * nStatusNums + j]; 
				fMax = (fMax < fTemp ? fTemp : fMax); 
			} 
 
			// 计算pa[t][j]和pb[t][j] 
			// 计算公式:pa[t][j] = fMax * pB[j][t]; 
			pa[t * nStatusNums + j] = fMax * pB[j * nCodeNums + pCodeBook[t]]; 
		} 
	} 
 
	// 终结 
	// 求最大概率, fMaxRate = max(pa[nCodeNums - 1][i]) 1 <= i <= nStatusNums 
	fMaxRate = pa[(nCodeNums - 1) * nStatusNums + 0]; 
	for (i = 0; i < nStatusNums; i++) 
	{ 
		fTemp = pa[(nCodeNums - 1) * nStatusNums + i]; 
		fMaxRate = (fMaxRate < fTemp ? fTemp : fMaxRate); 
	} 
 
	delete[] pa; 
 
	return fMaxRate; 
} 
 
////////////////////////////////////////////////////////////////////////// 
// 实现Baum Welch算法,对于给定的观察序列O, 
// 求使P(O | HMM参数)最大的HMM参数pi, A, B 
//  
// 创建人:		陈文凯 
// 创建日期:	2005-06-08 
// 修改人: 
// 修改日期: 
void CHMM::BaumWelch( 
					 const unsigned int* pCodeBook,	// 输入观察码本序列对应的HMM码本的下标 
					 unsigned int nCodeNums,		// 输入观察序列长度 
					 double* pPi,					// HMM的pi矢量 
					 double* pA,					// HMM的A矩阵 
					 double* pB,					// HMM的B矩阵 
					 unsigned int nStatusNums,		// HMM状态数 
					 double* pa,					// 前后向算法计算所得的pa 
					 double* pb,					// 前后向算法计算所得pb 
					 double fRate					// 前后向算法计算所得最大概率 
					 ) 
{ 
	// bw算法中概率矩阵 
	// pRate[nCodeNums][nStatusNums][nStatusNums] 
	double* pRate = NULL; 
	// 最小概率 
	double fMinRate = 0.0001; 
	unsigned int nOffSet = nStatusNums * nStatusNums; 
 
	// 循环变量 
	unsigned int t = 0; 
	unsigned int i = 0; 
	unsigned int j = 0; 
	unsigned int k = 0; 
 
	// 求概率时的分子 
	double fAvgUp = 0; 
	// 求概率时的分母 
	double fAvgDown = 0; 
	// 数组元素下标 
	unsigned int nIndex = 0; 
 
	// 初始化概率矩阵 
	pRate = new double[nCodeNums * nOffSet]; 
	memset(pRate, 0, sizeof(double) * nCodeNums * nOffSet); 
 
	// 计算pRate[t][i][j],即t时刻Markov链处于i状态, t+1时刻处于j状态的概率 
	for (t = 0; t < (nCodeNums - 1); t++) 
	{ 
		for (i = 0; i < nStatusNums; i++) 
		{ 
			for (j = 0; j < nStatusNums; j++) 
			{ 
				pRate[t * nOffSet + i * nStatusNums + j] =  
					(pa[t * nStatusNums + i] * pA[i * nStatusNums + j] *  
					pB[j* nCodeNums + pCodeBook[t + 1]] * pb[(t + 1) * nStatusNums + j]) / fRate; 
 
				TRACE("pRate[%d, %d, %d]: %f\n",  
					t, i, j, pRate[t * nOffSet + i * nStatusNums + j]); 
 
				TRACE("pa: %f, pb: %f, pA: %f, pB: %f\n",  
					pa[t * nStatusNums + i], 
					pA[i * nStatusNums + j], 
					pB[j* nCodeNums + pCodeBook[t + 1]], 
					pb[(t + 1) * nStatusNums + j] 
					); 
			} 
		} 
	} 
 
	// 使用重估公式计算HMM参数 
	// 计算Pi矢量, 即0时刻时Markov链处于i状态, t+1时刻处于其他任何状态的概率 
	for (i = 0; i < nStatusNums; i++) 
	{ 
		pPi[i] = 0; 
 
		for (j = 0; j < nStatusNums; j++) 
		{ 
			pPi[i] += pRate[0 * nOffSet + i * nStatusNums + j]; 
		} 
 
		pPi[i] = (pPi[i] > 0.0 ? pPi[i] : fMinRate); 
		TRACE("pi[%d]: %f\n", i, pPi[i]); 
	} 
 
	// 计算A矩阵  
	// pA[i][j] = (从状态Si过渡到Sj的平均次数) / (从状态Si向其他状态转移的平均次数) 
	for (i = 0; i < nStatusNums; i++) 
	{ 
		for (j = 0; j < nStatusNums; j++) 
		{ 
			// 计算状态Si过渡到Sj的平均次数 
			fAvgUp = 0; 
			// 从状态Si向其他状态转移的平均次数 
			fAvgDown = 0; 
 
			for (t = 0; t < nCodeNums; t++) 
			{ 
				fAvgUp += pRate[t * nOffSet + i * nStatusNums + j]; 
 
				for (k = 0; k < nStatusNums; k++) 
				{ 
					fAvgDown += pRate[t * nOffSet + i * nStatusNums + k]; 
				} 
			} 
 
			nIndex = i * nStatusNums + j; 
			pA[nIndex] = fAvgUp / fAvgDown; 
			pA[nIndex] = (pA[nIndex] > 0.0 ? pA[nIndex] : fMinRate); 
			TRACE("pA[%d,%d]: %f\n", i, j, pA[nIndex]); 
		} 
	} 
 
	// 计算B矩阵 
	// pB[i][k] = (出现状态i和观察值Ot=Vk的平均次数) / (处于状态i的次数) 
	for (i = 0; i < nStatusNums; i++) 
	{ 
		for (k = 0; k < nCodeNums; k++) 
		{ 
			// 计算出现状态i和观察值Ot=Vk的平均次数 
			fAvgUp = 0; 
 
			for (t = 0; t < nCodeNums; t++) 
			{ 
				if (pCodeBook[t] == pCodeBook[k]) 
				{ 
					for (j = 0; j < nStatusNums;j ++) 
					{ 
						fAvgUp += pRate[t * nOffSet + i * nStatusNums + j]; 
					} 
				} 
			} 
 
			// 处于状态i的次数 
			fAvgDown = 0; 
			for (t = 0; t < nCodeNums; t++) 
			{ 
				for (j = 0; j < nStatusNums; j++) 
				{ 
					fAvgDown += pRate[t * nOffSet + i * nStatusNums + j]; 
				} 
			} 
 
			// 计算pB[i][k] 
			nIndex = i * nCodeNums + pCodeBook[k]; 
			pB[nIndex] = fAvgUp / fAvgDown; 
			pB[nIndex] = (pB[nIndex] > 0.0 ? pB[nIndex] : fMinRate); 
			TRACE("pB[%d, %d]: %f\n", i, j, pB[nIndex]); 
		} 
	} 
 
	// 释放资源 
	if (pRate != NULL) 
	{ 
		delete[] pRate; 
	} 
} 
 
////////////////////////////////////////////////////////////////////////// 
// 基于Vertibi算法实现对HMM模型的识别 
CString CHMM::RecogonizeByViterbi( 
								  const double* pCodeBook,	// 输入码本 
								  unsigned int nCodeNums,	// 输入码本长度 
								  CString* strModelList,	// 模型文件路径 
								  unsigned int nModelCount	// 模型数量 
								  ) 
{ 
	CString strWord; 
	// 最大概率 
	double fMaxRate = 0; 
	// 识别概率 
	double fRate = 0; 
	// 模型 
	CHMM hmmModel; 
	// 输入码本对应的HMM码本下标 
	unsigned int* pCodeIndex = NULL; 
 
	if (pCodeBook != NULL && strModelList != NULL) 
	{ 
		// 初始化输入码本对应的HMM码本下标 
		pCodeIndex = new unsigned int[nCodeNums]; 
 
		for (unsigned int i = 0; i < nModelCount; i++) 
		{ 
			if (hmmModel.LoadModel(strModelList[i])) 
			{ 
				// 获取输入码本对应的HMM码本下标 
				CVQ::Classify( 
					pCodeBook, nCodeNums, hmmModel.m_pCodeBook, hmmModel.m_nCodeNums, pCodeIndex); 
 
				fRate = hmmModel.Viterbi( 
					pCodeIndex, nCodeNums, hmmModel.m_pPi, 
					hmmModel.m_pA, hmmModel.m_pB, hmmModel.m_nStatusNums); 
 
				// 保存最大概率 
				if (fMaxRate < fRate) 
				{ 
					fMaxRate = fRate; 
 
					// 保存最大概率模型对应单词 
					strWord = hmmModel.GetWord(); 
				} 
			} 
		} 
 
		delete[] pCodeIndex; 
	} 
 
	return strWord; 
} 
 
////////////////////////////////////////////////////////////////////////// 
// 基于DTW的Vertibi实现对HMM模型的识别 
CString CHMM::RecogonizeByDTW( 
							  const double* pCodeBook,	// 输入码本 
							  unsigned int nCodeNums,	// 输入码本长度 
							  CString* strModelList,	// 模型文件路径 
							  unsigned int nModelCount	// 模型数量 
						   ) 
{ 
	CString strWord; 
	// 最大概率 
	double fMaxRate = 0; 
	// 识别概率 
	double fRate = 0; 
	// 模型 
	CHMM hmmModel; 
	// 输入码本对应的HMM码本下标 
	unsigned int* pCodeIndex = NULL; 
 
	if (pCodeBook != NULL && strModelList != NULL) 
	{ 
		// 初始化输入码本对应的HMM码本下标 
		pCodeIndex = new unsigned int[nCodeNums]; 
 
		for (unsigned int i = 0; i < nModelCount; i++) 
		{ 
			if (hmmModel.LoadModel(strModelList[i])) 
			{ 
				// 获取输入码本对应的HMM码本下标 
				CVQ::Classify( 
					pCodeBook, nCodeNums, hmmModel.m_pCodeBook, hmmModel.m_nCodeNums, pCodeIndex); 
 
				fRate = hmmModel.DTW( 
					pCodeIndex, nCodeNums, 
					hmmModel.m_pA, hmmModel.m_pB, hmmModel.m_nStatusNums); 
 
				// 保存最大概率 
				if (fMaxRate < fRate) 
				{ 
					fMaxRate = fRate; 
 
					// 保存最大概率模型对应单词 
					strWord = hmmModel.GetWord(); 
				} 
			} 
		} 
 
		delete[] pCodeIndex; 
	} 
 
	return strWord; 
} 
 
////////////////////////////////////////////////////////////////////////// 
// 实现基于VQ的HMM模型识别 
CString CHMM::RecogonizeByVQ( 
							 const double* pCodeBook,	// 输入码本 
							 unsigned int nCodeNums,	// 输入码本长度 
							 CString* strModelList,	// 模型文件路径 
							 unsigned int nModelCount	// 模型数量 
							 ) 
{ 
	CString strWord; 
	// 最小距离 
	double fMinDis = 30000; 
	// 码本距离 
	double fDis = 0; 
	// 最小距离模型下标 
	unsigned int nMinModel = 0; 
	// 模型 
	CHMM hmmModel; 
 
	if (pCodeBook != NULL && strModelList != NULL) 
	{ 
		for (unsigned int i = 0; i < nModelCount; i++) 
		{ 
			if (hmmModel.LoadModel(strModelList[i])) 
			{ 
				fDis = CVQ::GetDistance(pCodeBook, hmmModel.m_pCodeBook, nCodeNums); 
 
				// 保存最小距离 
				if (fDis < fMinDis) 
				{ 
					fMinDis = fDis; 
 
					// 保存最小距离模型对应单词 
					strWord = hmmModel.GetWord(); 
					TRACE("%d, %f\n", i, fMinDis); 
				} 
			} 
		} 
	} 
 
	return strWord; 
} 
 
////////////////////////////////////////////////////////////////////////// 
// 实现基于DTW的Viterbi算法 
double CHMM::DTW( 
				 const unsigned int* pCodeBook,	// 输入观察码本序列对应的HMM码本的下标 
				 unsigned int nCodeNums,		// 输入观察序列长度 
				 double* pA,					// HMM的A矩阵 
				 double* pB,					// HMM的B矩阵 
				 unsigned int nStatusNums		// HMM状态数 
				 ) 
{ 
	double* pRate = NULL; 
	// 返回的D(nStatusNums, nInLen) 
	double fRetRate = 0; 
	// 计算最大D + A 
	double fMaxD = 0; 
	double fTemp1 = 0; 
	double fTemp2 = 0; 
 
	if (pCodeBook != NULL) 
	{ 
		// 初始话概率矩阵 
		pRate = new double[nCodeNums * nStatusNums]; 
		memset(pRate, 0, sizeof(double) * nCodeNums * nStatusNums); 
 
		for (unsigned int i = 0; i < nStatusNums; i++) 
		{ 
			for (unsigned int j = 1; j < nCodeNums; j++) 
			{ 
				// 计算D(i-1, j) + A(j, j) 
				fTemp1 = ((i - 1) >= 0 ? pRate[(i - 1) * nStatusNums + j] : 0); 
				fTemp1 += pA[j * nStatusNums + j]; 
 
				// 计算D(i-1, j-1) + A(j-1, j); 
				fTemp2 = ((i - 1) >= 0 && (j - 1) >= 0 ?  
					pRate[(i - 1) * nStatusNums + (j - 1)] : 0); 
				fTemp2 += ((j - 1) >= 0 ? pA[(j - 1) * nStatusNums + j] : 0); 
 
				// 取D(i, j) = B(i, j) + max(fTemp1, fTemp2); 
				fMaxD = (fTemp1 > fTemp2 ? fTemp1 : fTemp2); 
				pRate[i * nCodeNums + j] = pB[i * nCodeNums + pCodeBook[j]] + fMaxD; 
			} 
		} 
 
		fRetRate = pRate[(nCodeNums - 1) * nStatusNums + (nStatusNums - 1)]; 
 
		// 释放所占用资源 
		delete[] pRate; 
	} 
 
	return fRetRate; 
}