HMM.cpp
上传用户:goak128
上传日期:2013-07-17
资源大小:155k
文件大小:21k
源码类别:

控制台编程

开发平台:

C/C++

  1. #include "StdAfx.h"
  2. #include ".hmm.h"
  3. #include <complex>
  4. //////////////////////////////////////////////////////////////////////////
  5. // 构造函数
  6. // 
  7. // 创建人: 陈文凯
  8. // 创建日期: 2005-06-05
  9. // 修改人:
  10. // 修改日期:
  11. CHMM::CHMM()
  12. {
  13. // 设定数据
  14. this->m_pPi = NULL;
  15. this->m_pA = NULL; 
  16. this->m_pB = NULL;
  17. this->m_pCodeBook = NULL;
  18. }
  19. //////////////////////////////////////////////////////////////////////////
  20. // 析构函数
  21. // 
  22. // 创建人: 陈文凯
  23. // 创建日期: 2005-06-05
  24. // 修改人:
  25. // 修改日期:
  26. CHMM::~CHMM(void)
  27. {
  28. this->Dispose();
  29. }
  30. //////////////////////////////////////////////////////////////////////////
  31. // 释放模型占用资源
  32. // 
  33. // 创建人: 陈文凯
  34. // 创建日期: 2005-06-09
  35. // 修改人:
  36. // 修改日期:
  37. void CHMM::Dispose()
  38. {
  39. if (this->m_pA != NULL)
  40. {
  41. delete[] this->m_pA;
  42. this->m_pA = NULL;
  43. }
  44. if (this->m_pB != NULL)
  45. {
  46. delete[] this->m_pB;
  47. this->m_pB = NULL;
  48. }
  49. if (this->m_pPi != NULL)
  50. {
  51. delete[] this->m_pPi;
  52. this->m_pPi = NULL;
  53. }
  54. if (this->m_pCodeBook != NULL)
  55. {
  56. delete[] this->m_pCodeBook;
  57. this->m_pCodeBook = NULL;
  58. }
  59. }
  60. //////////////////////////////////////////////////////////////////////////
  61. // 导出HMM模型信息
  62. // 
  63. // 创建人: 陈文凯
  64. // 创建日期: 2005-06-09
  65. // 修改人:
  66. // 修改日期:
  67. BOOL CHMM::SaveModel(CString strFileName)
  68. {
  69. CFile outFile(strFileName, CFile::modeCreate | CFile::modeWrite);
  70. // 操作是否成功
  71. BOOL bRet = FALSE;
  72. if (outFile.m_hFile != CFile::hFileNull)
  73. {
  74. // 写入数字
  75. outFile.Write((LPCTSTR)this->m_strWord, CHMM_WORD_LEN);
  76. // 写入HMM状态数
  77. outFile.Write(&this->m_nStatusNums, sizeof(unsigned int));
  78. // 写入短时点数
  79. outFile.Write(&this->m_nFrameSize, sizeof(unsigned int));
  80. // 写入训练码本长度
  81. outFile.Write(&this->m_nCodeNums, sizeof(unsigned int));
  82. // 写入Pi序列
  83. outFile.Write(this->m_pPi, sizeof(double) * this->m_nStatusNums);
  84. // 写入A序列
  85. outFile.Write(this->m_pA, 
  86. sizeof(double) * this->m_nStatusNums * this->m_nStatusNums);
  87. // 写入B序列
  88. outFile.Write(this->m_pB, 
  89. sizeof(double) * this->m_nStatusNums * this->m_nCodeNums);
  90. // 写入码本
  91. outFile.Write(this->m_pCodeBook, sizeof(double) * this->m_nCodeNums);
  92. // 关闭导出文件
  93. outFile.Close();
  94. bRet = TRUE;
  95. }
  96. return bRet;
  97. }
  98. //////////////////////////////////////////////////////////////////////////
  99. // 导入HMM模型信息
  100. // 
  101. // 创建人: 陈文凯
  102. // 创建日期: 2005-06-09
  103. // 修改人:
  104. // 修改日期:
  105. BOOL CHMM::LoadModel(CString strFileName)
  106. {
  107. CFile inFile(strFileName, CFile::modeRead | CFile::shareDenyNone);
  108. // 操作是否成功
  109. BOOL bRet = FALSE;
  110. // 保存读入数字
  111. char strWord[CHMM_WORD_LEN];
  112. if (inFile.m_hFile != CFile::hFileNull)
  113. {
  114. // 释放模型占用资源
  115. this->Dispose();
  116. // 读入数字
  117. inFile.Read(&strWord, CHMM_WORD_LEN);
  118. this->m_strWord.Empty();
  119. this->m_strWord.Append(strWord, CHMM_WORD_LEN);
  120. // 读入HMM状态数
  121. inFile.Read(&this->m_nStatusNums, sizeof(unsigned int));
  122. // 读入短时点数
  123. inFile.Read(&this->m_nFrameSize, sizeof(unsigned int));
  124. // 读入训练码本长度
  125. inFile.Read(&this->m_nCodeNums, sizeof(unsigned int));
  126. // 分配参数空间
  127. this->m_pPi = new double[this->m_nStatusNums];
  128. this->m_pA = new double[this->m_nStatusNums * this->m_nStatusNums];
  129. this->m_pB = new double[this->m_nStatusNums * this->m_nCodeNums];
  130. this->m_pCodeBook = new double[this->m_nCodeNums];
  131. // 读入Pi序列
  132. inFile.Read(this->m_pPi, sizeof(double) * this->m_nStatusNums);
  133. // 读入A序列
  134. inFile.Read(this->m_pA, 
  135. sizeof(double) * this->m_nStatusNums * this->m_nStatusNums);
  136. // 读入B序列
  137. inFile.Read(this->m_pB, 
  138. sizeof(double) * this->m_nStatusNums * this->m_nCodeNums);
  139. // 读入码本
  140. inFile.Read(this->m_pCodeBook, sizeof(double) * this->m_nCodeNums);
  141. // 关闭导入文件
  142. inFile.Close();
  143. bRet = TRUE;
  144. }
  145. return bRet;
  146. }
  147. //////////////////////////////////////////////////////////////////////////
  148. // 实现对HMM模型的迭代训练
  149. // 
  150. // 创建人: 陈文凯
  151. // 创建日期: 2005-06-09
  152. // 修改人:
  153. // 修改日期:
  154. void CHMM::Train(
  155.  const double* pDataIn, // 输入采样序列
  156.  unsigned int nInLen // 输入采样序列长度
  157.  )
  158. {
  159. // 前后向算法返回参数
  160. double fRate = 0;
  161. double* pa = NULL;
  162. double* pb = NULL;
  163. // 对输入采样序列进行矢量量化形成的码本
  164. double* pCodeBook = NULL;
  165. // 输入码本对应的HMM模型下标
  166. unsigned int* pCodeIndex = NULL;
  167. // 判断输入数据是否合法
  168. if (pDataIn != NULL && this->m_pA != NULL && 
  169. this->m_pB != NULL && this->m_pPi != NULL)
  170. {
  171. pa = new double[this->m_nCodeNums * this->m_nStatusNums];
  172. pb = new double[this->m_nCodeNums * this->m_nStatusNums];
  173. // 对输入采样序列进行矢量量化
  174. pCodeBook = new double[this->m_nCodeNums];
  175. CVQ::KMeansCluster(
  176. pDataIn, nInLen, pCodeBook, this->m_nCodeNums);
  177. // 获得输入码本对应的HMM模型码本下标
  178. pCodeIndex = new unsigned int[this->m_nCodeNums];
  179. CVQ::Classify(
  180. pCodeBook, this->m_nCodeNums, this->m_pCodeBook, this->m_nCodeNums, pCodeIndex);
  181. // 调用前后向算法, 计算概率, pa, pb
  182. fRate = this->ForwardBackward(
  183. pCodeIndex, this->m_nCodeNums, this->m_pPi, this->m_pA, 
  184. this->m_pB, this->m_nStatusNums, pa, pb);
  185. // 调用BW算法进行参数优化
  186. this->BaumWelch(
  187. pCodeIndex, this->m_nCodeNums, this->m_pPi, this->m_pA,
  188. this->m_pB, this->m_nStatusNums, pa, pb, fRate);
  189. // 更新码本
  190. for (unsigned int i = 0; i < this->m_nCodeNums; i++)
  191. {
  192. // this->m_pCodeBook[i] = (this->m_pCodeBook[i] + pCodeBook[i]) / 2;
  193. this->m_pCodeBook[i] = this->m_pCodeBook[i] * 0.7 + pCodeBook[i] * 0.3;
  194. }
  195. delete[] pa;
  196. delete[] pb;
  197. delete[] pCodeBook;
  198. delete[] pCodeIndex;
  199. }
  200. }
  201. //////////////////////////////////////////////////////////////////////////
  202. // 实现对HMM模型得初始化训练
  203. // 
  204. // 创建人: 陈文凯
  205. // 创建日期: 2005-06-09
  206. // 修改人:
  207. // 修改日期:
  208. void CHMM::PrepareTrain( 
  209. CString strWord, 
  210. const double* pDataIn, // 第一个输入采样序列
  211. unsigned int nInLen, // 输入采样序列长度
  212. unsigned int nFrameSize,// 分帧宽度
  213. unsigned int nStatusNums /* = 6 */,  
  214. unsigned int nCodeNums  /* = 4   */
  215. )
  216. {
  217. // 计算概率
  218. double fRate = 0;
  219. // 循环变量
  220. unsigned int i = 0;
  221. unsigned int j = 0;
  222. // 设置模型信息
  223. this->m_strWord = strWord;
  224. this->m_nStatusNums = nStatusNums;
  225. this->m_nCodeNums = nCodeNums;
  226. this->m_nFrameSize = nFrameSize;
  227. // 初始化pI/pA/pB/pCodeBook
  228. this->m_pPi = new double[this->m_nStatusNums];
  229. this->m_pA = new double[this->m_nStatusNums * this->m_nStatusNums];
  230. this->m_pB = new double[this->m_nStatusNums * this->m_nCodeNums];
  231. this->m_pCodeBook = new double[this->m_nCodeNums];
  232. memset(this->m_pPi, 0, sizeof(double) * this->m_nStatusNums);
  233. memset(this->m_pA, 0, sizeof(double) * this->m_nStatusNums * this->m_nStatusNums);
  234. memset(this->m_pB, 0, sizeof(double) * this->m_nStatusNums * this->m_nCodeNums);
  235. memset(this->m_pCodeBook, 0, sizeof(double) * this->m_nCodeNums);
  236. // 对pI进行均分
  237. fRate = (double) 1 / nStatusNums;
  238. for (i = 0; i < nStatusNums; i++)
  239. {
  240. this->m_pPi[i] = fRate;
  241. }
  242. // 对PA[i][j]进行均分
  243. for (i = 0; i < nStatusNums; i++)
  244. {
  245. for (j = 0; j < nStatusNums; j++)
  246. {
  247. this->m_pA[i * nStatusNums + j] = fRate;
  248. }
  249. }
  250. // 对pB[i][j]进行均分
  251. fRate = (double) 1 / nCodeNums;
  252. for (i = 0; i < nStatusNums; i++)
  253. {
  254. for (j = 0; j < nCodeNums; j++)
  255. {
  256. this->m_pB[i * nCodeNums + j] = fRate;
  257. }
  258. }
  259. // 形成初始码本
  260. CVQ::KMeansCluster(pDataIn, nInLen, this->m_pCodeBook, this->m_nCodeNums);
  261. // 进行参数优化
  262. this->Train(pDataIn, nInLen);
  263. }
  264. //////////////////////////////////////////////////////////////////////////
  265. // 实现前后向算法,对于给定的HMM参数和观察序列O,
  266. // 求能够产生观察序列O的概率
  267. // 
  268. // 创建人: 陈文凯
  269. // 创建日期: 2005-06-08
  270. // 修改人:
  271. // 修改日期:
  272. double CHMM::ForwardBackward(
  273.  const unsigned int* pCodeBook, // 输入观察码本序列对应的HMM码本的下标
  274.  unsigned int nCodeNums, // 输入观察序列长度
  275.  const double* pPi, // HMM的pi矢量
  276.  const double* pA, // HMM的A矩阵
  277.  const double* pB, // HMM的B矩阵
  278.  unsigned int nStatusNums, // HMM状态数
  279.  double* pa, // 前后向算法中的a
  280.  double* pb // 前后向算法中的b
  281.  )
  282. {
  283. // 能够产生观察序列O的概率
  284. double fRate = 0;
  285. // 递推时做累积
  286. double fSum = 0;
  287. // 循环变量,damn vc
  288. // 输入序列循环
  289. int t = 0;
  290. // 状态循环
  291. int i = 0;
  292. int j = 0;
  293. // 初始化pa, pb
  294. memset(pa, 0, sizeof(double) * (nCodeNums * nStatusNums));
  295. memset(pb, 0, sizeof(double) * (nCodeNums * nStatusNums));
  296. // 实现前后向算法
  297. // 初始化
  298. for (i = 0; i < nStatusNums; i++)
  299. {
  300. // 计算公式:pa[1][i] = pI[i] * pB[i][o1] 1 <= i <= N
  301. pa[0 * nStatusNums + i] = pPi[i] * pB[i * nCodeNums + pCodeBook[0]];
  302. // 计算公式:pb[nCodeNums - 1][i] = 1
  303. pb[(nCodeNums - 1) * nStatusNums + i] = 1;
  304. }
  305. // 前向递推
  306. for (t = 1; t < nCodeNums; t++)
  307. {
  308. for (j = 0; j < nStatusNums; j++)
  309. {
  310. fSum = 0;
  311. for (i = 0; i < nStatusNums; i++)
  312. {
  313. // 累积 fSum += pa[t-1][i] * pA[i][j]
  314. fSum += pa[(t - 1) * nStatusNums + i] * pA[i * nStatusNums + j];
  315. }
  316. // 计算公式:pa[t][j] = fSum * pB[j][ot]
  317. pa[t * nStatusNums + j] = fSum * pB[j * nCodeNums + pCodeBook[t]];
  318. TRACE("pa(%d, %d) : %fn", t, j, pa[t * nStatusNums + j]);
  319. }
  320. }
  321. // 后向递推
  322. for (t = nCodeNums - 2; t >= 0; t--)
  323. {
  324. for (i = 0; i < nStatusNums; i++)
  325. {
  326. fSum = 0;
  327. for (j = 0; j < nStatusNums; j++)
  328. {
  329. // 累积 fSum += pA[i][j] * pB[j][t + 1] * pb[t + 1][j]
  330. fSum += pA[i * nStatusNums + j] * 
  331. pB[j * nCodeNums + pCodeBook[t + 1]] * 
  332. pb[(t + 1) * nStatusNums + j];
  333. }
  334. pb[t * nStatusNums + i] = fSum;
  335. TRACE("pb(%d, %d): %lfn", t, i, pb[t * nStatusNums + i]);
  336. }
  337. }
  338. // 结束
  339. // 计算概率,令t=nCodeNums-1
  340. t = nCodeNums - 1;
  341. for (i = 0; i < nStatusNums; i++)
  342. {
  343. // 计算公式:fRate += pa[t][i] * pb[t][i];
  344. fRate += pa[t * nStatusNums + i] * pb[t * nStatusNums + i];
  345. }
  346. return fRate;
  347. }
  348. //////////////////////////////////////////////////////////////////////////
  349. // 实现Viterbi算法,对于给定的HMM参数和观察序列O,
  350. // 返回最大概率
  351. // 
  352. // 创建人: 陈文凯
  353. // 创建日期: 2005-06-08
  354. // 修改人:
  355. // 修改日期:
  356. double CHMM::Viterbi(
  357.  const unsigned int* pCodeBook, // 输入观察码本序列对应的HMM码本的下标
  358.  unsigned int nCodeNums, // 输入观察序列长度
  359.  const double* pPi, // HMM的pi矢量
  360.  const double* pA, // HMM的A矩阵
  361.  const double* pB, // HMM的B矩阵
  362.  unsigned int nStatusNums // HMM状态数
  363.  )
  364. {
  365. // pa[t][i]为t时刻沿着一条路径q1, q2, ..., qt,且qt = st
  366. // 产生出输入观察序列o1, o2, o3, ..., ot的最大概率
  367. // 1 <= t <= nInLen, 1 <= i <= nStatusNums
  368. double* pa = NULL;
  369. // 临时计算
  370. double fTemp = 0;
  371. double fMax = 0;
  372. // 最大概率
  373. double fMaxRate = 0;
  374. // 循环变量
  375. // 采样序列循环
  376. unsigned int t = 0;
  377. // 状态循环
  378. unsigned int j = 0;
  379. unsigned int i = 0;
  380. // 分配和初始化pa
  381. pa = new double[nCodeNums * nStatusNums];
  382. memset(pa, 0, sizeof(double) * (nCodeNums * nStatusNums));
  383. // 初始化
  384. for (i = 0; i < nStatusNums; i++)
  385. {
  386. // 计算公式:pa[1][i] = pi[i] * pB[i][1]
  387. pa[0 * nStatusNums + i] = pPi[i] * pB[i * nCodeNums + pCodeBook[0]];
  388. }
  389. // 递推
  390. for (t = 1; t < nCodeNums; t++)
  391. {
  392. for (j = 0; j < nStatusNums; j++)
  393. {
  394. // 求pa[t - 1][i] * pA[i][j]的最大值, 1 <= i <= N
  395. fMax = pa[(t - 1) * nStatusNums + 0] * pA[0 * nStatusNums + j];
  396. for (i = 0; i < nStatusNums; i++)
  397. {
  398. fTemp = pa[(t - 1) * nStatusNums + i] * pA[i * nStatusNums + j];
  399. fMax = (fMax < fTemp ? fTemp : fMax);
  400. }
  401. // 计算pa[t][j]和pb[t][j]
  402. // 计算公式:pa[t][j] = fMax * pB[j][t];
  403. pa[t * nStatusNums + j] = fMax * pB[j * nCodeNums + pCodeBook[t]];
  404. }
  405. }
  406. // 终结
  407. // 求最大概率, fMaxRate = max(pa[nCodeNums - 1][i]) 1 <= i <= nStatusNums
  408. fMaxRate = pa[(nCodeNums - 1) * nStatusNums + 0];
  409. for (i = 0; i < nStatusNums; i++)
  410. {
  411. fTemp = pa[(nCodeNums - 1) * nStatusNums + i];
  412. fMaxRate = (fMaxRate < fTemp ? fTemp : fMaxRate);
  413. }
  414. delete[] pa;
  415. return fMaxRate;
  416. }
  417. //////////////////////////////////////////////////////////////////////////
  418. // 实现Baum Welch算法,对于给定的观察序列O,
  419. // 求使P(O | HMM参数)最大的HMM参数pi, A, B
  420. // 
  421. // 创建人: 陈文凯
  422. // 创建日期: 2005-06-08
  423. // 修改人:
  424. // 修改日期:
  425. void CHMM::BaumWelch(
  426.  const unsigned int* pCodeBook, // 输入观察码本序列对应的HMM码本的下标
  427.  unsigned int nCodeNums, // 输入观察序列长度
  428.  double* pPi, // HMM的pi矢量
  429.  double* pA, // HMM的A矩阵
  430.  double* pB, // HMM的B矩阵
  431.  unsigned int nStatusNums, // HMM状态数
  432.  double* pa, // 前后向算法计算所得的pa
  433.  double* pb, // 前后向算法计算所得pb
  434.  double fRate // 前后向算法计算所得最大概率
  435.  )
  436. {
  437. // bw算法中概率矩阵
  438. // pRate[nCodeNums][nStatusNums][nStatusNums]
  439. double* pRate = NULL;
  440. // 最小概率
  441. double fMinRate = 0.0001;
  442. unsigned int nOffSet = nStatusNums * nStatusNums;
  443. // 循环变量
  444. unsigned int t = 0;
  445. unsigned int i = 0;
  446. unsigned int j = 0;
  447. unsigned int k = 0;
  448. // 求概率时的分子
  449. double fAvgUp = 0;
  450. // 求概率时的分母
  451. double fAvgDown = 0;
  452. // 数组元素下标
  453. unsigned int nIndex = 0;
  454. // 初始化概率矩阵
  455. pRate = new double[nCodeNums * nOffSet];
  456. memset(pRate, 0, sizeof(double) * nCodeNums * nOffSet);
  457. // 计算pRate[t][i][j],即t时刻Markov链处于i状态, t+1时刻处于j状态的概率
  458. for (t = 0; t < (nCodeNums - 1); t++)
  459. {
  460. for (i = 0; i < nStatusNums; i++)
  461. {
  462. for (j = 0; j < nStatusNums; j++)
  463. {
  464. pRate[t * nOffSet + i * nStatusNums + j] = 
  465. (pa[t * nStatusNums + i] * pA[i * nStatusNums + j] * 
  466. pB[j* nCodeNums + pCodeBook[t + 1]] * pb[(t + 1) * nStatusNums + j]) / fRate;
  467. TRACE("pRate[%d, %d, %d]: %fn", 
  468. t, i, j, pRate[t * nOffSet + i * nStatusNums + j]);
  469. TRACE("pa: %f, pb: %f, pA: %f, pB: %fn", 
  470. pa[t * nStatusNums + i],
  471. pA[i * nStatusNums + j],
  472. pB[j* nCodeNums + pCodeBook[t + 1]],
  473. pb[(t + 1) * nStatusNums + j]
  474. );
  475. }
  476. }
  477. }
  478. // 使用重估公式计算HMM参数
  479. // 计算Pi矢量, 即0时刻时Markov链处于i状态, t+1时刻处于其他任何状态的概率
  480. for (i = 0; i < nStatusNums; i++)
  481. {
  482. pPi[i] = 0;
  483. for (j = 0; j < nStatusNums; j++)
  484. {
  485. pPi[i] += pRate[0 * nOffSet + i * nStatusNums + j];
  486. }
  487. pPi[i] = (pPi[i] > 0.0 ? pPi[i] : fMinRate);
  488. TRACE("pi[%d]: %fn", i, pPi[i]);
  489. }
  490. // 计算A矩阵 
  491. // pA[i][j] = (从状态Si过渡到Sj的平均次数) / (从状态Si向其他状态转移的平均次数)
  492. for (i = 0; i < nStatusNums; i++)
  493. {
  494. for (j = 0; j < nStatusNums; j++)
  495. {
  496. // 计算状态Si过渡到Sj的平均次数
  497. fAvgUp = 0;
  498. // 从状态Si向其他状态转移的平均次数
  499. fAvgDown = 0;
  500. for (t = 0; t < nCodeNums; t++)
  501. {
  502. fAvgUp += pRate[t * nOffSet + i * nStatusNums + j];
  503. for (k = 0; k < nStatusNums; k++)
  504. {
  505. fAvgDown += pRate[t * nOffSet + i * nStatusNums + k];
  506. }
  507. }
  508. nIndex = i * nStatusNums + j;
  509. pA[nIndex] = fAvgUp / fAvgDown;
  510. pA[nIndex] = (pA[nIndex] > 0.0 ? pA[nIndex] : fMinRate);
  511. TRACE("pA[%d,%d]: %fn", i, j, pA[nIndex]);
  512. }
  513. }
  514. // 计算B矩阵
  515. // pB[i][k] = (出现状态i和观察值Ot=Vk的平均次数) / (处于状态i的次数)
  516. for (i = 0; i < nStatusNums; i++)
  517. {
  518. for (k = 0; k < nCodeNums; k++)
  519. {
  520. // 计算出现状态i和观察值Ot=Vk的平均次数
  521. fAvgUp = 0;
  522. for (t = 0; t < nCodeNums; t++)
  523. {
  524. if (pCodeBook[t] == pCodeBook[k])
  525. {
  526. for (j = 0; j < nStatusNums;j ++)
  527. {
  528. fAvgUp += pRate[t * nOffSet + i * nStatusNums + j];
  529. }
  530. }
  531. }
  532. // 处于状态i的次数
  533. fAvgDown = 0;
  534. for (t = 0; t < nCodeNums; t++)
  535. {
  536. for (j = 0; j < nStatusNums; j++)
  537. {
  538. fAvgDown += pRate[t * nOffSet + i * nStatusNums + j];
  539. }
  540. }
  541. // 计算pB[i][k]
  542. nIndex = i * nCodeNums + pCodeBook[k];
  543. pB[nIndex] = fAvgUp / fAvgDown;
  544. pB[nIndex] = (pB[nIndex] > 0.0 ? pB[nIndex] : fMinRate);
  545. TRACE("pB[%d, %d]: %fn", i, j, pB[nIndex]);
  546. }
  547. }
  548. // 释放资源
  549. if (pRate != NULL)
  550. {
  551. delete[] pRate;
  552. }
  553. }
  554. //////////////////////////////////////////////////////////////////////////
  555. // 基于Vertibi算法实现对HMM模型的识别
  556. CString CHMM::RecogonizeByViterbi(
  557.   const double* pCodeBook, // 输入码本
  558.   unsigned int nCodeNums, // 输入码本长度
  559.   CString* strModelList, // 模型文件路径
  560.   unsigned int nModelCount // 模型数量
  561.   )
  562. {
  563. CString strWord;
  564. // 最大概率
  565. double fMaxRate = 0;
  566. // 识别概率
  567. double fRate = 0;
  568. // 模型
  569. CHMM hmmModel;
  570. // 输入码本对应的HMM码本下标
  571. unsigned int* pCodeIndex = NULL;
  572. if (pCodeBook != NULL && strModelList != NULL)
  573. {
  574. // 初始化输入码本对应的HMM码本下标
  575. pCodeIndex = new unsigned int[nCodeNums];
  576. for (unsigned int i = 0; i < nModelCount; i++)
  577. {
  578. if (hmmModel.LoadModel(strModelList[i]))
  579. {
  580. // 获取输入码本对应的HMM码本下标
  581. CVQ::Classify(
  582. pCodeBook, nCodeNums, hmmModel.m_pCodeBook, hmmModel.m_nCodeNums, pCodeIndex);
  583. fRate = hmmModel.Viterbi(
  584. pCodeIndex, nCodeNums, hmmModel.m_pPi,
  585. hmmModel.m_pA, hmmModel.m_pB, hmmModel.m_nStatusNums);
  586. // 保存最大概率
  587. if (fMaxRate < fRate)
  588. {
  589. fMaxRate = fRate;
  590. // 保存最大概率模型对应单词
  591. strWord = hmmModel.GetWord();
  592. }
  593. }
  594. }
  595. delete[] pCodeIndex;
  596. }
  597. return strWord;
  598. }
  599. //////////////////////////////////////////////////////////////////////////
  600. // 基于DTW的Vertibi实现对HMM模型的识别
  601. CString CHMM::RecogonizeByDTW(
  602.   const double* pCodeBook, // 输入码本
  603.   unsigned int nCodeNums, // 输入码本长度
  604.   CString* strModelList, // 模型文件路径
  605.   unsigned int nModelCount // 模型数量
  606.    )
  607. {
  608. CString strWord;
  609. // 最大概率
  610. double fMaxRate = 0;
  611. // 识别概率
  612. double fRate = 0;
  613. // 模型
  614. CHMM hmmModel;
  615. // 输入码本对应的HMM码本下标
  616. unsigned int* pCodeIndex = NULL;
  617. if (pCodeBook != NULL && strModelList != NULL)
  618. {
  619. // 初始化输入码本对应的HMM码本下标
  620. pCodeIndex = new unsigned int[nCodeNums];
  621. for (unsigned int i = 0; i < nModelCount; i++)
  622. {
  623. if (hmmModel.LoadModel(strModelList[i]))
  624. {
  625. // 获取输入码本对应的HMM码本下标
  626. CVQ::Classify(
  627. pCodeBook, nCodeNums, hmmModel.m_pCodeBook, hmmModel.m_nCodeNums, pCodeIndex);
  628. fRate = hmmModel.DTW(
  629. pCodeIndex, nCodeNums,
  630. hmmModel.m_pA, hmmModel.m_pB, hmmModel.m_nStatusNums);
  631. // 保存最大概率
  632. if (fMaxRate < fRate)
  633. {
  634. fMaxRate = fRate;
  635. // 保存最大概率模型对应单词
  636. strWord = hmmModel.GetWord();
  637. }
  638. }
  639. }
  640. delete[] pCodeIndex;
  641. }
  642. return strWord;
  643. }
  644. //////////////////////////////////////////////////////////////////////////
  645. // 实现基于VQ的HMM模型识别
  646. CString CHMM::RecogonizeByVQ(
  647.  const double* pCodeBook, // 输入码本
  648.  unsigned int nCodeNums, // 输入码本长度
  649.  CString* strModelList, // 模型文件路径
  650.  unsigned int nModelCount // 模型数量
  651.  )
  652. {
  653. CString strWord;
  654. // 最小距离
  655. double fMinDis = 30000;
  656. // 码本距离
  657. double fDis = 0;
  658. // 最小距离模型下标
  659. unsigned int nMinModel = 0;
  660. // 模型
  661. CHMM hmmModel;
  662. if (pCodeBook != NULL && strModelList != NULL)
  663. {
  664. for (unsigned int i = 0; i < nModelCount; i++)
  665. {
  666. if (hmmModel.LoadModel(strModelList[i]))
  667. {
  668. fDis = CVQ::GetDistance(pCodeBook, hmmModel.m_pCodeBook, nCodeNums);
  669. // 保存最小距离
  670. if (fDis < fMinDis)
  671. {
  672. fMinDis = fDis;
  673. // 保存最小距离模型对应单词
  674. strWord = hmmModel.GetWord();
  675. TRACE("%d, %fn", i, fMinDis);
  676. }
  677. }
  678. }
  679. }
  680. return strWord;
  681. }
  682. //////////////////////////////////////////////////////////////////////////
  683. // 实现基于DTW的Viterbi算法
  684. double CHMM::DTW(
  685.  const unsigned int* pCodeBook, // 输入观察码本序列对应的HMM码本的下标
  686.  unsigned int nCodeNums, // 输入观察序列长度
  687.  double* pA, // HMM的A矩阵
  688.  double* pB, // HMM的B矩阵
  689.  unsigned int nStatusNums // HMM状态数
  690.  )
  691. {
  692. double* pRate = NULL;
  693. // 返回的D(nStatusNums, nInLen)
  694. double fRetRate = 0;
  695. // 计算最大D + A
  696. double fMaxD = 0;
  697. double fTemp1 = 0;
  698. double fTemp2 = 0;
  699. if (pCodeBook != NULL)
  700. {
  701. // 初始话概率矩阵
  702. pRate = new double[nCodeNums * nStatusNums];
  703. memset(pRate, 0, sizeof(double) * nCodeNums * nStatusNums);
  704. for (unsigned int i = 0; i < nStatusNums; i++)
  705. {
  706. for (unsigned int j = 1; j < nCodeNums; j++)
  707. {
  708. // 计算D(i-1, j) + A(j, j)
  709. fTemp1 = ((i - 1) >= 0 ? pRate[(i - 1) * nStatusNums + j] : 0);
  710. fTemp1 += pA[j * nStatusNums + j];
  711. // 计算D(i-1, j-1) + A(j-1, j);
  712. fTemp2 = ((i - 1) >= 0 && (j - 1) >= 0 ? 
  713. pRate[(i - 1) * nStatusNums + (j - 1)] : 0);
  714. fTemp2 += ((j - 1) >= 0 ? pA[(j - 1) * nStatusNums + j] : 0);
  715. // 取D(i, j) = B(i, j) + max(fTemp1, fTemp2);
  716. fMaxD = (fTemp1 > fTemp2 ? fTemp1 : fTemp2);
  717. pRate[i * nCodeNums + j] = pB[i * nCodeNums + pCodeBook[j]] + fMaxD;
  718. }
  719. }
  720. fRetRate = pRate[(nCodeNums - 1) * nStatusNums + (nStatusNums - 1)];
  721. // 释放所占用资源
  722. delete[] pRate;
  723. }
  724. return fRetRate;
  725. }